unit testing调用另一种方法的方法
unit testing调用多个方法的方法的最佳方法是什么,例如:
modify(string value) { if(value.Length > 5) replaceit(value); else changeit(value); }
这个伪代码有一个修改方法(当前)调用replaceit()
或changeit()
。 我已经为replaceit
和changeit
编写了testing,所以编写一个新的testing用于修改将是99%的同一组代码。 我需要testing它,因为它将来可能会改变。
那么我复制粘贴现有的testing代码? 将testing代码移动到一个通用的function? 任何其他的想法? 我不确定这里的最佳做法。
这是一个经典的基于状态的testing与基于行为的testing场景。
在这个荒谬的简单的例子testing输出是好的。 但是在某些时候,你会遇到在执行过程中检查状态复杂的testing。 而是要检查行为(例如,validation是否使用特定值调用changeit)。
在这一点上,你可能应该看看像Rhino.Mocks(.Net)或Mockito(Java)的模拟对象框架,并开始编写更多基于接口的代码。
你有很多select。 哪一个最好取决于你的问题不清楚的细节。
- testing
modify
就像它是一个不相关的方法。 优点:它可能在某个时候成为一个。 - 只是testing你是否有权声明。 也就是说,只要testing一下,testing就会迫使你编写你需要的实现(调用
replaceit
和changeit
只是最简单的实现,如果你正在实践TDD,这应该是你自然而然的。覆盖率没有太多的重复努力。 - 子类和覆盖方法 (这是从“使用遗留代码有效地工作”一书中的依赖打破技术):在专门为了testing目的而引入的子类上testing该方法,该方法覆盖replace并用jar装答案进行
changeit
,或者设置感应variables(指示该方法是否被调用了正确的值的variables)。 优点:可能会简化你的testing(有些甚至不能),有时甚至只是使testing成为可能。 - 为
replaceit
和changeit
方法提取一个新类,包括该类的一个接口。 当testingmodify
时,存根或模拟接口。 优点:可能会使您的devise更具可测性, 并可以更好地解耦/重用(或不可重复使用)。
如果您已经独立testingreplaceit()
和changeit()
,那么您唯一要testing的是if条件。 使用几个值来testingmodify()
,以确保它在正确的条件下调用正确的函数(这些条件为null
, Strings
长度为4,5和6,代码示例如下)。
只要testingmodify
。
给定某些值时, Modify
应该返回某些值。
如何修改它的工作并不重要,只是它的工作。
而且,如果将来您将modify
为使用不同的方法(或者不使用方法),则它不会,也不应该,也不会影响您的testing。
这就是说,也testingreplaceit' and
changeit`。
在这种情况下,“testing代码”是什么? 设置和检查结果? 如果是这样的话,我会重构一个不同的方法,并从每个testing中使用它。 我只会在有大量数据的时候这么做 – 只要阅读这个方法的代码,就可以看到testing所做的一切,这样做有一个可读性好处。
复杂的testing方法往往会让我开始,说实话 – 往往不能切实避免,但如果你能简化它们,这是值得的。
按照喜好
- 修改(testing)只有2场景(每个arm的if stmt),所以我会写2个testing修改的forms。
如果预期更换结果(价值)很容易确定..
。
public TestModifyIfValueLength..() { string expectedValue = .. ;// literal result of replaceit(value) Assert.Equals( expectedValue, modify("asd") ); }
- 如果不是,请考虑使用存根(使用子类并覆盖changeit,replaceit)来validation是否调用了正确的方法。
- 如果存根工作太多,那么做模拟的事情。 提取界面并设置更改,replace的期望。
假设
- 你有testingreplaceit(价值)和changeit(价值),testing(如所有边界条件)这两种方法全面。
- replaceit()和changeit()是公共方法。如果不是,你应该考虑只对公共方法编写testing。 您应该可以自由地调整/查找私有方法,而不需要知道testing代码。
那么,不,你的testing代码不会是99%相同的,因为你实际上在这里testing不同的东西,除非replace,改变和修改所有返回相同的值。
不知道为什么困难。 修改方法的testing应该大概四行。 既然你已经在testing底层的function了,你所要做的就是确保这个特定的方法不会中断,编写一个testing来testing这个函数中的两个可能的代码path,返回期望值就足够了。
如果您已经为replaceit()和changeit()编写了testing,则修改testing将根据“value”的值简单地检查是否返回不同的结果。 然而,你只是简单地在testing中重新实现了方法的逻辑,这有点荒谬。
在这种情况下,我不会testing修改,直到它有更复杂的逻辑,或者更好 – 被另一个更重要的testing方法使用。
当testing边界条件如if (value.length > 5)
,应确保testing数据包含值为4
或6
。
你可以用这些方法创build一个func,然后模拟这些funcs。 或者,你可以创build一个虚拟的方法,并使用犀牛嘲笑 – 部分模拟,你可以嘲笑这些虚拟的方法。
你基本上需要2个testing。
1)通过一个像“快速的棕色狐狸跳跃”的string! (长度大于五)确保值受replaceit(...)
2)传入像“Foo”这样的string(长度小于5),并确保该值受changeit(...)
你的testing(伪代码)可能如下所示:
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作为调用发生后的实际值。
和Justin Standard一样, 加上 null
作为值(这显然会失败,因为你给我们的代码片段))unit testing的基本规则是“只testing特定于被测方法的东西”。 有一种不叫另一种方法的方法很不常见。