首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何处理模拟对象中的更改

如何处理模拟对象中的更改
EN

Stack Overflow用户
提问于 2012-03-05 21:57:47
回答 3查看 593关注 0票数 16

在编写单元测试时,对于与单元交互的每个对象,我将执行以下步骤(从我对JBrains‘集成测试是一个骗局。的理解中窃取):

  1. 在单元中编写测试,以确保它向协作对象发送正确的调用和params。
  2. 在单元中编写测试,以确保它处理来自协作对象的所有可能的响应。这些响应都是模拟的,因此对单元进行隔离测试。
  3. 在协作对象中编写测试,以确保它接受调用和params。
  4. 编写测试以确保每个可能的响应都被发送回。

当我决定重构一个在步骤2中具有模拟响应的对象时,我的问题就出现了。如果我改变了对象对调用的响应方式,那么其他对象对该调用的测试都不会失败,因为它们都是为了匹配旧的样式而被模拟的。如何使模拟与它们正在模拟的对象保持最新状态?有什么最佳做法吗?还是我完全误解了事情,而且做错了?

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2012-03-06 14:51:11

我就是这样做的。

假设我必须更改接口方法foo()的响应。我将存根foo()的所有协作测试收集到一个列表中。我收集方法foo()的所有契约测试,或者如果我没有契约测试,我会在一个列表中收集所有当前foo()实现的测试。

现在我创建了一个版本控制分支,因为它在一段时间内会很混乱。

I @Ignore (JUnit start )或以其他方式禁用存根foo()的协作测试,然后一个一个地重新实现和重新运行它们。我让他们都经过。我可以在不涉及foo()的任何生产实现的情况下做到这一点。

现在,我将一个接一个地实现实现foo()的对象,其预期结果与存根中的新返回值匹配。记住:协作测试中的存根对应于合同测试中的预期结果。

此时,所有协作测试现在都假设来自foo()的新响应,而契约测试/实现测试现在期望来自foo()的新响应,因此所有这些都应该能够正常工作。

现在把你的分支整合起来,给自己倒点酒。

票数 4
EN

Stack Overflow用户

发布于 2012-03-06 06:57:14

修正:这是一种权衡。通过将一个对象从它的环境中隔离出来,来轻松地进行测试,而不是相信当所有的部分聚集在一起时,它都能工作。

  1. 目标是稳定的角色:考虑面向客户的角色(而不是一堆方法)。我发现角色(根据客户的需求编写/客户优先/外部输入)不那么易变。检查该角色是否是一个漏洞百出的抽象,暴露了实现细节。也要注意角色的变化-磁铁(并想出一个缓解计划)。
  2. 如果您必须进行更改,请查看是否可以“依赖于编译器”。编译器将很好地标记诸如更改方法签名之类的内容。那就用它吧。
  3. 如果编译器无法帮助您发现更改,请更加勤奋地查看是否有遗漏(客户端使用)。
  4. 最后,您将回到验收测试以捕捉此类问题--确保对象A和协作者B、C、D按照相同的假设(协议)进行操作。如果有东西设法逃过了你的拖网,那么至少有一个测试会发现它的可能性很大。
票数 2
EN

Stack Overflow用户

发布于 2012-03-05 22:48:40

首先,使用集成测试获得这样的覆盖率肯定会更加困难,所以我认为单元测试仍然是更好的。不过,我觉得你说得有道理。很难让你的对象的行为保持同步。

解决这一问题的一个方法是进行部分集成测试,这些测试具有真正的服务1层深度,但更多的是模拟。例如:

代码语言:javascript
复制
var sut = new SubjectUnderTest(new Service1(Mock.Of<Service1A>(), ...), ...);

这解决了保持行为同步的问题,但增加了复杂性,因为您现在必须设置更多的模拟。

您可以使用受歧视的联合在函数式编程语言中解决这个问题。例如:

代码语言:javascript
复制
// 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和其他语言使用异常来传递失败条件。但是,这些失败的情况通常并不是“特殊”的情况,这最终导致了你正在处理的情况。

我认为函数式语言最接近于为你的问题提供最好的答案。坦率地说,我认为没有一个完美的答案,甚至在许多语言中没有一个好的答案。但是编译时检查会有很大帮助。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/9574662

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档