如何组织单元测试,不要让重构成为一场噩梦?

Nik*_*sev 12 c# tdd nunit unit-testing code-organization

我目前组织单元测试的方法归结为以下几点:

  • 每个项目都有自己的专用项目和单元测试.对于项目BusinessLayer,有一个BusinessLayer.UnitTests测试项目.
  • 对于我想要测试的每个类,测试项目中有一个单独的测试类放在完全相同的文件夹结构中,并且与测试中的类完全相同.对于一类CustomerRepository命名空间下BusinessLayer.Repositories,有一个测试类CustomerRepositoryTests的命名空间BusinessLayerUnitTests.Repositories.

每个测试类中的方法遵循简单的命名约定MethodName_Condition_ExpectedOutcome.因此,类CustomerRepositoryTests包含一类测试CustomerRepositoryGet定义方法如下所示:

[TestFixture]
public class CustomerRepositoryTests
{
    [Test]
    public void Get_WhenX_ThenRecordIsReturned()
    {
        // ...
    }

    [Test]
    public void Get_WhenY_ThenExceptionIsThrown()
    {
        // ...
    }
}
Run Code Online (Sandbox Code Playgroud)

这种方法对我很有用,因为它使得对某些代码的定位测试非常简单.在相反的网站上,它使代码重构变得非常困难,应该是:

  • 当我决定将一个项目分成多个较小的项目时,我还需要拆分我的测试项目.
  • 当我想要更改类的名称空间时,我必须记住更改测试类的名称空间(和文件夹结构).
  • 当我更改方法的名称时,我必须完成所有测试并在那里更改名称.当然,我可以使用搜索和替换,但这不是很可靠.最后,我仍然需要手动检查更改.

有没有组织单元测试仍然让我找到测试特定的代码迅速的一些巧妙的方式,并在同一时间更借本身对重构?

或者,是否有一些,呃,也许是Visual Studio扩展,这将允许我以某种方式说"嘿,这些测试是针对方法的,所以当方法的名称发生变化时,请非常友好并改变测试" ?说实话,我正在认真考虑写自己的东西:)

use*_*736 4

经过大量测试后,我开始意识到(至少对我来说)所有这些限制从长远来看会带来很多问题,而不是好事。因此,我们不再使用“名称”和约定来确定这一点,而是开始使用代码。每个项目和每个类可以有任意数量的测试项目和测试类。所有测试代码都是根据从功能角度测试的内容(或者它实现的需求,或者它重现的错误等)来组织的。然后为了找到一段代码的测试,我们这样做:

[TestFixture]
public class MyFunctionalityTests
{
    public IEnumerable<Type> TestedClasses()
    {
        // We can find the tests for a class, because the test cases references in some special method.
        return new []{typeof(SomeTestedType), typeof(OtherTestedType)};
    }

    [Test]
    public void TestRequirement23423432()
    {
        // ... test code.
        this.TestingMethod(someObject.methodBeingTested); //We do something similar for methods if we want to track which methods are being tested (we usually don't)
        // ... 
    }
}
Run Code Online (Sandbox Code Playgroud)

我们可以使用 resharper“用法”之类的工具来查找测试用例等...当这还不够时,我们可以通过反射和 LINQ 来加载所有测试类,并运行类似的东西,allTestClasses.where(testClass => testClass.TestedClasses().FindSomeTestClasses()); 您也可以使用 TearDown收集有关每个方法/类测试哪些方法的信息并执行相同的操作。