使用NUnit的Sequential属性是一个有效的策略来实现每次测试一次检查吗?

时间:2009-06-24 11:55:38

标签: c# unit-testing tdd nunit

假设我有几个小测试用例。设置简单,易于执行 假设每个小测试用例可以执行多次检查。

当我考虑到一个测试用例应理想检查一个条件这一事实时,我认为有几个选项可以实现这样的要求。

  1. 创建一个包含所有测试用例设置和执行部分的基类。后裔类每个都对一个要求进行验证。 (解决方案1)
  2. 将所有内容保持在一个类中,为每个需求编写验证方法,并让每个验证方法调用共享的设置/练习例程。 (解决方案2)
  3. (ab)使用NUnit的Sequential属性为一个条件实现一个测试。 (解决方案3)
  4. 由于我不太喜欢解决方案1或2,因为它需要您查找测试的设置和练习部分,您是否会在这种情况下使用NUnit的Sequential属性进行分类是一种气味或解决方案。


    原文:多项检查/测试方法

    [Test]
    public void MyTest1()
    {
        // Setup
        IMyObject object1 = new MyObject(/* Parameter combination X */)
        this.myObjectList.Add(object1);
        // Exercise
        result = this.LPSolve(this.myObjectList);
        // Check
        Assert.AreSame(null, result.Object);
        Assert.AreEqual(-1, result.Number);
        Assert.True(result.Messages.Contains(ErrorMessage1));
    }
    
    [Test]
    public void MyTest2()
    {
        // Setup
        IMyObject object1 = new MyObject(/* Parameter combination Y */)
        IMyObject object2 = new MyObject(/* Parameter combination Y */)
        this.myObjectList.Add(object1);
        this.myObjectList.Add(object2);
        // Exercise
        result = this.LPSolve(this.myObjectList);
        // Check
        Assert.AreSame(object2, result.Object);
        Assert.AreEqual(8, result.Number);
        Assert.True(result.Messages.Contains(ErrorMessage1));
        Assert.True(result.Messages.Contains(ErrorMessage2));
    }
    

    解决方案1:具有设置和延迟代码的基类

    [Test]
    public virtual void MyTest1()
    {
        // Setup
        IMyObject object1 = new MyObject(/* Parameter combination X */)
        this.myObjectList.Add(object1);
        // Exercise
        result = this.LPSolve(this.myObjectList);
        // Check
        MyTest1Check();
    }
    
    [Test]
    public virtual void MyTest2()
    {
        // Setup
        IMyObject object1 = new MyObject(/* Parameter combination Y */)
        IMyObject object2 = new MyObject(/* Parameter combination Y */)
        this.myObjectList.Add(object1);
        this.myObjectList.Add(object2);
        // Exercise
        result = this.LPSolve(this.myObjectList);
        // Check
        MyTest2Check();
    }
    ...
    /* Class ObjectCheck */
    /* Check result object */
    public override MyTest1Check()
    {
       Assert.AreSame(null, this.result.Object);        
    }
    
    public override MyTest2Check()
    {
       Assert.AreSame(this.myObjectList[1], this.result.Object);        
    }
    ...
    /* Class NumberCheck */
    /* Check result number */
    public override MyTest1Check()
    {
       Assert.AreSame(-1, this.result.Number);        
    }
    
    public override MyTest2Check()
    {
       Assert.AreSame(8, this.result.Number);        
    }
    ...
    /* and so on */
    

    解决方案2:调用设置/练习方法的测试方法

    public void SetupMyTest1()
    {
        // Setup
        IMyObject object1 = new MyObject(/* Parameter combination X */)
        this.myObjectList.Add(object1);
        // Exercise
        this.result = this.LPSolve(this.myObjectList);
    }
    
    [Test]
    public void MyTest1ObjectShouldBeNull()
    {
        SetupMyTest1();
        // Check
        Assert.AreSame(null, this.result.Object);
    }
    
    [Test]
    public void MyTest1ResultShouldBeMinus1()
    {
        SetupMyTest1();
        // Check
        Assert.AreEqual(-1, this.result.Number);
    }
    
    [Test]
    public void MyTest1MessageShouldContainErrorMessage1()
    {
        SetupMyTest1();
        // Check
        Assert.True(this.result.Messages.Contains(ErrorMessage1));
    }
    
    public void SetupMyTest2()
    {
        // Setup
        IMyObject object1 = new MyObject(/* Parameter combination Y */)
        IMyObject object2 = new MyObject(/* Parameter combination Y */)
        this.myObjectList.Add(object1);
        this.myObjectList.Add(object2);
        // Exercise
        this.result = this.LPSolve(this.myObjectList);
    }
    
    [Test]
    public void MyTest2ObjectShouldBeObject2()
    {
        SetupMyTest2();
        // Check
        Assert.AreSame(this.myObjectList[1], this.result.Object);
    }
    
    [Test]
    public void MyTest2ResultShouldBeEight()
    {
        SetupMyTest2();
        // Check
        Assert.AreEqual(8, this.result.Number);
    }
    
    [Test]
    public void MyTest2MessageShouldContainErrorMessage1()
    {
        SetupMyTest2();
        // Check
        Assert.True(this.result.Messages.Contains(ErrorMessage1));
    }
    
    [Test]
    public void MyTest2MessageShouldContainErrorMessage2()
    {
        SetupMyTest2();
        // Check
        Assert.True(this.result.Messages.Contains(ErrorMessage2));
    }
    

    解决方案3:使用属性将设置/执行/检查保存在一个地方并执行一次检查/测试(两种方法分别执行3和4次)

    [Test, Sequential]
    public void MyTest1([Values("CheckObject", "CheckNumber", "CheckErrorMessage1") string check)
    {
        // Setup
        IMyObject object1 = new MyObject(/* Parameter combination X */)
        this.myObjectList.Add(object1);
        // Exercise
        result = this.LPSolve(this.myObjectList);
        // Check
        if (check == "CheckObject") Assert.AreSame(null, result.Object);
        else if (check = "CheckNumber") Assert.AreEqual(-1, result.Number);
        else if (check = "CheckErrorMessage1") Assert.True(result.Messages.Contains(ErrorMessage1));
        else throw new ArgumentOutOfRangeException(check);
    }
    
    [Test, Sequential]
    public void MyTest2([Values("CheckObject", "CheckNumber", "CheckErrorMessage1", "CheckErrorMessage2") string check)
    {
        // Setup
        IMyObject object1 = new MyObject(/* Parameter combination Y */)
        IMyObject object2 = new MyObject(/* Parameter combination Y */)
        this.myObjectList.Add(object1);
        this.myObjectList.Add(object2);
        // Exercise
        result = this.LPSolve(this.myObjectList);
        // Check
        if (check == "CheckObject") Assert.AreSame(object2, result.Object);
        else if (check = "CheckNumber") Assert.AreEqual(8, result.Number);
        else if (check = "CheckErrorMessage1") Assert.True(result.Messages.Contains(ErrorMessage1));
        else if (check = "CheckErrorMessage2") Assert.True(result.Messages.Contains(ErrorMessage2));
        else throw new ArgumentOutOfRangeException(check);
    }
    

1 个答案:

答案 0 :(得分:1)

在我看来,您原来的两个单元测试很容易理解,可以单独使用。在给定的单元测试中,您要检查一个“单元”功能,这不一定在单个断言中表示。

在原始代码中,查看MyTest1()。在我看来,您正在使用无效参数并返回验证错误。我通常将单元测试命名为LPSolveReturnsErrorWithInvalidParameters()。这定义了一个要测试的单元。要证明此功能已完成,必须满足三个输出要求

  1. Object属性必须为null
  2. Number属性设置为-1
  3. Messages集合包含相应的错误信息
  4. 在单个单元测试中使用这三个断言似乎是合理的。您提供的其他潜在解决方案似乎过于复杂和不必要