问题描述
人们如何在 .NET Core 2 应用程序中对他们的 Startup.cs 类进行单元测试?所有功能似乎都是由不可模拟的静态扩展方法提供的?
How do people go about Unit Testing their Startup.cs classes in a .NET Core 2 application? All of the functionality seems to be provided by Static extensions methods which aren't mockable?
如果你以这个 ConfigureServices
方法为例:
If you take this ConfigureServices
method for example:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<BlogContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddMvc();
}
如何编写测试以确保 AddDbContext(...) &调用了 AddMvc(),通过 Extensions 方法实现所有这些功能的选择似乎使其无法测试?
How can I write tests to ensure that AddDbContext(...) & AddMvc() are called, The choice of implementing all of this functionality via Extensions methods seems to have made it untestable?
推荐答案
好吧,如果你想检查扩展方法 AddDbContext
在 services
上被调用的事实,你有麻烦了.好消息是您实际上不应该确切地检查这个事实.
Well yes, if you want to check the fact that extension method AddDbContext
was called on services
you are in trouble.
The good thing is that you shouldn't actually check exactly this fact.
Startup
类是一个应用程序composition root.并且在测试组合根时,您希望检查它是否实际注册了根对象(在 ASP.NET Core 应用程序中为控制器)实例化所需的所有依赖项.
Startup
class is an application composition root. And when testing a composition root you want to check that it actually registers all dependencies required for instantiation of the root objects (controllers in the case of ASP.NET Core application).
假设你有以下控制器:
public class TestController : Controller
{
public TestController(ISomeDependency dependency)
{
}
}
您可以尝试检查 Startup
是否注册了 ISomeDependency
的类型.但是 ISomeDependency
的实现也可能需要您应该检查的其他一些依赖项.最终你会得到一个对不同依赖项进行大量检查的测试,但它实际上并不能保证对象解析不会抛出缺少依赖项异常.这样的测试没有太大的价值.
You could try checking whether Startup
has registered the type for ISomeDependency
. But implementation of ISomeDependency
could also require some other dependencies that you should check.
Eventually you end up with a test that has tons of checks for different dependencies but it does not actually guarantee that object resolution will not throw missing dependency exception. There is not too much value in such a test.
在测试组合根时对我来说效果很好的一种方法是使用真正的依赖注入容器.然后我调用它的组合根并断言根对象的分辨率不会抛出.
An approach that works well for me when testing a composition root is to use real dependency injection container. Then I call a composition root on it and assert that resolution of the root object does not throw.
它不能被视为纯单元测试,因为我们使用其他非存根类.但与其他集成测试不同,此类测试快速且稳定.最重要的是,它们为正确的依赖项注册带来了有效检查的价值.如果此类测试通过,您可以确定该对象也将在产品中正确实例化.
It could not be considered as pure Unit Test because we use other non-stubbed class. But such tests, unlike other integration tests, are fast and stable. And most important they bring the value of valid check for correct dependencies registration. If such test passes you could be sure that object will also be correctly instantiated in the product.
以下是此类测试的示例:
Here is a sample of such test:
[TestMethod]
public void ConfigureServices_RegistersDependenciesCorrectly()
{
// Arrange
// Setting up the stuff required for Configuration.GetConnectionString("DefaultConnection")
Mock<IConfigurationSection> configurationSectionStub = new Mock<IConfigurationSection>();
configurationSectionStub.Setup(x => x["DefaultConnection"]).Returns("TestConnectionString");
Mock<Microsoft.Extensions.Configuration.IConfiguration> configurationStub = new Mock<Microsoft.Extensions.Configuration.IConfiguration>();
configurationStub.Setup(x => x.GetSection("ConnectionStrings")).Returns(configurationSectionStub.Object);
IServiceCollection services = new ServiceCollection();
var target = new Startup(configurationStub.Object);
// Act
target.ConfigureServices(services);
// Mimic internal asp.net core logic.
services.AddTransient<TestController>();
// Assert
var serviceProvider = services.BuildServiceProvider();
var controller = serviceProvider.GetService<TestController>();
Assert.IsNotNull(controller);
}
这篇关于如何在 .NET Core 中对 Startup.cs 进行单元测试的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持跟版网!