bUnit-dev / bUnit

bUnit is a testing library for Blazor components that make tests look, feel, and runs like regular unit tests. bUnit makes it easy to render and control a component under test’s life-cycle, pass parameter and inject services into it, trigger event handlers, and verify the rendered markup from the component using a built-in semantic HTML comparer.
https://bunit.dev
MIT License
1.12k stars 104 forks source link

Testing components with Telerik Grid throws System.InvalidOperationException #38

Closed jpiell closed 4 years ago

jpiell commented 4 years ago

I am testing a component that uses the Telerik UI Blazor grid component. The grid component throws an exception: Message=Cannot provide a value for property 'State' on type 'Telerik.Blazor.Components.TelerikGrid, There is no registered service of type 'Telerik.Blazor.Components.Grid.State.IGridState'.

IGridState is private so it cannot be mocked.

I would like some help on how to work around this issue.

Attached is a sample app I put together to demonstrate the issue I am having.

SampleApp.zip

egil commented 4 years ago

Hi @jpiell

I will take a look later tonight if time allows, otherwise tomorrow.

jpiell commented 4 years ago

Thank you!

On Tue, Jan 21, 2020 at 12:28 PM Egil Hansen notifications@github.com wrote:

Hi @jpiell https://github.com/jpiell

I will take a look later tonight if time allows, otherwise tomorrow.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/egil/razor-components-testing-library/issues/38?email_source=notifications&email_token=AHE7FB7RQASDZT3R4AWWCKTQ64WEPA5CNFSM4KJUVQUKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEJQSBAY#issuecomment-576790659, or unsubscribe https://github.com/notifications/unsubscribe-auth/AHE7FBYVTITLEGGE25MYNFDQ64WEPANCNFSM4KJUVQUA .

EdCharbeneau commented 4 years ago

@jpiell Hi, I work for Telerik. Sorry you're having trouble getting this set up. Because Blazor is new (still in preview), we haven't checked it against all integration points yet, this great testing framework by @egil is the perfect example.

We are evaluating how we can provide better support for this, and I'm testing myself as well.

Short term solution I believe that if TestServiceProvider implemented IServiceCollection then one could simply call Services.AddTeleirkBlazor() and that would solve the issue. This appears to be on the roadmap here.

Long term solution This also seems to be the only Telerik service that is set to private. I will forward this to the engineering team and see what the reason is and if it's possible to open up the interface. However, this will likely not change in the immediate future as it would need to be tested and queued for release. If you would like to track this, I would suggest making an entry on our feedback portal.

egil commented 4 years ago

@jpiell #39 should be fixed in the upcoming beta 5 release. My plan is to push that out in a day or two, so hopefully that will solve your issue.

jpiell commented 4 years ago

Hi Ed,

I believe you are correct. It looks like Egil just implemented IServiceCollection which should resolve the issue I am having. If engineering could make IGridState public, I could mock the interface and that would work as well (maybe even better).

I believe Beta 5 of the Razor.Component.Testing.Library will be released in the next couple of days.

Thanks for keeping an eye on the issue.

Regards,

Jeff

On Wed, Jan 22, 2020 at 10:49 AM Egil Hansen notifications@github.com wrote:

Closed #38 https://github.com/egil/razor-components-testing-library/issues/38.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/egil/razor-components-testing-library/issues/38?email_source=notifications&email_token=AHE7FB4ZR3MWRH2HT7L2N53Q7BTI5A5CNFSM4KJUVQUKYY3PNVWWK3TUL52HS4DFWZEXG43VMVCXMZLOORHG65DJMZUWGYLUNFXW5KTDN5WW2ZLOORPWSZGOWEHWFEY#event-2970575507, or unsubscribe https://github.com/notifications/unsubscribe-auth/AHE7FBYZ3VGG5YOSWZJGMWDQ7BTI5ANCNFSM4KJUVQUA .

jpiell commented 4 years ago

Thank you sir!

Sent from my iPhone

On Jan 22, 2020, at 10:49 AM, Egil Hansen notifications@github.com wrote:

 @jpiell #39 should be fixed in the upcoming beta 5 release. My plan is to push that out in a day or two, so hopefully that will solve your issue.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub, or unsubscribe.

jpiell commented 4 years ago

Egil,

You need to expose the IServiceCollection or we will not be able to register services.

Thanks,

Jeff

egil commented 4 years ago

Egil,

You need to expose the IServiceCollection or we will not be able to register services.

Thanks,

Jeff

jpiell, in beta-5, TestServiceProvider, available through the Services property in the test context, is a IServiceCollection.

I just did a dotnet new razortest with beta-5, and it looks as if it works as it should. This is my test file:

using System;
using Xunit;
using Egil.RazorComponents.Testing;
using Egil.RazorComponents.Testing.Diffing;
using Egil.RazorComponents.Testing.Asserting;
using Egil.RazorComponents.Testing.Mocking.JSInterop;
using Egil.RazorComponents.Testing.EventDispatchExtensions;
using Microsoft.Extensions.DependencyInjection;

namespace tspsc
{
  public class Component1Test : ComponentTestFixture
  {
    [Fact]
    public void Component1RendersCorrectly()
    {
      Services.AddMySrvs();
      var cut = RenderComponent<Component1>();      

      cut.MarkupMatches(@"<div class=""my-component"">
                            This Blazor component is defined in the <strong>razorclasslib1</strong> package.
                          </div>");
    }
  }

  public static class SrvColExt 
  {
    public static void AddMySrvs(this IServiceCollection services)
    {
      services.AddSingleton(new object());
    }
  }
}

And in razor based tests:

@inherits TestComponentBase

<Fixture Setup="() => Services.AddMySrvs()">
    <ComponentUnderTest><Component1/></ComponentUnderTest>
</Fixture>

Both files compile and run on my computer.

egil commented 4 years ago

It looks as if Teleriks ServiceCollectionExtensions class matches the SrvColExt class in my comment above (https://docs.telerik.com/blazor-ui/api/Microsoft.Extensions.DependencyInjection.ServiceCollectionExtensions.html), so it should work.

Am I missing something here?

egil commented 4 years ago

OK, so I installed into my testing project: <PackageReference Include="Telerik.UI.for.Blazor.Trial" Version="2.6.1" />

Afterwords, both cs and razor tests compile and execute:

using Xunit;
using Egil.RazorComponents.Testing;
using Egil.RazorComponents.Testing.Asserting;
using Microsoft.Extensions.DependencyInjection;

namespace tspsc
{
    public class Component1Test : ComponentTestFixture
    {
        [Fact]
        public void Component1RendersCorrectly()
        {
            Services.AddMySrvs();
            Services.AddTelerikBlazor();

            var cut = RenderComponent<Component1>();

            cut.MarkupMatches(@"<div class=""my-component"">
                            This Blazor component is defined in the <strong>razorclasslib1</strong> package.
                          </div>");
        }
    }

    public static class SrvColExt
    {
        public static void AddMySrvs(this IServiceCollection services)
        {
            services.AddSingleton(new object());
        }
    }
}
@inherits TestComponentBase

<Fixture Setup="() => Services.AddTelerikBlazor()">
    <ComponentUnderTest><Component1/></ComponentUnderTest>
</Fixture>
jpiell commented 4 years ago

Do I need to do something other than update my NuGet package?

egil commented 4 years ago

Not entirely sure. But if you follow the guide here: https://github.com/egil/razor-components-testing-library/wiki/Creating-a-new-test-project

Install the template, and the use the dotnet new razortest command, you should end up with a project where you can get my examples above working. Then you can compare and contrast with your existing.

That said, going to beta-5 should be enough, but you might also have to change/add some imports.

jpiell commented 4 years ago

All is good now - Thanks again for your help!

EdCharbeneau commented 4 years ago

@jpiell I was able to verify that a cut could render a Telerik Grid.

@inherits TestComponentBase
@using Microsoft.AspNetCore.Components.Web
@using Egil.RazorComponents.Testing
@using Egil.RazorComponents.Testing.Diffing
@using Egil.RazorComponents.Testing.Asserting
@using Egil.RazorComponents.Testing.EventDispatchExtensions
@using Xunit
@using Telerik.Blazor.Components
@using Microsoft.Extensions.DependencyInjection;

<Fixture Test="Test1">
    <ComponentUnderTest>
        <TelerikRootComponent>
            <TelerikGrid Data="forecasts">
                <GridColumns>
                    <GridColumn Field="@nameof(WeatherForecast.Date)"></GridColumn>
                </GridColumns>
            </TelerikGrid>
        </TelerikRootComponent>
    </ComponentUnderTest>
</Fixture>
@code {

    WeatherForecast[] forecasts = new List<WeatherForecast>().ToArray();

    public void AddTelerikBlazor()
    {
        Services.AddMockJsRuntime();
        Services.AddTelerikBlazor();

}

    void Test1()
    {
        AddTelerikBlazor();

        var cut = GetComponentUnderTest();

        Assert.NotNull(cut);
    }

    public class WeatherForecast
    {
        public DateTime Date { get; set; }

        public int TemperatureC { get; set; }

        public string Summary { get; set; }

        public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
    }
}
jpiell commented 4 years ago

@jpiell Hi, I work for Telerik. Sorry you're having trouble getting this set up. Because Blazor is new (still in preview), we haven't checked it against all integration points yet, this great testing framework by @egil is the perfect example.

We are evaluating how we can provide better support for this, and I'm testing myself as well.

Short term solution I believe that if TestServiceProvider implemented IServiceCollection then one could simply call Services.AddTeleirkBlazor() and that would solve the issue. This appears to be on the roadmap here.

Long term solution This also seems to be the only Telerik service that is set to private. I will forward this to the engineering team and see what the reason is and if it's possible to open up the interface. However, this will likely not change in the immediate future as it would need to be tested and queued for release. If you would like to track this, I would suggest making an entry on our feedback portal.

Hi Ed,

Would you happen to have any advice on how to get around:

@inherits LayoutComponentBase

    <TelerikRootComponent>

        <div class="sidebar">
            <NavMenu />
        </div>

        <div class="main">
            @Body
        </div>

    </TelerikRootComponent>

My tests are c# only.

Thanks,

Jeff Piell

EdCharbeneau commented 4 years ago

@jpiell

I believe this is the right start. However, I don't know how the various templates are described. @egil ?? Reference markup

        <TelerikRootComponent> (cascading value)
            <TelerikGrid Data="forecasts"> (component<T> parameter Data)
                <GridColumns> (parameter of TelerikGrid)
                    <GridColumn Field="@nameof(WeatherForecast.Date)"></GridColumn> (GridColumn component cascading value from TelerikGrid, parameter Field)
                </GridColumns>
            </TelerikGrid>
        </TelerikRootComponent>
using Egil.RazorComponents.Testing;
using Xunit;
using Telerik.Blazor.Components;
using Microsoft.Extensions.DependencyInjection;
using System;

public class CounterTest : ComponentTestFixture
{
    [Fact]
    public void InitialHtmlIsCorrect()
    {
        Services.AddMockJsRuntime();
        Services.AddTelerikBlazor();
        // Arrange - renders the Counter component
        var root = new TelerikRootComponent();
        var cut = RenderComponent<TelerikGrid<WeatherForecast>>(
            CascadingValue(root));
        Assert.NotNull(cut);
    }

    public class WeatherForecast
    {
        public DateTime Date { get; set; }

        public int TemperatureC { get; set; }

        public string Summary { get; set; }

        public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
    }
}
jpiell commented 4 years ago

Ed,

I got it to work!

Thanks for your help!

Jeff Piell

On Thu, Jan 23, 2020 at 5:26 PM Ed Charbeneau notifications@github.com wrote:

@jpiell https://github.com/jpiell

I believe this is the right start. However, I don't know how the various templates are described. @egil https://github.com/egil ?? Reference markup

    <TelerikRootComponent> (cascading value)
        <TelerikGrid Data="forecasts"> (component<T> parameter Data)
            <GridColumns> (parameter of TelerikGrid)
                <GridColumn Field="@nameof(WeatherForecast.Date)"></GridColumn> (GridColumn component cascading value from TelerikGrid, parameter Field)
            </GridColumns>
        </TelerikGrid>
    </TelerikRootComponent>

using Egil.RazorComponents.Testing; using Xunit; using Telerik.Blazor.Components; using Microsoft.Extensions.DependencyInjection; using System;

public class CounterTest : ComponentTestFixture { [Fact] public void InitialHtmlIsCorrect() { Services.AddMockJsRuntime(); Services.AddTelerikBlazor(); // Arrange - renders the Counter component var root = new TelerikRootComponent(); var cut = RenderComponent<TelerikGrid>( CascadingValue(root)); Assert.NotNull(cut); }

public class WeatherForecast
{
    public DateTime Date { get; set; }

    public int TemperatureC { get; set; }

    public string Summary { get; set; }

    public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
}

}

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/egil/razor-components-testing-library/issues/38?email_source=notifications&email_token=AHE7FB3INZSOLTH2CSUASHDQ7IKRNA5CNFSM4KJUVQUKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEJZCU2Q#issuecomment-577907306, or unsubscribe https://github.com/notifications/unsubscribe-auth/AHE7FBZU2XTT5QE6KUZ36BLQ7IKRNANCNFSM4KJUVQUA .

jpiell commented 4 years ago

Ed,

The example doesn't populate the grid.

See the cut.Markup below:

<div id="840207f6-cc00-4007-aab4-bb4e5b86f403" class="k-grid k-widget telerik-blazor" style="" __internal_stopPropagation_onkeydown>

    <div class="k-grid-header" id="ac16a65b-ddff-41bf-b45d-5959cd7d9fdd" role="presentation">
<div class="k-grid-header-wrap" role="presentation">
    <table role="grid" style="width: ;">
        <colgroup role="presentation">

        </colgroup>
        <thead role="rowgroup">
            <tr role="row" data-render-row-index="0">

            </tr>
        </thead>
    </table>
</div>

    <div class="k-grid-container">
        <div class="k-grid-content k-virtual-content" id="72b5dfe2-5b56-4e8f-8798-bfdb9e1db4eb">
<div style="position: relative;">
    <div class="k-virtual-position" style="" data-translate="0" data-total="0">

            <table data-role="grid" role="grid" class="k-grid-table" style="height: auto; width: ;" aria-rowcount="0" aria-colcount="0">
                <colgroup>

                </colgroup>
                <tbody role="rowgroup">

                </tbody>
            </table>

    </div>
</div>
<div class="k-height-container">
    <div style=""></div>
</div>

</div>
jpiell commented 4 years ago

@jpiell I was able to verify that a cut could render a Telerik Grid.

@inherits TestComponentBase
@using Microsoft.AspNetCore.Components.Web
@using Egil.RazorComponents.Testing
@using Egil.RazorComponents.Testing.Diffing
@using Egil.RazorComponents.Testing.Asserting
@using Egil.RazorComponents.Testing.EventDispatchExtensions
@using Xunit
@using Telerik.Blazor.Components
@using Microsoft.Extensions.DependencyInjection;

<Fixture Test="Test1">
    <ComponentUnderTest>
        <TelerikRootComponent>
            <TelerikGrid Data="forecasts">
                <GridColumns>
                    <GridColumn Field="@nameof(WeatherForecast.Date)"></GridColumn>
                </GridColumns>
            </TelerikGrid>
        </TelerikRootComponent>
    </ComponentUnderTest>
</Fixture>
@code {

    WeatherForecast[] forecasts = new List<WeatherForecast>().ToArray();

    public void AddTelerikBlazor()
    {
        Services.AddMockJsRuntime();
        Services.AddTelerikBlazor();

}

    void Test1()
    {
        AddTelerikBlazor();

        var cut = GetComponentUnderTest();

        Assert.NotNull(cut);
    }

    public class WeatherForecast
    {
        public DateTime Date { get; set; }

        public int TemperatureC { get; set; }

        public string Summary { get; set; }

        public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
    }
}

It should actually be:

        var cut = RenderComponent<TelerikRootComponent>(
            ChildContent<FetchData>(
            ));