用单身人士进行单元测试

yey*_*man 46 singleton unit-testing

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

string r_providerName = ConfigurationManager.ConnectionStrings["main_db"].ProviderName;
Run Code Online (Sandbox Code Playgroud)

但是我在这一行中收到了一个例外.我想这是因为ConfigurationManager是一个单例.你怎么能解决单元测试中的单例问题?


谢谢你的回复.所有这些都非常有启发性.

Gre*_*osz 80

看看Google Testing博客:

并且:

最后,Misko Hevery在他的博客上写了一篇指南:编写可测试代码.

  • +1!另见Michael Feathers撰写的"有效使用遗产代码"一书; 他提供了使用Singletons进行测试的技巧.http://www.amazon.com/Working-Effectively-Legacy-Michael-Feathers/dp/0131177052 (2认同)

bni*_*dyc 13

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

public class SingletonDependedClass
{
    private string _ProviderName;

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

    public SingletonDependedClass(string providerName)
    {
        _ProviderName = providerName;
    }
}
Run Code Online (Sandbox Code Playgroud)

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

此外,如果您使用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;
        ...
    }
}
Run Code Online (Sandbox Code Playgroud)

首先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;
    }
}
Run Code Online (Sandbox Code Playgroud)

测试SingletonDependedClass(使用Moq模拟库):

[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);
    ...
}
Run Code Online (Sandbox Code Playgroud)


Teo*_*ahi 9

书中的示例:有效地使用旧版代码

同样在这里给出相同的答案: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) {
    ...
    }
    ...
}
Run Code Online (Sandbox Code Playgroud)

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

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

  • 似乎setUp()方法中缺少某些东西.PermitRepository有一个私有构造函数,所以你不能在那里使用new ... (4认同)

Joh*_*lph 5

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

我已经在解耦设计的背景下对这个问题进行了详细的分析.我会试着总结一下我的观点:

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