Dependency injection 为什么ServiceProvider无法在ASP.NET Core中创建控制器实例?
我在.NETCore中开发集成测试。因为控制器有多个依赖项,所以手动创建它们的实例很不方便。以下是我如何创建ServiceProvider实例以访问.NET核心本机DI容器:Dependency injection 为什么ServiceProvider无法在ASP.NET Core中创建控制器实例?,dependency-injection,asp.net-core,asp.net-core-mvc,Dependency Injection,Asp.net Core,Asp.net Core Mvc,我在.NETCore中开发集成测试。因为控制器有多个依赖项,所以手动创建它们的实例很不方便。以下是我如何创建ServiceProvider实例以访问.NET核心本机DI容器: HostingEnvironment env = new HostingEnvironment(); env.ContentRootPath = Directory.GetCurrentDirectory(); env.EnvironmentName = "Development"; Startup startup =
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();
启动
类取自测试程序集
令我惊讶的是,服务提供商无法创建控制器的实例。它可以实例化已注册的服务,但不能实例化控制器。有人知道为什么会这样吗
因为控制器有多个依赖项,所以手动创建它们的实例很不方便
事实不应如此。在编写单元测试时,您不应该依赖于DI容器;您应该手动创建依赖项。在容器上添加依赖项只会使测试复杂化,并在库上添加不必要的依赖项
您可以应用一些有趣的模式来简化您的创建。其中之一是模式的一种变体,您在测试类中有一个私有工厂方法来构建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生成器使您的单元测试非常可读和可维护,因为向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());
}
您应该使用
TestServer
,它引导一个完整的测试环境(可以选择使用不同的启动类或环境变量,在这里您可以用内存存储替换数据存储)
ASP.NET内核很好地解决了这个问题
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"));
我建议您使用第一种方法,因为它更为一致,而且正是有环境支持的Startup
类存在的原因
更新
重要的补充。如果使用TestServer
,还可以访问其DI容器(IServiceProvider
实例)
var server=newtestserver(new WebHostBuilder()
.UseStartup()
.UseEnvironment(“集成测试”);
var controller=server.Host.Services.GetService();
编写单元测试时,不应依赖DI容器;您应该手动创建依赖项(请重新阅读问题;)他询问有关整合的问题tests@Tseng:谢谢你指出这一点。您是对的,对于集成测试来说,使用与生产中相同的基础结构(即容器)更为自然。您是否尝试过使用TestServer
而不是自己布线?
services
.AddMvc()
.AddControllersAsServices()
.AddViewComponentsAsServices()
.AddTagHelpersAsServices();
var server = new TestServer(new WebHostBuilder()
.UseStartup<Startup>()
.UseEnvironment("IntegrationTest"));
var controller = server.Host.Services.GetService<MyController>();