c# - 如何使单例设计可测试?

标签 c# unit-testing singleton

假设我有以下单例:

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 和 ConfigurationManager.AppSettings 调用,以便我可以使用单元测试对其进行测试?

其实这引出了TDD的另一个问题:我们在设计系统的时候真的有必要考虑可测试性吗?似乎不是每个设计都能符合 TDD 原则,比如 DI 等。我是不是误解了什么?

最佳答案

在设计系统的时候要不要考虑可测试性?如果您编写的代码符合 SOLID原则,那么你的系统将是可测试的。因此,只需考虑您是否需要良好的面向对象设计。

替代解决方案 - 如果您正在使用依赖注入(inject)框架,那么您可以只专注于此类的单一职责 - 读取配置值。第二个职责(保持类的单个实例)将交给 DI 框架,您将能够在这里使用简单的构造函数注入(inject)。

测试你的单例类 - 为了模拟类依赖,你应该依赖抽象而不是实现。并且您应该通过依赖注入(inject)提供实现。这里有几个选项 - 您可以将 RecordsPerPage 属性转换为方法并使用参数注入(inject):

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 的 mock 传递给您的单例(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,那么你可以声明属性来设置它(属性注入(inject)):

public IConfigSource Source { get; set; }

并在测试期间设置源代码:

var reader = ConfigReader.Instance;
reader.Source = sourceMock.Object;
var actual = reader.RecordsPerPage;

关于c# - 如何使单例设计可测试?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20425833/

相关文章:

javascript - 基于浏览器的测试工具哪个不坏?

c# - 将 Moq 设置为在调用方法时增加值

java - 让接口(interface)提供实现它的类的实例

c# - .NET 2.0 的类 TPL 库

c# - 使用类层次结构重载 - 大多数派生未使用

java - Java 8 LocalDate 的 Hamcrest 匹配器

java - Scala 的单例在序列化方面有哪些保证?

c# 如何修复 foreach ,当我尝试 console.write 列表中的元素 <string>

c# - 在 C# 中根据 XSD 模式验证 json 数据

java - 如何扩展 Singleton 类