2012-06-04 25 views
2

我是单元测试的新手 - 我只使用纯粹的Testmethods(我的最后一个模块,我创建了其中的约50个)完成了基本的断言测试。了解一些单元测试的做法

我正在读单元测试一本书,书中的众多例子中的一个已经创造了我对每个单独测试一个新的类。以下是为一个测试用例创建的示例对象之一。我的问题是有没有必要这样做?或者什么时候应用这种方法,何时不需要?

public class and_saving_an_invalid_item_type : when_working_with_the_item_type_repository 
    { 
     private Exception _result; 

     protected override void Establish_context() 
     { 
      base.Establish_context(); 

      _session.Setup(s => s.Save(null)).Throws(new ArgumentNullException()); 
     } 

     protected override void Because_of() 
     { 
      try 
      { 
       _itemTypeRepository.Save(null); 
      } 
      catch (Exception exception) 
      { 
       _result = exception; 
      } 
     } 

     [Test] 
     public void then_an_argument_null_exception_should_be_raised() 
     { 
      _result.ShouldBeInstanceOfType(typeof(ArgumentNullException)); 
     } 
    } 

回答

3

您是否需要为每个单独的测试创建一个新类?我会说不,你当然不会。我不知道这本书为什么这样说,或者他们只是在帮助说明他们的例子。

为了回答你的问题,我建议每个组的测试中使用一个类...但它确实比这更复杂一点,因为你如何定义“组”是多种多样的,取决于你当时正在做。

在我的经验,一组测试真的逻辑结构类似于一个文件,这可以通过一些常用的方面包含的测试中,分组(和有时嵌套的)一个或多个集合在一起。用于测试面向对象代码的自然分组是按类别分组,然后按方法分组。

下面是一个例子

  • 试验类别1个
    • 试验方法1次
        方法的
      • 初级行为1次
      • 方法的
      • 替代行为1
    • 测试为方法2
      • 方法的主要行为2
      • 方法的替代行为2

不幸的是,在C#或Java(或类似的语言),你只得到了结构的两个水平(而不是你真正想要的3或4),所以你必须破解适合的东西。

做到这一点的常用方法是使用一类组合到一起组测试,并没有任何团体在方法层面,因为像这样的:

class TestsForClass1 { 
    void Test_method1_primary() 
    void Test_method1_alternate() 

    void Test_method2_primary() 
    void Test_method2_alternate() 
} 

如果同时你的方法1方法2都具有相同的安装/拆卸,那么这是好的,但有时他们不这样做,导致这种故障:

class TestsForClass1_method1 { 
    void Test_primary() 
    void Test_alternate() 
} 

class TestsForClass1_method2 { 
    void Test_primary() 
    void Test_alternate() 
} 

如果你有更复杂的需求(假设你有method_1 10次的测试中,前5个具有设置要求X,接下来的5个具有不同的设置要求),那么人们通常最终只是使得越来越多的类名是这样的:

class TestsForClass1_method1_withRequirementX { ... } 
class TestsForClass1_method1_withRequirementY { ... } 

这很烂,但嘿 - 方挂,圆孔等

个人而言,我的粉丝在方法内部使用lambda函数为您提供第三级分组。 NSpec显示了一种方式,可以做到这一点......我们有一个内部测试框架,它是略有不同,它读取有点像这样:

class TestsForClass1 { 
    void TestsForMethod1() { 
     It.Should("perform it's primary function",() => { 
     // .... 
     }); 

     It.Should("perform it's alternate function",() => { 
     // .... 
     }); 
    } 
} 

这有一些缺点(如果第一个它语句失败,别人不跑),但我认为这种折衷值得)


- 问题最初读:“是它曾经真的有必要创建一个对象的每个单独的测试,我想继续出来吗?”。根据这个解释,答案是(主要)是。

通常,单元测试涉及的两个部分

  • 被测对象的相互作用。通常这是你写的一个类或一个函数的实例
  • 环境。通常这是你传递给函数的任何参数,以及对象可能引用的其他依赖关系。

为了使单元测试可靠,这两个部分都需要对每个测试都是“新鲜”的,以确保系统的状态合理可靠。

  • 如果被测的事情是不会刷新为每个测试,然后一个功能可以改变对象的内部状态,并导致下一个测试错误地失败

  • 如果环境不刷新每个测试,那么一个函数可能会改变环境(例如:在外部数据库或其他地方设置一些变量),这可能会导致下一次测试错误地失败。

很显然,许多情况下,这是不是这样的 - 你可能例如有一个纯粹的数学函数只需要整数作为参数,并且不触及任何外部状态,那么你可能不希望麻烦重新创建被测对象或测试环境......但通常,任何面向对象系统中的大多数事情都需要更新,所以这就是为什么这是“标准实践”。

2

我不太能够效仿你的例子,但理想情况下,任何测试用例都应该能够独立于任何其他运行 - 独立于其他任何东西真的。