[英]C# MVC Integration Test - System.InvalidOperationException : Cannot resolve scoped service '<APP NAME>.Data.ApplicationDbContext' from root provider
I am trying to access my in-memory database created for integration tests.我正在尝试访问为集成测试创建的内存数据库。 My
CustomWebApplicationFactory
file looks like this:我的
CustomWebApplicationFactory
文件如下所示:
using System;
using System.Linq;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using amaranth.Data;
namespace amaranth.Tests
{
public class CustomWebApplicationFactory<TStartup>
: WebApplicationFactory<TStartup> where TStartup: class
{
public ApplicationDbContext GetDb()
{
return Services.GetService<ApplicationDbContext>() ?? throw new NullReferenceException("Unable to get DB from registered services");
}
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureServices(services =>
{
var descriptor = services.SingleOrDefault(
d => d.ServiceType ==
typeof(DbContextOptions<ApplicationDbContext>));
services.Remove(descriptor);
services.AddDbContext<ApplicationDbContext>(options =>
{
options.UseInMemoryDatabase("InMemoryDbForTesting");
});
services.AddAntiforgery(t =>
{
t.Cookie.Name = AntiForgeryTokenExtractor.AntiForgeryCookieName;
t.FormFieldName = AntiForgeryTokenExtractor.AntiForgeryFieldName;
});
var sp = services.BuildServiceProvider();
using (var scope = sp.CreateScope())
{
var scopedServices = scope.ServiceProvider;
var db = scopedServices.GetRequiredService<ApplicationDbContext>();
var logger = scopedServices
.GetRequiredService<ILogger<CustomWebApplicationFactory<TStartup>>>();
db.Database.EnsureCreated();
}
});
}
}
}
And this is the test in question, from the file IntegrationTests/AuthTests.cs
:这是有问题的测试,来自文件
IntegrationTests/AuthTests.cs
:
using System.Collections.Generic;
using System.Net;
using System.Net.Http.Headers;
using System.Security.Claims;
using System.Text.Encodings.Web;
using System.Threading.Tasks;
using Microsoft.Net.Http.Headers;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using AngleSharp.Html.Dom;
using Xunit;
using amaranth.Tests.Helpers;
using Microsoft.EntityFrameworkCore;
using amaranth.Data;
namespace amaranth.Tests
{
public class AuthTests :
IClassFixture<CustomWebApplicationFactory<amaranth.Startup>>
{
private readonly CustomWebApplicationFactory<amaranth.Startup>
_factory;
public AuthTests(CustomWebApplicationFactory<amaranth.Startup> factory)
{
_factory = factory;
}
[Fact]
public async Task Post_FirsttoRegisterClaimsAdmin()
{
// Arrange
var client = _factory.CreateClient(
new WebApplicationFactoryClientOptions
{
AllowAutoRedirect = true
});
var initResponse = await client.GetAsync("/Identity/Account/Register");
var antiForgeryValues = await AntiForgeryTokenExtractor.ExtractAntiForgeryValues(initResponse);
var postRequest = new HttpRequestMessage(HttpMethod.Post, "/Identity/Account/Register");
postRequest.Headers.Add("Cookie", new CookieHeaderValue(AntiForgeryTokenExtractor.AntiForgeryCookieName, antiForgeryValues.cookieValue).ToString());
var formModel = new Dictionary<string, string>
{
{ AntiForgeryTokenExtractor.AntiForgeryFieldName, antiForgeryValues.fieldValue },
{ "Email", "test@example.com" },
{ "Password", "pas3w0!rRd" },
{ "ConfirmPassword", "pas3w0!rRd" },
};
postRequest.Content = new FormUrlEncodedContent(formModel);
var response = await client.SendAsync(postRequest);
response.EnsureSuccessStatusCode();
var postRequest2 = new HttpRequestMessage(HttpMethod.Post, "/Home/SetAdminToUser");
postRequest2.Headers.Add("Cookie", new CookieHeaderValue(AntiForgeryTokenExtractor.AntiForgeryCookieName, antiForgeryValues.cookieValue).ToString());
var formModel2 = new Dictionary<string, string>
{
{ AntiForgeryTokenExtractor.AntiForgeryFieldName, antiForgeryValues.fieldValue },
{ "check1", "True" },
{ "check2", "True" },
{ "check3", "True" },
};
postRequest2.Content = new FormUrlEncodedContent(formModel2);
var response2 = await client.SendAsync(postRequest2);
response2.EnsureSuccessStatusCode();
Assert.Equal("http://localhost/Home/ClaimAdmin", response.RequestMessage.RequestUri.ToString());
Assert.Equal("http://localhost/Admin/MasterWalletList", response2.RequestMessage.RequestUri.ToString());
//TODO check the db to see if the user is now admin
var db = _factory.GetDb();
var user = db.UserRoles.FirstOrDefault(ur => ur.RoleId == "admin");
Assert.NotNull(user);
Assert.Equal(user?.UserId, "TODO GET USER ID");
#endregion
}
}
public class TestAuthHandler : AuthenticationHandler<AuthenticationSchemeOptions>
{
public TestAuthHandler(IOptionsMonitor<AuthenticationSchemeOptions> options,
ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock)
: base(options, logger, encoder, clock)
{
}
protected override Task<AuthenticateResult> HandleAuthenticateAsync()
{
var claims = new[] { new Claim(ClaimTypes.Name, "Test user") };
var identity = new ClaimsIdentity(claims, "Test");
var principal = new ClaimsPrincipal(identity);
var ticket = new AuthenticationTicket(principal, "Test");
var result = AuthenticateResult.Success(ticket);
return Task.FromResult(result);
}
}
}
On this line var db = _factory.GetDb();
在这一行
var db = _factory.GetDb();
I get this error:我收到此错误:
Request finished HTTP/1.1 GET http://localhost/Admin/MasterWalletList - - - 200 - text/html;+charset=utf-8 11.1544ms
请求完成 HTTP/1.1 GET http://localhost/Admin/MasterWalletList - - - 200 - text/html;+charset=utf-8 11.1544ms
[xUnit.net 00:00:02.78] amaranth.Tests.AuthTests.Post_FirsttoRegisterClaimsAdmin [FAIL][xUnit.net 00:00:02.78] amaranth.Tests.AuthTests.Post_FirsttoRegisterClaimsAdmin [失败]
Failed amaranth.Tests.AuthTests.Post_FirsttoRegisterClaimsAdmin [1 s]amaranth.Tests.AuthTests.Post_FirsttoRegisterClaimsAdmin 失败 [1 秒]
Error Message:
错误信息:
System.InvalidOperationException : Cannot resolve scoped service 'amaranth.Data.ApplicationDbContext' from root provider.
System.InvalidOperationException:无法从根提供程序解析范围服务“amaranth.Data.ApplicationDbContext”。
Stack Trace:
堆栈跟踪:
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteValidator.ValidateResolution(Type serviceType, IServiceScope scope, IServiceScope rootScope)在 Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteValidator.ValidateResolution(类型 serviceType、IServiceScope 范围、IServiceScope rootScope)
at Microsoft.Extensions.DependencyInjection.ServiceProvider.GetService(Type serviceType, ServiceProviderEngineScope serviceProviderEngineScope)在 Microsoft.Extensions.DependencyInjection.ServiceProvider.GetService(类型 serviceType,ServiceProviderEngineScope serviceProviderEngineScope)
at Microsoft.Extensions.DependencyInjection.ServiceProvider.GetService(Type serviceType)在 Microsoft.Extensions.DependencyInjection.ServiceProvider.GetService(类型 serviceType)
at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetService[T](IServiceProvider provider) at amaranth.Tests.CustomWebApplicationFactory`1.GetDb() in /path/to/project/directory/amaranth.Tests/CustomWebApplicationFactory.cs:line 17 at amaranth.Tests.AuthTests.Post_FirsttoRegisterClaimsAdmin() in /path/to/project/directory/amaranth.Tests/IntegrationTests/AuthTests.cs:line 128在 Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetService[T](IServiceProvider provider) at amaranth.Tests.CustomWebApplicationFactory`1.GetDb() in /path/to/project/directory/amaranth.Tests/CustomWebApplicationFactory.cs:line 17 at /path/to/project/directory/amaranth.Tests/IntegrationTests/AuthTests.cs:line 128 中的 amaranth.Tests.AuthTests.Post_FirsttoRegisterClaimsAdmin()
--- End of stack trace from previous location ------ 上一个位置的堆栈跟踪结束 ---
info: Microsoft.EntityFrameworkCore.Infrastructure[10403]信息:Microsoft.EntityFrameworkCore.Infrastructure[10403]
What am I doing wrong?我究竟做错了什么? Why can't I access
amaranth.Data.ApplicationDbContext
?为什么我无法访问
amaranth.Data.ApplicationDbContext
?
I solved this by making var db
scoped.我通过使
var db
作用域解决了这个问题。 So you can fix this by replacing:因此,您可以通过替换来解决此问题:
var db = _factory.GetDb();
var user = db.UserRoles.FirstOrDefault(ur => ur.RoleId == "admin");
Assert.NotNull(user);
Assert.Equal(user?.UserId, "TODO GET USER ID");
With this:有了这个:
Microsoft.AspNetCore.Identity.IdentityUserRole<string>? userRl;
Microsoft.AspNetCore.Identity.IdentityUser? user;
using (var scope = _factory.Services.CreateScope())
{
var scopedServices = scope.ServiceProvider;
var db = scopedServices.GetRequiredService<ApplicationDbContext>();
user = db.Users.FirstOrDefault();
userRl = db.UserRoles.FirstOrDefault();
}
Assert.NotNull(user);
Assert.True(userRl?.UserId == user?.Id);
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.