单元测试调用另一个方法的方法

时间:2008-12-12 17:48:46

标签: unit-testing testing

对调用多种方法的方法进行单元测试的最佳方法是什么,例如:

modify(string value)
{
    if(value.Length > 5)  replaceit(value);

    else changeit(value);
}

此伪代码具有(当前)调用replaceit()changeit()的修改方法。我已经为replaceitchangeit编写了测试,因此编写一个新的修改测试将是同一组代码的99%。我需要对它进行测试,因为它可能在将来发生变化。

那么我是否复制粘贴现有的测试代码?将测试代码移动到一个通用功能?还有其他想法吗?我不确定这里的最佳做法。

12 个答案:

答案 0 :(得分:35)

这是一种经典的基于状态的测试与基于行为的测试场景。

在这个荒谬简单的例子中测试输出很好。但是,在某些时候,您将遇到测试,在执行后检查状态很复杂。而是要检查行为(例如,验证是否使用特定值调用changeit)。

此时你可能应该研究像Rhino.Mocks(.Net)或Mockito(Java)这样的模拟对象框架,并开始编写更多基于接口的代码。

答案 1 :(得分:15)

如果您已经独立测试了replaceit()changeit(),那么您唯一要测试的就是if条件。使用一些值测试modify()以确保它在正确的条件下调用正确的函数(对于示例代码,这些条件为长度为4,5和6的nullStrings给)。

答案 2 :(得分:15)

您有很多选择。哪一个最好取决于您的问题中不清楚的细节。

  • 测试modify就好像它是一种无关的方法。优势:它可能在某个时候成为一个。
  • 只是测试你是否正确的if语句。也就是说,测试足以让测试强制您编写所需的实现(调用replaceitchangeit只是最简单的实现,可能有效。如果你的正在练习TDD,这应该是你自然而然的。优点:高测试覆盖率,没有多少重复工作。
  • 子类和覆盖方法(这是“使用传统代码有效工作”一书中的依赖性破坏技术):在您仅为测试目的而引入的子类上测试方法,该子类覆盖{ {1}}和replaceit带有固定答案,或者设置感知变量(指示方法是否已使用正确的值调用的变量)。优点:可能会简化您的测试(或不测试),有时甚至只是使测试成为可能。
  • changeitreplaceit方法提取新类,包括该类的接口。测试changeit时接口的Stub或Mock。优点:可能会使您的设计更易于测试一般(或不)可以更好地解耦/重复使用。

答案 3 :(得分:6)

只需测试modify

在给定特定值时,

Modify应该返回某些值。

修改

如果将来更改modify以使用不同的方法(或没有方法),它不会,也不应该,也不会影响您的测试。

那就是说,还要测试replaceit' and changeit`。

答案 4 :(得分:5)

按优先顺序

  1. 修改(测试)只有2个场景(if stmt的每个分支),所以我会写2个测试来修改表格。
    如果预期的替换结果(值)很容易确定..
  2. public TestModifyIfValueLength..()
        {
          string expectedValue = .. ;// literal result of replaceit(value)
          Assert.Equals( expectedValue, modify("asd") );
        }
    
    1. 如果没有,请考虑使用存根(使用子类并覆盖changeit,replaceit)来验证是否调用了正确的方法。
    2. 如果存根工作太多,请做模拟的事情。提取界面并设置对changeit,replacementit的期望。
    3. 假设

      • 您对replaceit(value)和changeit(value)进行了测试,全面测试这两种方法(例如所有边界条件)。
      • replaceit()和changeit()是公共方法。如果没有,您应该考虑仅针对公共方法编写测试。你应该可以在没有测试代码知道的情况下自由调整/删除私有方法。

答案 5 :(得分:4)

在这种情况下,“测试代码”是什么?设置和检查结果?如果是这样,我会将它重构为一个不同的方法,并在每个测试中使用它。我只会这样做,但是如果有大量的话 - 通过阅读该方法的代码,能够看到测试所做的一切都有可读性好处。

复杂的测试方法常常让我感到困扰,老实说 - 通常情况下它们无法避免,但如果可以简化它们,那么值得这样做。

答案 6 :(得分:2)

嗯,不,你的测试代码不会是99%相同,因为你实际上在这里测试不同的东西,除非replaceit,changeit和modify都返回相同的值。

不确定为什么难度。修改方法的测试应该大约四行。由于您已经在测试基础功能,并且您要做的就是确保此特定方法不会中断,编写测试此函数中两个可能的代码路径的测试返回预期值应该足够了。

答案 7 :(得分:2)

如果你已经为replaceit()和changeit()编写了测试,那么修改测试只会检查是否返回了不同的结果,具体取决于'value'的值。但是,您只需在测试中重新实现该方法的逻辑,这有点荒谬。

在这种情况下,我不会测试修改,直到它有更复杂的逻辑,或更好 - 由另一个更重要的测试方法使用。

答案 8 :(得分:2)

你基本上需要2次测试。

1)传递像“快速布朗福克斯跳跃!”这样的字符串。 (长度大于5)确保该值受replaceit(...)

影响

2)传入像“Foo”这样的字符串(长度小于5)并确保该值受changeit(...)

的影响

您的测试(伪代码)可能如下所示:

testLongValue() {
    string testValue = "A value longer than 5 chars";
    string expected = "Replaced!";
    string actual = modify(testValue);
    assertEqual(expected, actual);
}

testShortValue() {
    string testValue = "len4";
    string expected = "Changed!";
    string actual = modify(testValue);
    assertEqual(expected, actual);
}

显然,如果我知道replacecit()和changeit()应该做什么,我可以给你一个更现实的例子,但这应该给你一个想法。如果它改变原始值引用而不是返回它,则可以在调用发生后使用testValue作为实际值。

答案 9 :(得分:2)

在测试if (value.length > 5)等边界条件时,您应确保测试数据包含长度为value45的{​​{1}}值。

答案 10 :(得分:2)

您可以从方法中创建一个func并模拟这些func。或者,您可以创建虚拟方法并使用Rhino模拟 - 部分模拟,您可以模拟这些虚拟方法。

答案 11 :(得分:0)

与Justin Standard相同, plus 传递null作为值(对于您提供给我们的代码段显然会失败;)) 单元测试的基本规则是“仅测试特定于测试方法的内容”。有一种不会调用另一种方法的方法非常罕见。