单元测试我认为如何将逻辑错误指向正在测试的类

本文关键字:测试 错误 单元测试 | 更新日期: 2023-09-27 18:22:14

我正在阅读《单元测试的艺术》一书,并试图理解基于状态的测试逻辑。在一个主题的例子中,有一个类似的计算器类

public class Calculator
{
    private int sum=0;
    public void Add(int number)
    {
      sum+=number;
    }
    public int Sum()
    {
      int temp = sum;
      sum = 0;
      return temp;
    }
 }

这本书展示了我们如何将其测试为:

[TestFixture]
public class CalculatorTests
{
    private Calculator calc;
    [SetUp]
    public void Setup()
    {
        calc = new Calculator();
    }
    [Test]
    public void Sum_NoAddCalls_DefaultsToZero()
    {
        int lastSum = calc.Sum();
        Assert.AreEqual(0,lastSum);
    }
    [Test]
    public void Add_CalledOnce_SavesNumberForSum()
    {
        calc.Add(1);
        int lastSum = calc.Sum();
        Assert.AreEqual(1,lastSum);
    }
    [Test]
    public void Sum_AfterCall_ResetsToZero()
    {
        calc.Add(1);
        calc.Sum();
        int lastSum = calc.Sum();
        Assert.AreEqual(0, lastSum);
    }
}

所以在这之前,一切都很好,但是,比方说,我正在写一个计算器类和那个类一样多,并且我制作了类似的方法

public int Sum()
{
  return sum;
}


测试类,如

 [TestFixture]
public class CalculatorTests
{
    private Calculator calc;
    [SetUp]
    public void Setup()
    {
        calc = new Calculator();
    }
    [Test]
    public void Sum_NoAddCalls_DefaultsToZero()
    {
        int lastSum = calc.Sum();
        Assert.AreEqual(0,lastSum);
    }
    [Test]
    public void Add_CalledOnce_SavesNumberForSum()
    {
        calc.Add(1);
        int lastSum = calc.Sum();
        Assert.AreEqual(1,lastSum);
    }
}

比方说,我没有很好地意识到,当我写代码时,当我为此写单元测试时,我是如何发现下面的错误的?因为错误是在2加法之后总和不会为零,就像下面的过程一样

add(1)
add(23)
sum()  is 24 now
add(11)
add(12)
sum()  => will be 47 but it has to be 23.

那么,当我写单元测试的时候,我是怎么想得到那个逻辑错误的呢。(如果我写它,NUnit会告诉我有一个错误)然后我回来,会看到要点,我会改变计算器类,比如

public int Sum() 
{ 
  int temp = sum; 
  sum = 0; 
  return temp; 
}


我希望你能理解我想说的话
谢谢

单元测试我认为如何将逻辑错误指向正在测试的类

基本上你不能确定地找到所有的边缘情况。但是,您可以指定代码要做什么,并编写干净的代码。如果计算器在要求求和后应该重置其和,那么这是应该进行测试的"规范"的一部分,这是某人发明的"要求",所以写测试应该很容易记住。

更困难的是通过编码的方式创建的所有边缘情况。我过去常常参加编码面试,为候选人编写代码单元测试。我以为我有一套很好的测试来证明某些东西有效。但我很快发现,人们可以通过引入难以测试的边缘案例的方式来编写代码(比如某个东西在第9次失败时,它做了一些似乎每次都应该有效的事情)。因此,主要的是,如果你遵循TDD的建议,编写测试,编写代码以使其通过,重构以使代码干净,你就不会错得太多。

记住,这不是一个神奇的子弹,这不是一些神奇的公式,可以让你写完美的代码。你仍然需要思考,思考你在做什么。

听起来你基本上已经有了一个测试用例:

[Test]
public void CallingSumResets()
{
    var calc = new Calculator();
    calc.Add(10);
    Assert.AreEqual(10, calc.Sum());
    Assert.AreEqual(0, calc.Sum());
}

它实际执行添加的测试将在其他测试中完成——这只是测试在您第一次调用Sum后,它会重置内部状态。

此测试应失败:

   [Test]
    public void Sum_AfterCall_ResetsToZero()
    {
        calc.Add(1);
        calc.Sum();
        int lastSum = calc.Sum();
        Assert.AreEqual(0, lastSum);
    }

直到您更改代码以在调用Sum()后重置总和。但是,我更愿意创建一个单独的方法Clear(),而不是在getter中重置总和。

TDD步骤

  1. 想想你想让计算器做什么
  2. 为它写一个测试
  3. 写入通过测试的代码

如果我理解正确,下面的示例代码就是您的实现,并且有一个bug;它不会像正确的实现那样将和值重置为零,从而产生错误。你的问题是,如何为此编写单元测试?

public int Sum()
{
  return sum;
}

假设我正确解释了你的问题,你应该简单地编写一个测试,在第二次调用时检测值是否为零:

add(11)
add(12)
sum() => ignore result
sum() => Should be zero