单元测试中的依赖注入,xUnit
Dependency injection in unit testing, xUnit
这可能是一个愚蠢的问题,但我正经历其中的一天。
我有一个名为 JwtManager
的 class 和一个名为 IJwtManager
的接口,我正在尝试将接口注入单元测试 class 以便我可以测试它但它不起作用,编译器给我这条消息:The following constructor parameters did not have matching fixture data: IJwtManager jwtManager
.
我认为问题在于编译器不知道 class 应该将接口映射到什么,但是我如何在测试 class 中编写 services.AddScoped<>()
的等价物?
测试代码示例class:
public class JwtManagerTests
{
private readonly IJwtManager jwtManager;
public JwtManagerTests(IJwtManager jwtManager)
{
this.jwtManager = jwtManager;
}
[Fact]
public void Test_Something()
{
// Test here
}
}
将 DI 容器与单元测试项目一起使用是可能的,但确实有点矫枉过正。它迫使您在测试之间共享依赖关系,这使得调整依赖关系的行为以适应特定测试变得更加困难,因为您最终会破坏其他测试。
相反,我们应该努力在我们的测试方法中定义被测系统的依赖关系。您提到您的 JwtManager class 依赖于 IConfiguration。没问题。
[Fact]
public void JwtManagerShouldCreateJwtWhenDetailsAreValid()
{
// Arrange
var configuration = new ConfigurationBuilder().AddInMemoryCollection(
new Dictionary<string, string>
{
{ "section:key1", "value1" },
{ "section:key2", "value2"}
}).Build();
var jwtManager = new JwtManager(configuration);
// Act
var result = jwtManager.CreateJwt("some input");
// Assert
result.Should().BeAValidJwt();
}
这样做的一个好处是您不必向配置对象添加任何配置,除了测试需要的配置。如果 JwtManager 的不同测试需要不同的配置设置来测试不同的场景,那现在很容易做到,因为配置不是来自单个 IoC 容器。
不过,我要提醒您不要直接依赖 classes 中的 IConfiguration。依赖强类型 class 通常更好。它使依赖关系更加清晰(您不必打开 JwtManager 来查看它会使用 IConfiguration 中的哪些设置)并且语法在测试中更清晰。例如:
public class JwtManagerSettings
{
public string SomeSetting { get; set; }
public int SomeOtherSetting { get; set; }
}
public class JwtManager
{
readonly JwtManagerSettings _settings;
public JwtManager(JwtManagerSettings settings)
{
_settings = settings;
}
}
[Fact]
public void JwtManagerShouldCreateJwtWhenDetailsAreValid()
{
// Arrange
var settings = new JwtManagerSettings
{
SomeSetting = "abc",
SomeOtherSettings = 123
};
var jwtManager = new JwtManager(settings);
// Act
var result = jwtManager.CreateJwt("some input");
// Assert
result.Should().BeAValidJwt();
}
在您的实际应用中,Microsoft.Extensions.Configuration 完全能够从 IConfiguration 中解析 JwtManagerSettings 等复杂类型。无论您在哪里配置 IoC 容器(例如 Startup.cs),您都可以这样做:
//assuming your settings are defined in a JSON key called "JwtManager"
var jwtManagerSettings = configuration.Get<JwtManagerSettings>("JwtManager");
services.AddSingleton(jwtManagerSettings);
{
"JwtManager": {
"SomeSetting": "abc",
"SomeOtherSetting": 123
}
}
这可能是一个愚蠢的问题,但我正经历其中的一天。
我有一个名为 JwtManager
的 class 和一个名为 IJwtManager
的接口,我正在尝试将接口注入单元测试 class 以便我可以测试它但它不起作用,编译器给我这条消息:The following constructor parameters did not have matching fixture data: IJwtManager jwtManager
.
我认为问题在于编译器不知道 class 应该将接口映射到什么,但是我如何在测试 class 中编写 services.AddScoped<>()
的等价物?
测试代码示例class:
public class JwtManagerTests
{
private readonly IJwtManager jwtManager;
public JwtManagerTests(IJwtManager jwtManager)
{
this.jwtManager = jwtManager;
}
[Fact]
public void Test_Something()
{
// Test here
}
}
将 DI 容器与单元测试项目一起使用是可能的,但确实有点矫枉过正。它迫使您在测试之间共享依赖关系,这使得调整依赖关系的行为以适应特定测试变得更加困难,因为您最终会破坏其他测试。
相反,我们应该努力在我们的测试方法中定义被测系统的依赖关系。您提到您的 JwtManager class 依赖于 IConfiguration。没问题。
[Fact]
public void JwtManagerShouldCreateJwtWhenDetailsAreValid()
{
// Arrange
var configuration = new ConfigurationBuilder().AddInMemoryCollection(
new Dictionary<string, string>
{
{ "section:key1", "value1" },
{ "section:key2", "value2"}
}).Build();
var jwtManager = new JwtManager(configuration);
// Act
var result = jwtManager.CreateJwt("some input");
// Assert
result.Should().BeAValidJwt();
}
这样做的一个好处是您不必向配置对象添加任何配置,除了测试需要的配置。如果 JwtManager 的不同测试需要不同的配置设置来测试不同的场景,那现在很容易做到,因为配置不是来自单个 IoC 容器。
不过,我要提醒您不要直接依赖 classes 中的 IConfiguration。依赖强类型 class 通常更好。它使依赖关系更加清晰(您不必打开 JwtManager 来查看它会使用 IConfiguration 中的哪些设置)并且语法在测试中更清晰。例如:
public class JwtManagerSettings
{
public string SomeSetting { get; set; }
public int SomeOtherSetting { get; set; }
}
public class JwtManager
{
readonly JwtManagerSettings _settings;
public JwtManager(JwtManagerSettings settings)
{
_settings = settings;
}
}
[Fact]
public void JwtManagerShouldCreateJwtWhenDetailsAreValid()
{
// Arrange
var settings = new JwtManagerSettings
{
SomeSetting = "abc",
SomeOtherSettings = 123
};
var jwtManager = new JwtManager(settings);
// Act
var result = jwtManager.CreateJwt("some input");
// Assert
result.Should().BeAValidJwt();
}
在您的实际应用中,Microsoft.Extensions.Configuration 完全能够从 IConfiguration 中解析 JwtManagerSettings 等复杂类型。无论您在哪里配置 IoC 容器(例如 Startup.cs),您都可以这样做:
//assuming your settings are defined in a JSON key called "JwtManager"
var jwtManagerSettings = configuration.Get<JwtManagerSettings>("JwtManager");
services.AddSingleton(jwtManagerSettings);
{
"JwtManager": {
"SomeSetting": "abc",
"SomeOtherSetting": 123
}
}