模拟自定义配置 class
Mocking custom Configuration class
我有一个自定义 class 用于读取我的 Azure Function App v2 中的配置值:
public class Config
{
public string Key1 { get; set; }
public string Key2 { get; set; }
public Config()
{
this.Key1 = Environment.GetEnvironmentVariable("abc");
this.Key2 = Environment.GetEnvironmentVariable("xyz");
}
}
我已经在 Startup.cs
class 的 Configure
方法中注册了,如下所示:
builder.Services.AddSingleton((s) =>
{
return new Config();
});
现在,当我尝试模拟此 Config.cs
class 并为其键设置值时,它会抛出错误:
var mockConfiguration = new Mock<Config>();
mockConfiguration.Setup(m => m.Key1).Returns("value");
我使用 XUnit
作为测试框架,MOQ
用于模拟。如果我不想为 class 创建接口,我还能如何模拟我的配置 class?
这源于最初的设计问题。
Config
class 与实现问题紧密耦合
Environment.GetEnvironmentVariable
在单独测试时不存在并导致异常。
正如评论中准确建议的那样,您应该利用配置模块并注册您的配置,而不是与 Environment
class.
紧密耦合
参考: Configure simple options with a delegate
builder.Services.Configure<Config>(options => {
options.Key1 = Environment.GetEnvironmentVariable("abc");
options.Key2 = Environment.GetEnvironmentVariable("xyz");
});
这意味着 class 可以简化为基本的 POCO
public class Config {
public string Key1 { get; set; }
public string Key2 { get; set; }
}
并将 IOptions<Config>
显式注入主题函数。
private readonly Config config;
//ctor
public MyFunction(IOptions<Config> options) {
config = options.Value;
//...
}
但是,如果您不想将函数紧密耦合到 IOptions<>
接口,则可以通过与最初所做的类似的额外注册来解决这个问题。注册您的类型并解析选项以在工厂委托中提取其值。
builder.Services.Configure<Config>(options => {
options.Key1 = Environment.GetEnvironmentVariable("abc");
options.Key2 = Environment.GetEnvironmentVariable("xyz");
});
builder.Services.AddSingleton((s) => {
return s.GetRequiredService<IOptions<Config>>().Value;
});
这将允许 Config
class 显式注入主题函数,而不需要简单 POCO 可以执行的接口。
//ctor
public MyFunction(Config config) {
//...
}
从而允许单独测试功能,而不会因实施问题而产生不必要的副作用
//Arrange
var mockConfiguration = new Config() {
Key1 = "value"
};
var subject = new MyFunction(mockConfiguration);
//...
我有一个自定义 class 用于读取我的 Azure Function App v2 中的配置值:
public class Config
{
public string Key1 { get; set; }
public string Key2 { get; set; }
public Config()
{
this.Key1 = Environment.GetEnvironmentVariable("abc");
this.Key2 = Environment.GetEnvironmentVariable("xyz");
}
}
我已经在 Startup.cs
class 的 Configure
方法中注册了,如下所示:
builder.Services.AddSingleton((s) =>
{
return new Config();
});
现在,当我尝试模拟此 Config.cs
class 并为其键设置值时,它会抛出错误:
var mockConfiguration = new Mock<Config>();
mockConfiguration.Setup(m => m.Key1).Returns("value");
我使用 XUnit
作为测试框架,MOQ
用于模拟。如果我不想为 class 创建接口,我还能如何模拟我的配置 class?
这源于最初的设计问题。
Config
class 与实现问题紧密耦合
Environment.GetEnvironmentVariable
在单独测试时不存在并导致异常。
正如评论中准确建议的那样,您应该利用配置模块并注册您的配置,而不是与 Environment
class.
参考: Configure simple options with a delegate
builder.Services.Configure<Config>(options => {
options.Key1 = Environment.GetEnvironmentVariable("abc");
options.Key2 = Environment.GetEnvironmentVariable("xyz");
});
这意味着 class 可以简化为基本的 POCO
public class Config {
public string Key1 { get; set; }
public string Key2 { get; set; }
}
并将 IOptions<Config>
显式注入主题函数。
private readonly Config config;
//ctor
public MyFunction(IOptions<Config> options) {
config = options.Value;
//...
}
但是,如果您不想将函数紧密耦合到 IOptions<>
接口,则可以通过与最初所做的类似的额外注册来解决这个问题。注册您的类型并解析选项以在工厂委托中提取其值。
builder.Services.Configure<Config>(options => {
options.Key1 = Environment.GetEnvironmentVariable("abc");
options.Key2 = Environment.GetEnvironmentVariable("xyz");
});
builder.Services.AddSingleton((s) => {
return s.GetRequiredService<IOptions<Config>>().Value;
});
这将允许 Config
class 显式注入主题函数,而不需要简单 POCO 可以执行的接口。
//ctor
public MyFunction(Config config) {
//...
}
从而允许单独测试功能,而不会因实施问题而产生不必要的副作用
//Arrange
var mockConfiguration = new Config() {
Key1 = "value"
};
var subject = new MyFunction(mockConfiguration);
//...