Skip to content

Commit ba9227d

Browse files
committed
Update test infra and dependencies; refactor user mocking
- Bump ActualLab.Fusion, QuestPDF, and Blazor.Serilog.Sinks.SQLite to latest versions. - Refactor ICurrentUserAccessor mocking to singleton with SessionInfo setup. - Use live DbConnection for Respawner checkpoint and database resets. - Switch SendAsync to use IMediator directly. - Add CreateDbContext helper and improve AddAsync/CountAsync. - Clean up usings and minor formatting in test base. - Improves reliability and maintainability of integration tests.
1 parent adb9a72 commit ba9227d

4 files changed

Lines changed: 74 additions & 52 deletions

File tree

src/Application/Application.csproj

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,9 @@
2424
<PackageReference Include="System.Linq.Dynamic.Core" Version="1.7.1" />
2525
<PackageReference Include="Hangfire.Core" Version="1.8.22" />
2626
<PackageReference Include="ZiggyCreatures.FusionCache" Version="2.4.0" />
27-
<PackageReference Include="ActualLab.Fusion" Version="11.3.81" />
28-
<PackageReference Include="ActualLab.Fusion.Blazor" Version="11.3.81" />
29-
<PackageReference Include="ActualLab.Generators" Version="11.3.81">
27+
<PackageReference Include="ActualLab.Fusion" Version="11.3.84" />
28+
<PackageReference Include="ActualLab.Fusion.Blazor" Version="11.3.84" />
29+
<PackageReference Include="ActualLab.Generators" Version="11.3.84">
3030
<PrivateAssets>all</PrivateAssets>
3131
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
3232
</PackageReference>

src/Infrastructure/Infrastructure.csproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,13 @@
1111
<PackageReference Include="Microsoft.AspNetCore.Authentication.Facebook" Version="10.0.1" />
1212
<PackageReference Include="Microsoft.AspNetCore.Authentication.Google" Version="10.0.1" />
1313
<PackageReference Include="Microsoft.AspNetCore.Authentication.MicrosoftAccount" Version="10.0.1" />
14-
<PackageReference Include="QuestPDF" Version="2025.7.4" />
14+
<PackageReference Include="QuestPDF" Version="2025.12.0" />
1515
<PackageReference Include="Serilog.AspNetCore" Version="10.0.0" />
1616
<PackageReference Include="Serilog.Sinks.MSSqlServer" Version="9.0.2" />
1717
<PackageReference Include="Serilog.Sinks.Postgresql.Alternative" Version="4.2.0" />
1818
<PackageReference Include="Serilog.Sinks.Async" Version="2.1.0" />
1919
<PackageReference Include="Serilog.Sinks.Console" Version="6.1.1" />
20-
<PackageReference Include="Blazor.Serilog.Sinks.SQLite" Version="1.0.8" />
20+
<PackageReference Include="Blazor.Serilog.Sinks.SQLite" Version="1.0.9" />
2121
<PackageReference Include="Serilog.Sinks.Seq" Version="9.0.0" />
2222
</ItemGroup>
2323
<ItemGroup>

tests/Application.IntegrationTests/TestBase.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using System.Threading.Tasks;
1+
using System.Threading.Tasks;
22
using NUnit.Framework;
33

44
namespace CleanArchitecture.Blazor.Application.IntegrationTests;
@@ -12,4 +12,4 @@ public async Task TestSetUp()
1212
{
1313
await ResetState();
1414
}
15-
}
15+
}

tests/Application.IntegrationTests/Testing.cs

Lines changed: 67 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,9 @@
44
using System.Threading.Tasks;
55
using CleanArchitecture.Blazor.Application.Common.Interfaces;
66
using CleanArchitecture.Blazor.Application.Common.Interfaces.Identity;
7-
using CleanArchitecture.Blazor.Application.Common.Interfaces.MediatorWrapper;
8-
using CleanArchitecture.Blazor.Application.Common.Interfaces.MultiTenant;
97
using CleanArchitecture.Blazor.Domain.Identity;
108
using CleanArchitecture.Blazor.Infrastructure;
11-
using CleanArchitecture.Blazor.Infrastructure.Extensions;
9+
using CleanArchitecture.Blazor.Application.Common.Extensions;
1210
using CleanArchitecture.Blazor.Infrastructure.Persistence;
1311
using MediatR;
1412
using Microsoft.AspNetCore.Hosting;
@@ -20,6 +18,12 @@
2018
using NUnit.Framework;
2119
using Respawn;
2220
using Respawn.Graph;
21+
using CleanArchitecture.Blazor.Application.Features.PicklistSets.DTOs;
22+
using CleanArchitecture.Blazor.Infrastructure.Services;
23+
using CleanArchitecture.Blazor.Application.Features.Tenants.DTOs;
24+
using CleanArchitecture.Blazor.Infrastructure.Services.MultiTenant;
25+
using System.Data.Common;
26+
using CleanArchitecture.Blazor.Application.Common.Interfaces.MultiTenant;
2327

2428
namespace CleanArchitecture.Blazor.Application.IntegrationTests;
2529

@@ -30,7 +34,6 @@ public class Testing
3034
private static IServiceScopeFactory _scopeFactory;
3135
private static Respawner _checkpoint;
3236
private static string _currentUserId;
33-
private static string _currentTenantId;
3437

3538
[OneTimeSetUp]
3639
public async Task RunBeforeAnyTests()
@@ -46,7 +49,6 @@ public async Task RunBeforeAnyTests()
4649

4750
var services = new ServiceCollection();
4851

49-
5052
services.AddSingleton(Mock.Of<IWebHostEnvironment>(w =>
5153
w.EnvironmentName == "Development" &&
5254
w.ApplicationName == "Server.UI"));
@@ -58,44 +60,60 @@ public async Task RunBeforeAnyTests()
5860

5961
//startup.ConfigureServices(services);
6062

61-
// Replace service registration for ICurrentUserService
62-
// Remove existing registration
63-
var currentUserServiceDescriptor = services.FirstOrDefault(d =>
63+
// 替换 IUserContextAccessor 的注册
64+
var userContextServiceDescriptor = services.FirstOrDefault(d =>
6465
d.ServiceType == typeof(ICurrentUserAccessor));
66+
if (userContextServiceDescriptor != null)
67+
{
68+
services.Remove(userContextServiceDescriptor);
69+
}
6570

66-
services.Remove(currentUserServiceDescriptor);
67-
68-
// Register testing version
69-
services.AddScoped(provider =>
70-
Mock.Of<ICurrentUserAccessor>(s => s.SessionInfo.UserId == _currentUserId));
71-
72-
73-
_scopeFactory = services.BuildServiceProvider().GetService<IServiceScopeFactory>();
74-
75-
_checkpoint = await Respawner.CreateAsync(_configuration.GetValue<string>("DatabaseSettings:ConnectionString"),
76-
new RespawnerOptions
71+
// 使用 Moq 创建 Mock 对象并配置 Current 属性
72+
services.AddSingleton<ICurrentUserAccessor>(provider =>
73+
{
74+
var mockUserContextAccessor = new Mock<ICurrentUserAccessor>();
75+
if (!string.IsNullOrEmpty(_currentUserId))
7776
{
78-
TablesToIgnore = new Table[] { "__EFMigrationsHistory" }
79-
});
77+
var userContext = new SessionInfo("admin", "admin","admin", null,"", "", UserPresence.Available);
78+
mockUserContextAccessor.Setup(x => x.SessionInfo).Returns(userContext);
79+
}
80+
return mockUserContextAccessor.Object;
81+
});
8082

83+
_scopeFactory = services.BuildServiceProvider().GetService<IServiceScopeFactory>();
8184
EnsureDatabase();
85+
using var scope = services.BuildServiceProvider().CreateScope();
86+
var context = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
87+
var connection = context.Database.GetDbConnection();
88+
await connection.OpenAsync();
89+
try
90+
{
91+
_checkpoint = await Respawner.CreateAsync(
92+
connection,
93+
new RespawnerOptions
94+
{
95+
TablesToIgnore = new Table[] { "__EFMigrationsHistory" }
96+
});
97+
}
98+
finally
99+
{
100+
await connection.CloseAsync();
101+
}
102+
103+
82104
}
83105

84106
private static void EnsureDatabase()
85107
{
86108
using var scope = _scopeFactory.CreateScope();
87-
88109
var context = scope.ServiceProvider.GetService<ApplicationDbContext>();
89-
90110
context.Database.Migrate();
91111
}
92112

93113
public static async Task<TResponse> SendAsync<TResponse>(IRequest<TResponse> request)
94114
{
95115
using var scope = _scopeFactory.CreateScope();
96-
97-
var mediator = scope.ServiceProvider.GetService<IScopedMediator>();
98-
116+
var mediator = scope.ServiceProvider.GetService<IMediator>();
99117
return await mediator.Send(request);
100118
}
101119

@@ -112,69 +130,73 @@ public static async Task<string> RunAsAdministratorAsync()
112130
public static async Task<string> RunAsUserAsync(string userName, string password, string[] roles)
113131
{
114132
using var scope = _scopeFactory.CreateScope();
115-
116133
var userManager = scope.ServiceProvider.GetService<UserManager<ApplicationUser>>();
117-
118134
var user = new ApplicationUser { UserName = userName, Email = userName };
119-
120135
var result = await userManager.CreateAsync(user, password);
121136

122137
if (roles.Any())
123138
{
124139
var roleManager = scope.ServiceProvider.GetService<RoleManager<IdentityRole>>();
125-
126-
foreach (var role in roles) await roleManager.CreateAsync(new IdentityRole(role));
127-
140+
foreach (var role in roles)
141+
{
142+
await roleManager.CreateAsync(new IdentityRole(role));
143+
}
128144
await userManager.AddToRolesAsync(user, roles);
129145
}
130146

131147
if (result.Succeeded)
132148
{
133149
_currentUserId = user.Id;
134-
135150
return _currentUserId;
136151
}
137152

138-
var errors = string.Join(Environment.NewLine, result.ToApplicationResult().Errors);
139-
153+
var errors = string.Join(Environment.NewLine, result.Errors);
140154
throw new Exception($"Unable to create {userName}.{Environment.NewLine}{errors}");
141155
}
142156

143157
public static async Task ResetState()
144158
{
145-
await _checkpoint.ResetAsync(_configuration.GetValue<string>("DatabaseSettings:ConnectionString"));
159+
using var scope = _scopeFactory.CreateScope();
160+
var context = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
161+
var connection = context.Database.GetDbConnection();
162+
await connection.OpenAsync();
163+
try
164+
{
165+
await _checkpoint.ResetAsync(connection);
166+
}
167+
finally
168+
{
169+
await connection.CloseAsync();
170+
}
146171
_currentUserId = null;
147-
_currentTenantId = null;
148172
}
149173

150174
public static async Task<TEntity> FindAsync<TEntity>(params object[] keyValues)
151175
where TEntity : class
152176
{
153177
using var scope = _scopeFactory.CreateScope();
154-
155178
var context = scope.ServiceProvider.GetService<ApplicationDbContext>();
156-
157179
return await context.FindAsync<TEntity>(keyValues);
158180
}
181+
public static IApplicationDbContext CreateDbContext()
182+
{
183+
var scope = _scopeFactory.CreateScope();
184+
return scope.ServiceProvider.GetRequiredService<ApplicationDbContext>();
185+
}
159186

160187
public static async Task AddAsync<TEntity>(TEntity entity)
161188
where TEntity : class
162189
{
163190
using var scope = _scopeFactory.CreateScope();
164-
165191
var context = scope.ServiceProvider.GetService<ApplicationDbContext>();
166-
167192
context.Add(entity);
168-
169193
await context.SaveChangesAsync();
170194
}
171195

172196
public static async Task<int> CountAsync<TEntity>() where TEntity : class
173197
{
174198
using var scope = _scopeFactory.CreateScope();
175-
176199
var context = scope.ServiceProvider.GetService<ApplicationDbContext>();
177-
178200
return await context.Set<TEntity>().CountAsync();
179201
}
180202

@@ -194,4 +216,4 @@ public static ITenantService CreateTenantsService()
194216
public void RunAfterAnyTests()
195217
{
196218
}
197-
}
219+
}

0 commit comments

Comments
 (0)