我正在寻找以下单元测试用例的解决方案/模式。
案例:
让我们假设我们有三个类,A,B,C,每个类都有一个方法。 A的方法调用B的方法,该方法调用C的方法。因此,A-> B-> C.每种方法都采用一个输入(方法A的输入A,输入B,输入C)。对方法A的调用的结果输出将是树结构,例如:
Root(从方法A创建) - 节点B(从方法B创建) - 节点C1 - 节点C2(均由方法C创建)
对我来说,单元测试是关于单独测试方法输入的输出。因此,我们将为上述每种方法编写单元测试。因为测试是孤立编写的,所以我们在为方法A编写单元测试时模拟方法B,在为方法B编写单元测试用例时模拟方法C.
到目前为止,一切都很好,我们可以在每个方法的输出上写下期望,以确保结果树结构得到尊重。
问题:
现在让我们添加另一个类,它将调用方法B,以便我们也有以下调用链:D-> B-> C.生成的根树将如下所示:
在开发过程中,有人意识到方法A的要求被误解了,树的结果应该是这样的:
开发人员很乐意更改方法C,以便输出只返回一个节点而不是两个节点。他会改变单元测试,以便反映这些变化。 但是,方法D要求不应该改变,并且该方法的输出应该仍然具有节点C1和节点C2。
问题:
你如何编写你的单元测试,以便第二个开发人员会被告知他会为方法D引入的更改?我宁愿避免在这里看起来最合适的集成测试。
感谢。
答案 0 :(得分:2)
你应该混合使用“pure¹”单元测试,不纯的单元测试,集成(外部资源命中)测试和完整堆栈测试(UI down)。 “纯”单元测试不是测试的结束。
因此,对于您开发的每个功能,您最终会得到一些以下类型的测试:
只要你有一些更高级别的测试,即使测试达到A隔离,它们也应该失败。
¹只是使用短语“纯单元测试”,因为有些人认为单元测试的定义是单独的方法
答案 1 :(得分:1)
理想情况下,您的单元测试应该一直渗透到C类。单元测试并不一定意味着 ONE CLASS 上的 ONE METHOD 。它只是意味着您可以在不需要其他依赖项(如库或数据库等)的情况下运行它。然后您需要进行集成测试。
您的单元测试应该一直渗透到C,当需求更改为A时,单元测试将中断,D的开发人员将知道出现问题。
答案 2 :(得分:0)
如果你想坚持使用隔离单元测试(我对大多数中心对象都这样做,因为我发现几乎不可能用正确的深度编写适量的集成测试并保持快速),一个有趣的方法是Joe Rainsberger的Contract and Collaboration Tests。他提出了一个观点,即除非你有相应的契约测试确保该依赖项的具体实现确实在现实世界中以这种方式运行,否则你不应该模拟依赖项在测试中表现出某种方式。
在您的示例中,说“方法C现在只返回一个节点而不是两个节点”意味着更改C的合同并相应地进行合同测试。这引入了模拟C与新合同不同步的可能性,因此你应该在你的测试中查看C被模拟并在需要时调整模拟。
在仔细检查现有测试之后会让您意识到您并不是真的想要更改方法C的合同,而是引入适合其需要的新方法C2 甲
Rainsberger描述了他用来确保他在测试here中具有互惠性的过程。您还可以在this video中观看该技术。