用单身人士进行单元测试

时间:2010-01-18 12:27:07

标签: unit-testing singleton

我已经使用Visual Studio Team Edition测试框架准备了一些自动测试。我希望其中一个测试按照在程序中完成的正常方式连接到数据库:

string r_providerName = ConfigurationManager.ConnectionStrings["main_db"].ProviderName;

但我在这一行收到一个例外。我想这是因为ConfigurationManager是一个单例。你如何解决单元测试中的单例问题?


感谢您的回复。所有这些都非常有启发性。

4 个答案:

答案 0 :(得分:76)

答案 1 :(得分:13)

您可以使用构造函数依赖注入。例如:

public class SingletonDependedClass
{
    private string _ProviderName;

    public SingletonDependedClass()
        : this(ConfigurationManager.ConnectionStrings["main_db"].ProviderName)
    {
    }

    public SingletonDependedClass(string providerName)
    {
        _ProviderName = providerName;
    }
}

允许您在测试期间将连接字符串直接传递给对象。

此外,如果您使用Visual Studio Team Edition测试框架,您可以使用参数private创建构造函数,并通过访问器测试该类。

实际上我用嘲笑来解决这类问题。例如:

你有一个依赖单身的课程:

public class Singleton
{
    public virtual string SomeProperty { get; set; }

    private static Singleton _Instance;
    public static Singleton Insatnce
    {
        get
        {
            if (_Instance == null)
            {
                _Instance = new Singleton();
            }

            return _Instance;
        }
    }

    protected Singleton()
    {
    }
}

public class SingletonDependedClass
{
    public void SomeMethod()
    {
        ...
        string str = Singleton.Insatnce.SomeProperty;
        ...
    }
}

首先需要重构SingletonDependedClass以将Singleton实例作为构造函数参数:

public class SingletonDependedClass
{    
    private Singleton _SingletonInstance;

    public SingletonDependedClass()
        : this(Singleton.Insatnce)
    {
    }

    private SingletonDependedClass(Singleton singletonInstance)
    {
        _SingletonInstance = singletonInstance;
    }

    public void SomeMethod()
    {
        string str = _SingletonInstance.SomeProperty;
    }
}

使用SingletonDependedClassMoq mocking library的测试):

[TestMethod()]
public void SomeMethodTest()
{
    var singletonMock = new Mock<Singleton>();
    singletonMock.Setup(s => s.SomeProperty).Returns("some test data");
    var target = new SingletonDependedClass_Accessor(singletonMock.Object);
    ...
}

答案 2 :(得分:9)

图书中的示例Working Effectively with Legacy Code

此处也给出了相同的答案: https://stackoverflow.com/a/28613595/929902

要在测试工具中运行包含单例的代码,我们必须放松单例属性。这是我们如何做到的。第一步是向singleton类添加一个新的静态方法。该方法允许我们替换单例中的静态实例。我们称之为 的 setTestingInstance 即可。

public class PermitRepository
{
    private static PermitRepository instance = null;
    private PermitRepository() {}
    public static void setTestingInstance(PermitRepository newInstance)
    {
        instance = newInstance;
    }
    public static PermitRepository getInstance()
    {
        if (instance == null) {
            instance = new PermitRepository();
        }
        return instance;
    }
    public Permit findAssociatedPermit(PermitNotice notice) {
    ...
    }
    ...
}

现在我们有了这个setter,我们可以创建一个a的测试实例 PermitRepository并设置它。我们想在我们的测试设置中编写这样的代码:

public void setUp() {
    PermitRepository repository = PermitRepository.getInstance();
    ...
    // add permits to the repository here
    ...
    PermitRepository.setTestingInstance(repository);
}

答案 3 :(得分:5)

这里面临着一个更普遍的问题。如果误用,单身人士会阻碍测试。

我在解耦设计的背景下完成了detailed analysis这个问题。我会试着总结一下我的观点:

  1. 如果您的Singleton具有重要的全局状态,请不要使用Singleton。这包括持久存储,如数据库,文件等。
  2. 如果类名称对Singleton Object的依赖性不明显,则应该注入依赖项。将Singleton实例注入类的需要证明了模式的错误使用(参见第1点)。
  3. 假设Singleton的生命周期与应用程序的生命周期相同。大多数Singleton实现都使用延迟加载机制来实例化自己。这是微不足道的,他们的生命周期不太可能改变,否则你不应该使用Singleton。