如何在.NET Core中对Startup.cs进行单元测试 [英] How to Unit Test Startup.cs in .NET Core
问题描述
人们如何着手对.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
方法为例:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<BlogContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddMvc();
}
如何编写测试以确保AddDbContext(...)&会调用AddMvc(),通过扩展方法实现所有这些功能的选择似乎使其无法测试了吗?
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?
推荐答案
是的,如果您想检查在services
上调用了扩展方法AddDbContext
的事实,您会遇到麻烦.
好消息是您实际上不应检查这个事实.
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
类是应用程序组合根.而且,在测试组合根时,您需要检查它是否实际注册了实例化根对象所需的所有依赖项(对于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进行单元测试的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!