我已经使用Visual Studio Team Edition测试框架准备了一些自动测试。我希望其中一个测试按照在程序中完成的正常方式连接到数据库:
string r_providerName = ConfigurationManager.ConnectionStrings["main_db"].ProviderName;
但我在这一行收到一个例外。我想这是因为ConfigurationManager是一个单例。你如何解决单元测试中的单例问题?
感谢您的回复。所有这些都非常有启发性。
答案 0 :(得分:76)
还有:
最后,Misko Hevery在他的博客上写了一个指南:Writing Testable Code。
答案 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;
}
}
使用SingletonDependedClass
(Moq 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这个问题。我会试着总结一下我的观点: