在编写单元测试时,对于与单元交互的每个对象,我将执行以下步骤(从我对JBrains‘集成测试是一个骗局。的理解中窃取):
当我决定重构一个在步骤2中具有模拟响应的对象时,我的问题就出现了。如果我改变了对象对调用的响应方式,那么其他对象对该调用的测试都不会失败,因为它们都是为了匹配旧的样式而被模拟的。如何使模拟与它们正在模拟的对象保持最新状态?有什么最佳做法吗?还是我完全误解了事情,而且做错了?
发布于 2012-03-06 14:51:11
我就是这样做的。
假设我必须更改接口方法foo()的响应。我将存根foo()的所有协作测试收集到一个列表中。我收集方法foo()的所有契约测试,或者如果我没有契约测试,我会在一个列表中收集所有当前foo()实现的测试。
现在我创建了一个版本控制分支,因为它在一段时间内会很混乱。
I @Ignore (JUnit start )或以其他方式禁用存根foo()的协作测试,然后一个一个地重新实现和重新运行它们。我让他们都经过。我可以在不涉及foo()的任何生产实现的情况下做到这一点。
现在,我将一个接一个地实现实现foo()的对象,其预期结果与存根中的新返回值匹配。记住:协作测试中的存根对应于合同测试中的预期结果。
此时,所有协作测试现在都假设来自foo()的新响应,而契约测试/实现测试现在期望来自foo()的新响应,因此所有这些都应该能够正常工作。
现在把你的分支整合起来,给自己倒点酒。
发布于 2012-03-06 06:57:14
修正:这是一种权衡。通过将一个对象从它的环境中隔离出来,来轻松地进行测试,而不是相信当所有的部分聚集在一起时,它都能工作。
发布于 2012-03-05 22:48:40
首先,使用集成测试获得这样的覆盖率肯定会更加困难,所以我认为单元测试仍然是更好的。不过,我觉得你说得有道理。很难让你的对象的行为保持同步。
解决这一问题的一个方法是进行部分集成测试,这些测试具有真正的服务1层深度,但更多的是模拟。例如:
var sut = new SubjectUnderTest(new Service1(Mock.Of<Service1A>(), ...), ...);这解决了保持行为同步的问题,但增加了复杂性,因为您现在必须设置更多的模拟。
您可以使用受歧视的联合在函数式编程语言中解决这个问题。例如:
// discriminated union
type ResponseType
| Success
| Fail of string // takes an argument of type string
// a function
let saveObject x =
if x = "" then
Fail "argument was empty"
else
// do something
Success
let result = saveObject arg
// handle response types
match result with
| Success -> printf "success"
| Fail msg -> printf "Failure: %s" msg您定义了一个名为ResponseType的受歧视的联合,它具有许多可能的状态,其中一些状态可以接受参数和其他元数据。每次访问返回值时,都必须处理可能的各种条件。如果要添加另一种失败类型或成功类型,编译器将在每次不处理新成员时向您发出警告。
这个概念对于处理程序的发展有很大的帮助。C#、Java、Ruby和其他语言使用异常来传递失败条件。但是,这些失败的情况通常并不是“特殊”的情况,这最终导致了你正在处理的情况。
我认为函数式语言最接近于为你的问题提供最好的答案。坦率地说,我认为没有一个完美的答案,甚至在许多语言中没有一个好的答案。但是编译时检查会有很大帮助。
https://stackoverflow.com/questions/9574662
复制相似问题