我是否只需要在单元测试中模拟外部依赖项?
如果我想测试的方法依赖于同一个程序集中的另一个类,该怎么办?我是否必须模拟依赖性以确保只测试一件事并进行单元测试而不是集成测试?
集成测试是一般测试依赖项的测试,还是我必须区分内部和外部依赖项?
一个例子是一个拥有2000行代码和5个方法调用的方法(所有方法来自同一个程序集)。
答案 0 :(得分:5)
通常,正确的单元测试仅测试 单个代码。所以这样的场景就是你开始问自己这两个类的耦合。 A类内部是否依赖于B类的实施?或者它只是需要提供类型B的实例(注意类和类型之间的差异)?
如果是后者,那么嘲笑它,因为你没有测试B类,只是A类。
如果是前者,那么听起来像创建测试已经确定了一些可以(甚至应该)重新考虑的耦合。
编辑(响应您的评论)我认为在执行此操作时要记住的一个关键事项(并且将旧单元测试改进遗留系统真的非常非常困难)是在精神上分开类和类型的概念。
单元测试不适用于A类,它们适用于A类.A类是A类的实现,它将通过或不通过测试。 A类可能对B类具有内部依赖性,需要提供它,但A类可能不会。类型A是功能合同,其单元测试进一步表示。
类型A是否在其合同中指定实现将需要类型B的实例?或者A类是否在内部解析它的实例?类型A 是否需要来指定它,或者类型A的不同实现是否可能不需要类型B的实例?
如果Type A需要B类实例,那么它应该在外部公开它,并且你将在测试中提供mock。如果A类内部解析了B类实例,那么您可能希望使用IoC容器,然后在运行测试之前使用类型B的模拟引导它。
无论哪种方式,类型B 应该是模拟而不是实现。这只是打破这种耦合的问题,在传统系统中可能会或可能不会很难。 (此外,可能会或可能不会为业务带来良好的投资回报率。)
答案 1 :(得分:4)
使用您正在描述的代码库并不容易 多个问题合并到您不知道如何开始更改的内容中。类之间以及问题之间存在很强的依赖关系,甚至可能没有整体设计。
根据我的经验,这需要花费很多精力,时间和技巧来完成这类工作。要了解如何使用遗留代码,非常好的资源是 Michael Feather的书:Working Effectively with Legacy Code 。
简而言之,您可以做一些安全的重构,而不会有破坏事情的风险,这可能会帮助您开始。还有其他重构需要测试以保护工作方式。 重构代码时,测试至关重要。这当然不是100%保证事情不会中断,因为当你开始时可能会有很多隐藏的“功能”和复杂性你无法察觉。根据代码库的不同,您需要做的工作量差异很大,但对于大型代码库,通常会有很多工作。
您需要了解代码的作用,只需了解它或找出当前代码的作用即可。在任何一种情况下,你开始编写“更大”的测试,这些测试不是真正的单元测试,它们只是保护当前的代码。它们可能涵盖更大的部件,更像是集成/功能测试。当你开始重构代码时,这些是你的警卫。如果您已经进行了此类测试并且您对代码的作用感到满意,则可以开始重构“更大”测试所涵盖的部分。对于较小的部件,您可以更改编写适当的单元测试。迭代执行各种重构将在某些时候使初始大型测试变得不必要,因为您现在有了更好的代码库和单元测试(或者您只是将它们作为功能测试)。
现在,回到你的问题。
我明白你对你的问题的意思,但我仍然想稍微改变它,因为有更重要的方面而不是外部和内部。我相信一个更好的问题是问我需要打破哪些依赖项来获得更好的设计并编写单元测试?
这个问题的答案是你应该打破你无法控制的所有依赖关系,缓慢,非确定性或拉入太多状态进行单个单元测试。这些肯定都是外部的(文件系统,打印机,网络等)。另请注意,多线程不适合单元测试,因为这不是确定性的。对于内部依赖项,我假设您指的是具有调用其他函数的成员或函数的类。答案可能就是这样。你需要决定你是否掌控,以及设计是否良好。可能在您的情况下,您无法控制并且代码不好,所以在这里您需要重构事物以控制事物并进入更好的设计。 Michael Feather的书很棒,但你需要找到如何在你的代码库中应用这些东西。
打破依赖关系的一个非常好的技术是dependency injection 。简而言之,它改变了设计,以便传入一个类使用的成员,而不是让类本身实例化它们。对于这些,您有一个接口(抽象基类)用于传入的这些依赖项,因此您可以轻松更改传入的内容。例如,使用此方法,您可以为生产中的类和单元测试时使用不同的成员实现。这是一项伟大的技术,如果明智地使用,也会带来良好的设计。
祝你好运,慢慢来! ;)答案 2 :(得分:1)
一般来说,使用2000行代码的方法很简单。我通常会开始寻找创建新类的理由 - 甚至不是方法,而是类 - 当我必须使用pagedown键超过三到四次来浏览它时(并且可折叠区域不计算)。
所以,是的,你确实需要摆脱程序集外部和内部的依赖关系,你需要考虑类的责任。听起来这个人的肩膀太重了,听起来很难写出单元测试。如果您认为可测试性,您将自动开始注入依赖项,并缩小您的类,并且BAM !!!你有它;漂亮而漂亮的代码!! : - )
此致 的Morten