[Route("api/[controller]")]
public class DigitalDocumentController : Controller
{
private IDigitalDocumentService digitalDocumentService;
private IDatabaseInitializer databaseInitializer;
public DigitalDocumentController(IDigitalDocumentService digitalDocumentService)
{
this.digitalDocumentService = digitalDocumentService;
}
public DigitalDocumentController(IDatabaseInitializer databaseInitializer)
{
this.databaseInitializer = databaseInitializer;
}
我希望我的项目中的两个控制器构造器能够在xUnit Testing中模拟,但是我的招摇界面中有一个错误{ “错误”:“在类型'i2ana.Web.Controllers.DigitalDocumentController'中找到了接受所有给定参数类型的多个构造函数。应该只有一个适用的构造函数。 } 有人可以帮我怎么做吗?
... 我想做的是在数据库中测试名称字段的唯一性 我的测试代码:
[事实] 公共无效AddNotUniqueName_ReturnsNotFoundObjectResult() { var digitalDocument =新的DigitalDocument { 图片=新字节[] {0x20、0x20、0x20、0x20、0x20、0x20、0x20}, CreatedOn = DateTime.Today, ID = 6 位置=“温度”, 名称=“花”, Tages =新列表{新标签{ID = 1,值=“标签1”},新标签{ID = 1,值=“标签2”}} }; //安排 var mockRepo = new Mock(); mockRepo.Setup(repo => repo.SeedAsync())。Returns(Task.FromResult(AddUniqueDigitalDocument(digitalDocument))); var controller = new DigitalDocumentController(mockRepo.Object);
// Act var result = controller.Add(digitalDocument); // Assert var viewResult = Assert.IsType<NotFoundObjectResult>(result); var model = Assert.IsAssignableFrom<int>(viewResult.Value); Assert.NotEqual(6, model); }
“ AddUniqueDigitalDocument”仅返回6以测试新的数字文档与我的初始化数据ID是否不同。
答案 0 :(得分:1)
使用依赖项注入时,您应该只有一个可以满足所有依赖项的构造函数。否则,DI容器如何知道要使用哪个构造函数?这是您的问题。使用Microsoft.Extensions.DependencyInjection程序包,并且由于这是您要注入的控制器,因此只有一种合理的方法可以解决此问题:不要注册IDigitalDocumentService
或{{ 1}}。如果仅注册了一个,则服务集合将仅使用其已注册服务的构造函数。
使用功能更强大的DI容器是可能的,您可以配置一些内容以允许其选择适当的构造函数。但是,如何执行将完全取决于最终要使用的DI容器,因此在这一点上还不能说太多。只需意识到默认容器(Microsoft.Extensions.DependencyInjection)是有意简化的,所以如果您需要更复杂,则应将其放入一个完整的DI容器中。
更新
您应该使用测试主机和内存数据库进行集成测试。基本方法是:
IDatatabaseInitializer
在您应用的public MyTests()
{
_server = new TestServer(new WebHostBuilder().UseStartup<TestStartup>());
_context = _server.Host.Services.GetRequiredService<MyContext>();
_client = _server.CreateClient();
}
中,创建一个虚拟方法:
Startup
然后,在public virtual void ConfigureDatabase(IServiceCollection services)
{
// normal database setup here, e.g.
services.AddDbContext<MyContext>(o =>
o.UseSqlServer(Configuration.GetConnectionString("Foo")));
}
中,用对此方法的调用替换您的数据库设置。
最后,在测试项目中,创建一个ConfigureServices
类并覆盖TestStartup
方法:
ConfigureDatabase
现在,在测试中,您仅向测试客户端发出请求(这只是一个public class TestStartup : Startup
{
public override void ConfigureDatabase(IServiceCollection services)
{
var databaseName = Guid.NewGuid().ToString();
services.AddDbContext<MyContext>(o =>
o.UseInMemoryDatabase(databaseName));
}
}
实例,因此它的工作原理与其他HttpClient
一样)。首先,使用适当的测试数据设置数据库,然后确保返回正确的响应:
HttpClient
使用API显然要容易得多,就像使用Web应用程序一样,您总是需要进行一些HTML解析。但是,docs和相应的sample app可以帮助您。
此外,在实际操作中,您还希望使用测试夹具来避免必须为每个测试引导测试服务器。同样,文档涵盖了您。但是要注意的一件事是,一旦您切换到使用固定装置,您的数据库将在测试之间保持不变。要隔离测试数据,请确保在每次测试之前在上下文中调用// Arrange
_context.Add(new DigitalDocument { Name = "Foo" });
await _context.SaveChanges();
// Act
// Submit a `DigitalDocument` with the same name via `_client`
// Assert
// Inspect the response body for some indication that it was considered invalid. Or you could simply assert that no new `DigitalDocument` was created by querying `_context` (or both)
。这可以在测试类的构造函数中轻松完成:
EnsureDeleted()
但是,我什至在测试中都不喜欢这么多的自举代码,所以我通常改为继承自Fixture类:
public class MyTests : IClassFixture<WebApplicationFactory<Startup>>
{
private readonly HttpClient _client;
private readonly MyContext _context;
public MyTests(WebApplicationFactory<Startup> factory)
{
factory = factory.WithWebHostBuilder(builder => builder.UseStartup<TestStartup>());
_client = factory.CreateClient();
_context = factory.Server.Host.Services.GetRequiredService<MyContext>();
_context.EnsureDeleted();
}
然后,对于每个测试类别:
public class TestServerFixture : IClassFixture<WebApplicationFactory<Startup>>
{
protected readonly HttpClient _client;
protected readonly MyContext _context;
public TestServerFixture(WebApplicationFactory<Startup> factory)
{
factory = factory.WithWebHostBuilder(builder => builder.UseStartup<TestStartup>());
_client = factory.CreateClient();
_context = factory.Server.Host.Services.GetRequiredService<MyContext>();
_context.EnsureDeleted();
}
}
这似乎很多,但其中大多数是一次性设置。这样,您的测试将在许多方面变得更加准确,更加健壮,甚至更加轻松。