Skip to content

Commit

Permalink
fix: Change all bUnit services registration from singleton to scoped
Browse files Browse the repository at this point in the history
  • Loading branch information
egil committed Jul 7, 2023
1 parent ef86f1c commit c34880d
Show file tree
Hide file tree
Showing 10 changed files with 255 additions and 240 deletions.
394 changes: 199 additions & 195 deletions CHANGELOG.md

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions benchmark/bunit.benchmarks/BenchmarkBase.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Attributes;
using Bunit.Rendering;
using Microsoft.AspNetCore.Components;
using Microsoft.Extensions.DependencyInjection;
Expand Down Expand Up @@ -42,6 +42,6 @@ protected virtual void InternalCleanup()

protected virtual void RegisterServices(IServiceCollection serviceCollection)
{
services.AddSingleton<BunitHtmlParser>();
services.AddScoped<BunitHtmlParser>();
}
}
}
2 changes: 1 addition & 1 deletion src/bunit.core/TestContextBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ protected TestContextBase()
{
Services = new TestServiceProvider();
#if NET5_0_OR_GREATER
Services.AddSingleton<ComponentFactoryCollection>(_ => ComponentFactories);
Services.AddScoped<ComponentFactoryCollection>(_ => ComponentFactories);
#endif
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ public static FakePersistentComponentState AddFakePersistentComponentState(this
if (testContext is null)
throw new ArgumentNullException(nameof(testContext));

testContext.Services.AddSingleton<ComponentStatePersistenceManager>();
testContext.Services.AddSingleton<PersistentComponentState>(s => s.GetRequiredService<ComponentStatePersistenceManager>().State);
testContext.Services.AddScoped<ComponentStatePersistenceManager>();
testContext.Services.AddScoped<PersistentComponentState>(s => s.GetRequiredService<ComponentStatePersistenceManager>().State);
return new FakePersistentComponentState(testContext.Services);
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/bunit.core/TestServiceProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ public object GetService(Type serviceType)
{
if (serviceProvider is null)
{
serviceCollection.AddSingleton<TestServiceProvider>(this);
serviceCollection.AddScoped<TestServiceProvider>(_ => this);
rootServiceProvider = serviceCollection.BuildServiceProvider(options);
serviceScope = rootServiceProvider.CreateScope();
serviceProvider = serviceScope.ServiceProvider;
Expand Down
2 changes: 1 addition & 1 deletion src/bunit.web.testcomponents/Xunit.Sdk/RazorTestInvoker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,6 @@ protected override object CallTestMethod(object testClassInstance)

private void RegisterXunitHelpersInTest(RazorTestBase test)
{
test.Services.AddSingleton<ITestOutputHelper>(_ => testOutputHelperFactory());
test.Services.AddScoped<ITestOutputHelper>(_ => testOutputHelperFactory());
}
}
38 changes: 19 additions & 19 deletions src/bunit.web/Extensions/TestServiceProviderExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,37 +28,37 @@ public static IServiceCollection AddDefaultTestContextServices(this IServiceColl

// Placeholders and defaults for common Blazor services
services.AddLogging();
services.AddSingleton<AuthenticationStateProvider, PlaceholderAuthenticationStateProvider>();
services.AddSingleton<IAuthorizationService, PlaceholderAuthorizationService>();
services.AddSingleton<HttpClient, PlaceholderHttpClient>();
services.AddSingleton<IStringLocalizer, PlaceholderStringLocalization>();
services.AddScoped<AuthenticationStateProvider, PlaceholderAuthenticationStateProvider>();
services.AddScoped<IAuthorizationService, PlaceholderAuthorizationService>();
services.AddScoped<HttpClient, PlaceholderHttpClient>();
services.AddScoped<IStringLocalizer, PlaceholderStringLocalization>();

// bUnits fake JSInterop
services.AddSingleton<IJSRuntime>(jsInterop.JSRuntime);
services.AddScoped<IJSRuntime>(_ => jsInterop.JSRuntime);

// bUnits fake Navigation Manager
services.AddSingleton<FakeNavigationManager>();
services.AddSingleton<NavigationManager>(s => s.GetRequiredService<FakeNavigationManager>());
services.AddSingleton<INavigationInterception, FakeNavigationInterception>();
services.AddScoped<FakeNavigationManager>();
services.AddScoped<NavigationManager>(s => s.GetRequiredService<FakeNavigationManager>());
services.AddScoped<INavigationInterception, FakeNavigationInterception>();

// bUnits fake WebAssemblyHostEnvironment
services.AddSingleton<FakeWebAssemblyHostEnvironment>();
services.AddSingleton<IWebAssemblyHostEnvironment>(s => s.GetRequiredService<FakeWebAssemblyHostEnvironment>());
services.AddScoped<FakeWebAssemblyHostEnvironment>();
services.AddScoped<IWebAssemblyHostEnvironment>(s => s.GetRequiredService<FakeWebAssemblyHostEnvironment>());

// bUnit specific services
services.AddSingleton<TestContextBase>(testContext);
services.AddSingleton<WebTestRenderer>();
services.AddSingleton<TestRenderer>(s => s.GetRequiredService<WebTestRenderer>());
services.AddSingleton<Renderer>(s => s.GetRequiredService<WebTestRenderer>());
services.AddSingleton<ITestRenderer>(s => s.GetRequiredService<WebTestRenderer>());
services.AddSingleton<HtmlComparer>();
services.AddSingleton<BunitHtmlParser>();
services.AddSingleton<IRenderedComponentActivator, RenderedComponentActivator>();
services.AddScoped<TestContextBase>(_ => testContext);
services.AddScoped<WebTestRenderer>();
services.AddScoped<TestRenderer>(s => s.GetRequiredService<WebTestRenderer>());
services.AddScoped<Renderer>(s => s.GetRequiredService<WebTestRenderer>());
services.AddScoped<ITestRenderer>(s => s.GetRequiredService<WebTestRenderer>());
services.AddScoped<HtmlComparer>();
services.AddScoped<BunitHtmlParser>();
services.AddScoped<IRenderedComponentActivator, RenderedComponentActivator>();

services.AddMemoryCache();

#if NET6_0_OR_GREATER
services.AddSingleton<IErrorBoundaryLogger, BunitErrorBoundaryLogger>();
services.AddScoped<IErrorBoundaryLogger, BunitErrorBoundaryLogger>();
#endif
return services;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ public static TestAuthorizationContext AddTestAuthorization(this TestContextBase
throw new ArgumentNullException(nameof(context));

context.RenderTree.TryAdd<CascadingAuthenticationState>();
context.Services.AddSingleton<FakeSignOutSessionStateManager>();
context.Services.AddScoped<FakeSignOutSessionStateManager>();
#pragma warning disable CS0618
context.Services.AddSingleton<SignOutSessionStateManager>(s => s.GetRequiredService<FakeSignOutSessionStateManager>());
context.Services.AddScoped<SignOutSessionStateManager>(s => s.GetRequiredService<FakeSignOutSessionStateManager>());
#pragma warning restore CS0618
var authCtx = new TestAuthorizationContext();
authCtx.SetNotAuthorized();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,17 +56,19 @@ public class TestAuthorizationContext
/// <param name="services">Service provider to use.</param>
public void RegisterAuthorizationServices(IServiceCollection services)
{
services.AddSingleton<IAuthorizationService>(authService);
services.AddSingleton<IAuthorizationPolicyProvider>(policyProvider);
services.AddSingleton<AuthenticationStateProvider>(authProvider);
services.AddScoped<IAuthorizationService>(_ => authService);
services.AddScoped<IAuthorizationPolicyProvider>(_ => policyProvider);
services.AddScoped<AuthenticationStateProvider>(_ => authProvider);
}

/// <summary>
/// Authenticates the user with specified name and authorization state.
/// </summary>
/// <param name="userName">User name for the principal identity.</param>
/// <param name="state">Authorization state.</param>
public TestAuthorizationContext SetAuthorized(string userName, AuthorizationState state = AuthorizationState.Authorized)
public TestAuthorizationContext SetAuthorized(
string userName,
AuthorizationState state = AuthorizationState.Authorized)
{
IsAuthenticated = true;
UserName = userName;
Expand Down
33 changes: 21 additions & 12 deletions tests/bunit.core.tests/Rendering/BunitComponentActivatorTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,40 +6,49 @@ namespace Bunit.Rendering;

public class BunitComponentActivatorTest : TestContext
{
[Fact(DisplayName = "Default activator")]
public void Test001()
public static TheoryData<Action<IServiceCollection, IComponentActivator>> CustomActivatorRegistrations { get; } = new()
{
var activator = new CustomComponentActivator();
Services.AddSingleton<IComponentActivator>(activator);
static (services, activator) => services.AddSingleton(activator),
static (services, activator) => services.AddScoped(_ => activator),
static (services, activator) => services.AddTransient(_ => activator),
};

[Fact(DisplayName = "Default bUnit activator")]
public void Test001()
{
var cut = RenderComponent<Simple1>();

cut.Instance.ShouldBeOfType<Simple1>();
}

[Fact(DisplayName = "Custom singleton IComponentActivator registered in Services")]
public void Test002()
[Theory(DisplayName = "Custom IComponentActivator registered in Services")]
[MemberData(nameof(CustomActivatorRegistrations))]
public void Test002(Action<IServiceCollection, IComponentActivator> registerCustomActivator)
{
var activator = new CustomComponentActivator();
Services.AddSingleton<IComponentActivator>(activator);
registerCustomActivator(Services, activator);

RenderComponent<Simple1>();

activator.RequestedComponentTypes
activator
.RequestedComponentTypes
.ShouldHaveSingleItem()
.ShouldBe(typeof(Simple1));
}

[Fact(DisplayName = "Custom singleton IComponentActivator registered in Services with ComponentFactories in use")]
public void Test003()
[Theory(DisplayName = "Custom singleton IComponentActivator registered in Services with ComponentFactories in use")]
[MemberData(nameof(CustomActivatorRegistrations))]
public void Test003(Action<IServiceCollection, IComponentActivator> registerCustomActivator)
{
var activator = new CustomComponentActivator();
Services.AddSingleton<IComponentActivator>(activator);
registerCustomActivator(Services, activator);

ComponentFactories.AddStub<ClickCounter>();

var cut = RenderComponent<Wrapper>(ps => ps.AddChildContent<ClickCounter>());

activator.RequestedComponentTypes
activator
.RequestedComponentTypes
.ShouldHaveSingleItem()
.ShouldBe(typeof(Wrapper));
cut.HasComponent<ClickCounter>().ShouldBeFalse();
Expand Down

0 comments on commit c34880d

Please sign in to comment.