假设我有以下单身人士:
public class ConfigReader
{
static readonly ConfigReader instance = new ConfigReader();
private ConfigReader() { }
public static ConfigReader Instance { get { return instance; } }
public int RecordsPerPage
{
get
{
var configPath = DB.Table<ConfigSource>().GetFieldValue("Path").ToString();
if (configSource == "Database")
return Convert.ToInt32(DB.Table<Configuration>().GetFieldValue("RecordsPerPage"));
else
return Convert.ToInt32(ConfigurationManager.AppSettings["RecordsPerPage"]);
}
}
}
如何编写单元测试来模拟DB.Table&amp; ConfigurationManager.AppSettings在这个单例中调用,以便我可以用单元测试来测试它吗?
实际上这导致了TDD的另一个问题:我们在设计系统时是否真的需要考虑可测试性?似乎并非每个设计都符合TDD原则,如DI等。我是否误解了某些内容?
答案 0 :(得分:2)
在设计系统时我们应该考虑可测试性吗?如果您编写符合SOLID原则的代码,那么您的系统将是可测试的。因此,只需考虑是否需要良好的面向对象设计。
替代解决方案 - 如果您使用的是依赖注入框架,那么您只需关注此类的单一责任 - 读取配置值。第二个责任(保持单个类的实例)将转到DI框架,你可以在这里使用简单的构造函数注入。
测试你的单例类 - 为了模拟类依赖,你应该依赖于抽象而不是实现。您应该通过依赖注入提供实现。这里有几个选项 - 您可以将RecordsPerPage
属性转换为方法并使用参数注入:
public int RecordsPerPage(IConfigSource source)
{
var configPath = source.GetFieldValue("Path").ToString();
if (configPath == "Database")
return Convert.ToInt32(source.GetFieldValue("RecordsPerPage"));
return Convert.ToInt32(ConfigurationManager.AppSettings["RecordsPerPage"]);
}
现在你可以将IConfigSource
的模拟传递给你的单身人士(Moq样本):
int expected = random.Next();
Mock<IConfigSource> sourceMock = new Mock<IConfigSource>();
sourceMock.Setup(s => s.GetFieldValue("Path")).Returns("Database");
sourceMock.Setup(s => s.GetFieldValue("RecordsPerPage")).Returns(expected);
var reader = ConfigReader.Instance;
var actual = reader.RecordsPerPage(sourceMock.Object);
Assert.That(actual, Is.EqualTo(expected));
sourceMock.VerifyAll();
如果你想重用IConfigSource
来调用不同的单例,那么你可以声明属性来设置它(属性注入):
public IConfigSource Source { get; set; }
在测试期间设置源:
var reader = ConfigReader.Instance;
reader.Source = sourceMock.Object;
var actual = reader.RecordsPerPage;