Closed spankr closed 1 month ago
Hey @spankr
Thanks for reporting this. This code is mainly for backward compatibility, and Blazor itself has roughly the same piece:
return serviceProvider.GetService<IComponentActivator>()
?? new DefaultComponentActivator(serviceProvider);
That said, I am unsure if I can follow your expected behavior.
We are basically offering the way for a custom IComponentActivator
if available, otherwise we use the bUnit internal one.
I can fondly remember that we did this as customer feedback (@egil do you remember the ticket?).
I would think this could also be resolved with a some documentation where it talks about mocking and using a fallback provider. That the fallback provider should not respond with a mocked IComponentActivator.
Indeed, that would be a fair point.
We merged the updated documentation. Thanks for reporting @spankr
For anyone using AutoFixture in combination with automocking like NSubstitute
, this is how I have fixed this:
// BunitCustomization.cs
public class BunitCustomization : ICustomization
{
public void Customize(IFixture fixture)
{
fixture.Customize<TestContext>(composer => composer.FromFactory(() =>
{
var testContext = new TestContext();
testContext.Services.AddFallbackServiceProvider(new AutoFixtureServiceProvider(fixture));
return testContext;
});
fixture.Customize<BunitJSInterop>(composer => composer.FromFactory(
() => fixture.Freeze<TestContext>().JSInterop);
// This is necessary to prevent an automatically created instance by NSubstitute, which prevents components from actually rendering in BUnit
fixture.Customize<IComponentActivator>(composer => composer.FromFactory(
() => (IComponentActivator)null));
}
}
// AutoFixtureServiceProvider.cs
public class AutoFixtureServiceProvider : IServiceProvider
{
private readonly IFixture _fixture;
public AutoFixtureServiceProvider(IFixture fixture)
=> _fixture = fixture;
public object? GetService(Type serviceType)
=> _fixture.Create(serviceType, new SpecimenContext(_fixture));
}
And then you can do in your unittest:
public class MyComponentTests
{
[Theory, BunitAutoData]
public void MyComponent_Should_RenderCorrectly(TestContext testContext)
{
// Arrange
// Act
var renderAction = () => testContext.RenderComponent<MyComponent>();
// Assert
renderAction.Should().NotThrow();
}
}
Description
Our team came across this issue when upgrading from 1.25.3. We narrowed it down to the change in 1.27.12-preview, specifically the feature for supporting parameters as query string.
The change to src/bunit.web/TestContext.cs is asking the
IServiceProvider
for an implementation ofIComponentActivator
and then implementingWebTestRenderer
with it, if it finds one.The issue for us is that we use AutoMock as the fallback provider. We use loose behavior for mocking (for anyone unfamiliar with mocking behaviors, "loose" essentially means that if a mock is not pre-defined, a default one will be generated/provided for your test.). This means that when line 92 in TestContext.cs asks for an
IComponentActivator
, AutoMock provides a mocked implementation (and not null).Our tests then die horribly with this complaint:
Reproduction Steps
We have a simple test scenario
and our test base class
It's a hack but this modification to our code bypasses the problem for us:
Expected behavior
That our tests run without a low-level error in
Microsoft.AspNetCore.Components.ComponentFactory.InstantiateComponent
.Actual behavior
Test failure with
Known Workarounds
When setting up the FallbackServiceProvider, add a check for the type of
IComponentActivator
and return null.Other information
Commit 8f99afa
src/bunit.web/TestContext.cs, lines 92-95: