我在.NET Core中开发集成测试。因为控制器具有多个依赖性,所以手动创建它们的实例是不方便的。以下是我如何创建ServiceProvider实例以便能够访问.NET Core本机DI容器:
HostingEnvironment env = new HostingEnvironment();
env.ContentRootPath = Directory.GetCurrentDirectory();
env.EnvironmentName = "Development";
Startup startup = new Startup(env);
ServiceCollection sc = new ServiceCollection();
startup.ConfigureServices(sc);
ServiceProvider = sc.BuildServiceProvider();
Startup
课程来自经过测试的装配。
令我惊讶的是,服务提供商无法创建控制器实例。它可以实例化注册的服务,但不能实现控制器。有没有人知道为什么会这样?
答案 0 :(得分:3)
您应该使用TestServer
来引导一个完整的测试环境(可选地使用不同的启动类或环境变量,您可以在其中用内存存储替换数据存储)。
ASP.NET核心documentation很好地涵盖了这个案例。
var server = new TestServer(new WebHostBuilder()
.UseStartup<Startup>()
// this would cause it to use StartupIntegrationTest or ConfigureServicesIntegrationTest / ConfigureIntegrationTest methods (if existing)
// rather than Startup, ConfigureServices and Configure
.UseEnvironment("IntegrationTest"));
在这些替代/环境相关的方法中,您可以将集成测试特定替换/ DI配置。
其次,默认情况下,控制器(以及标签助手和视图组件)未在DI系统中注册。控制器工厂将解析类型。如果您希望通过内置或第三方IoC容器解析控制器,您必须告诉ASP.NET Core明确注册它们(请参阅this related question)。
以下是一个例子:
services
.AddMvc()
.AddControllersAsServices()
.AddViewComponentsAsServices()
.AddTagHelpersAsServices();
我建议你使用第一种方法,因为它更加一致,而且正是具有环境支持的Startup
类的原因。
重要的补充。如果您使用TestServer
,您还可以访问其DI容器(IServiceProvider
实例)。
var server = new TestServer(new WebHostBuilder()
.UseStartup<Startup>()
.UseEnvironment("IntegrationTest"));
var controller = server.Host.Services.GetService<MyController>();
答案 1 :(得分:0)
由于控制器具有多个依赖关系,因此手动创建实例非常不方便。
不应该是这种情况。编写单元测试时,不应该依赖DI容器;你应该手动创建依赖项。在容器上添加依赖项只会使测试复杂化,并在库中添加不必要的依赖项。
您可以应用一些有趣的模式来轻松创建SUT。其中一个是Test Data Builder模式的变体,你在测试类中有一个私有工厂方法来构建SUT:
private static ShipOrderController CreateController(
IShipOrderHandler handler = null,
ILogger logger = null,
IMailSender mailSender = null)
{
return new ShipOrderController(
handler ?? new ShipOrderHandlerStub(),
logger ?? new LoggerStub(),
mailSender ?? new MailSenderStub());
}
这样的SUT Builder使您的单元测试具有可读性和可维护性,因为向SUT添加依赖项只会影响工厂方法;不是任何现有的测试。
测试可以如下所示:
[TestMethod]
public void Index_Always_Logs()
{
// Arrange
var logger = new FakeLogger();
var sut = CreateController(logger: logger);
// Act
sut.Index();
// Assert
Assert.IsTrue(logger.Entries.Any());
}