2009-02-28 35 views
1

我想更好地使用NUnit来测试我编写的应用程序,但我经常发现,我编写的单元测试可以直接链接到环境或底层数据库开发机器。NUnit测试应用程序,而不是环境或数据库

让我举个例子。

我正在写一个类,它有一个单一的责任来回顾一个字符串,该字符串已被另一个应用程序存储在注册表中。密钥存储在HKCU \ Software \ CustomApplication \ IniPath中。

测试我最终写作看起来像这样;

[Test] 
public void GetIniDir() 
{ 
    RegistryReader r = new RegistryReader(); 
    Assert.AreEqual(@"C:\Programfiles\CustomApplication\SomeDir", r.IniDir); 
} 

但这里的问题是,字符串@ “C:\ PROGRAMFILES \ CustomApplication \ SomeDir” 实际上只是正确的现在。明天它可能会变成@“C:\ Anotherdir \ SomeDir”,并突然间断了我的单元测试,即使代码没有改变。

当我创建一个对数据库执行CRUD操作的类时,也会出现此问题。数据库中的数据可能会一直改变,这又会导致测试失败。所以即使我的班级做了它打算做的事情,它也会失败,因为数据库会返回我最初编写测试时所拥有的更多客户。

[Test] 
public void GetAllCustomersCount() 
{ 
    DAL d = new DAL(); 
    Assert.AreEqual(249, d.GetCustomerCount()); 
} 

你们有没有写测试的提示,它不依赖于周围的环境呢?

回答

6

这个问题的解决方案是众所周知的:mocking。将代码重构为接口,然后开发假类以实现这些接口或使用嘲笑框架来嘲笑它们,例如RhinoMocks,easyMock,Moq等。人。使用假或模拟类允许您定义接口为您的测试返回的内容,而无需实际与外部实体(如数据库)交互。

有关通过SO模拟的更多信息,请尝试以下Google搜索:http://www.google.com/search?q=mock+site:stackoverflow.com。您可能还会对以下定义感兴趣:What's the difference between faking, mocking, and stubbing?

此外,良好的开发实践(如依赖注入(如@Patrik建议),允许将类与其依赖关系解耦,以及避免静态对象,这让单元测试变得更加困难,这将有助于您的测试。使用TDD实践 - 首先开发测试 - 将帮助您自然地开发融合了这些设计原则的应用程序。

0

其他方法是为测试创建单独的数据库。

+1

但是,你不是真的单元测试,你是集成测试。 – 2009-02-28 16:10:59

2

最简单的方法是使用依赖注入来显式依赖。例如,你的第一个例子依赖于注册表,通过传递一个IRegistry(你将定义的一个接口)实例来显式地明确这个依赖关系,然后只使用这个传入的依赖关系从注册表中读取。通过这种方式,您可以在测试中传入IRegistry存根时始终返回已知值,而在生产中您使用的是实际从注册表中读取的实现。

public interface IRegistry 
{ 
    string GetCurrentUserValue(string key); 
} 

public class RegistryReader 
{ 
    public RegistryReader(IRegistry registry) 
    { 
     ... 
     // make the dependency explicit in the constructor. 
    } 
} 
[TestFixture] 
public class RegistryReaderTests 
{ 
    [Test] 
    public void Foo_test() 
    { 
     var stub = new StubRegistry(); 
     stub.ReturnValue = "known value"; 

     RegistryReader testedReader = new RegistryReader(stub); 

     // test here... 
    } 

    public class StubRegistry 
     : IRegistry 
    { 
     public string ReturnValue; 

     public string GetCurrentUserValue(string key) 
     { 
      return ReturnValue; 
     } 
    } 
} 

在这个快速示例中,我使用手动存根,当然,您可以使用任何模拟框架。

0

您应该了解控制原理的反转以及如何使用依赖注入技术 - 这真的可以帮助您编写可测试的代码。

就你而言,你应该有一个界面 - 例如IIniDirProvider - 这是由RegistryBasedIniDirProvider实现的,它可以提供基于注册表中特定键的初始目录。

然后,当其他一些类需要查找的初始目录,其他类应具有以下构造函数:

public SomeOtherClass(IIniDirProvider iniDirProvider) 
{ 
    this.iniDirProvider = iniDirProvider; 
} 

- 允许你在一个模拟IIniDirProvider传递,当你需要进行单元测试SomeOtherClass 。这样你的单元测试就不会依赖注册表中的任何东西。

相关问题