【问题标题】:Is there any advantage to writing both classist and mockist unit tests for a single method?为单个方法编写 classist 和 mockist 单元测试有什么好处吗?
【发布时间】:2011-12-29 10:15:38
【问题描述】:
可以按照http://martinfowler.com/articles/mocksArentStubs.html 以古典或模拟方式编写单元测试
由于对状态和行为都进行了测试,为单个方法编写 classist 和 mockist 单元测试会增加代码的健壮性吗?
我的同事们似乎一直在嘲笑,因为他们是“榜样”,除非我有充分的理由不这样做,否则假定我也会嘲笑。 (我是单元测试的新手)。但是,我觉得只测试 mockist 方式假设了未测试私有方法的实现的正确性,这就是为什么我也想要 classist 测试(间接测试私有方法)。
还是浪费时间?
【问题讨论】:
标签:
c#
unit-testing
mocking
【解决方案1】:
私有方法只是类的内部工作。换句话说,如果您完全测试公共方法然后是私有方法,但是根据定义,完全按照他们需要做的事情去做,因为只有公共行为很重要。
关于“状态”,我有两个想法。
1) 如果状态是内部的(私有的),那么它是如何实现行为的实现。这是一个内部的“秘密”。如果它很重要,则测试产生的行为。
2) 如果状态是公开的……没问题。
我会去嘲笑。
【解决方案2】:
使用 Mocks 进行测试确实也间接测试私有方法 - 任何私有方法都应该在调用堆栈中包含一些公共方法。如果您的公共方法实现了 100% 的代码覆盖率,那么您的所有私有方法都将被调用。
正如我从 Fowler 的文章中记得的那样,不同之处在于 mockist 测试类的内部工作 - 他们验证您的类是否按预期调用其他类 API。如果你不能正确使用它,你的类的功能会在哪里受到影响是有道理的 - 例如。如果您不将某些数据写入数据库,或者更糟糕的是:写入错误的数据。
【解决方案3】:
mock 或存根通常不用于测试私有成员,因为这些成员是封装的核心,任何人都不应依赖于新版本程序集中私有成员的存在。如果您确实需要对私有成员进行单元测试,请考虑阅读How do you unit test private methods?。
使用存根代替模拟的一个很好的理由是显而易见的,即当您需要在特定操作之后验证对象的状态时,如果它适合您应该使用的任务。有些人错误地认为模拟是新一代的单元测试,不应使用过时的存根。事实上,您可以有效地单独或组合使用这两种技术。
【解决方案4】:
是的。使用这两种策略将确保被测代码的健壮性,因为您正在测试代码的依赖关系(mockist)和模拟生产环境的功能(状态)之间的代码契约。通过混合这两种方法,您可以保证平衡的测试方法。
但是请注意,基于状态的测试通常需要更多开销,因为您必须为与被测对象相关的所有组件设置和配置环境。这通常会导致脆弱的测试。将一小部分基于状态的测试用于模拟测试就足够了。
使用 mockist 策略促进了单一职责原则,其中每个类都有一组有限的职责,并且依赖于其他类来承担其职责。根据我的经验,如果对象之间的职责定义不明确,您会发现自己陷入基于状态的测试,这表明存在封装或抽象问题。
【解决方案5】:
Will writing both classist and mockist unit tests for a single method increase robustness of the code since both state and behaviour is tested?
没有。测试一个东西两次是重复。
上次我检查时,这些方法并不是排他性的。而是它们是互补的。根据个别测试的具体情况,您可以选择最佳方法。
- 如果您正在处理一个类的行为,其中依赖项对测试友好,请使用基于状态的测试。
- 如果您要处理对文件系统/网络的依赖,除了检查交互之外别无选择。我是否调用了 SaveFile()?然而,这不是在任何两个相互调用的类之间引入接口的免费通行证。尽管这是对“mockist”方法的普遍批评,但事实是,真正的“mockist”非常努力地发现最小的健壮角色/接口并指定绝对最小值(期望值)。货物崇拜者定义了所有声称它是正确方式的事物之间的接口;角色是不稳定的变化磁铁,测试有太多的期望导致脆弱的测试。每次有接口变化,都要进行大量的测试维护。