Unit testing 我是否只需要在单元测试中模拟外部依赖关系?内部依赖关系是什么?

Unit testing 我是否只需要在单元测试中模拟外部依赖关系?内部依赖关系是什么?,unit-testing,integration-testing,Unit Testing,Integration Testing,我是否只需要在单元测试中模拟外部依赖关系 如果我要测试的方法依赖于同一程序集中的另一个类,该怎么办?我是否必须模拟依赖关系,确保只测试一件事情,然后进行单元测试而不是集成测试 集成测试通常是测试依赖关系的测试,还是必须区分内部依赖关系和外部依赖关系 例如,一个方法有2000行代码,有5个方法调用,所有方法都来自同一个程序集。通常,正确的单元测试只测试这一段代码。因此,在这样一个场景中,您开始问自己这两个类的耦合。类A内部是否依赖于类B的实现?还是只需要提供一个类型B的实例就可以了?注意类和类型之

我是否只需要在单元测试中模拟外部依赖关系

如果我要测试的方法依赖于同一程序集中的另一个类,该怎么办?我是否必须模拟依赖关系,确保只测试一件事情,然后进行单元测试而不是集成测试

集成测试通常是测试依赖关系的测试,还是必须区分内部依赖关系和外部依赖关系


例如,一个方法有2000行代码,有5个方法调用,所有方法都来自同一个程序集。

通常,正确的单元测试只测试这一段代码。因此,在这样一个场景中,您开始问自己这两个类的耦合。类A内部是否依赖于类B的实现?还是只需要提供一个类型B的实例就可以了?注意类和类型之间的区别

如果是后者,那么就模拟它,因为你不是在测试B类,只是测试A类

如果是前者,那么听起来好像创建测试已经确定了一些耦合,甚至可能应该重新考虑

编辑:作为对您评论的回应,我想在执行此操作以及将单元测试重新装配到遗留系统中时需要记住的一件关键事情是,在精神上分离类和类型的概念非常非常困难

单元测试不是针对A类的,而是针对A类的。A类是A类的实现,它将通过或失败测试。类A可能对类型B有内部依赖关系,需要提供它,但类型A可能没有。类型A是一种功能契约,其进一步表现为单元测试

类型A是否在其合同中指定实现将需要类型B的实例?还是类A在内部解析它的实例?类型A是否需要指定这一点,或者类型A的不同实现是否可能不需要类型B的实例

如果类型A需要类型B的实例,那么它应该在外部公开该实例,并且您应该在测试中提供模拟。如果类A在内部解析类型B的实例,那么您可能希望使用IoC容器,在运行测试之前,您可以使用类型B的模拟来引导它


无论哪种方式,类型B都应该是模拟的,而不是实现。这只是打破这种耦合的问题,这在遗留系统中可能很难,也可能不难。此外,业务的投资回报率可能很好,也可能不好。

一般来说,一个有2000行代码的方法是非常糟糕的。当我必须使用pagedown键浏览三到四次以上,可折叠区域不起作用时,我通常会开始寻找创建新类的理由——甚至不是方法,而是类

所以,是的,您确实需要摆脱来自程序集外部和内部的依赖关系,并且您需要考虑类的责任。这听起来像是它肩上有太多的负担,而且它听起来像是非常接近不可能为它编写单元测试。如果您考虑可测试性,您将自动开始注入依赖项,并缩小您的类,然后BAM!!!你有了它;漂亮的代码!!:-

问候,, 莫顿(Morten)

处理您正在描述的代码库并不容易,因为您不知道如何开始更改多个问题。类之间以及问题之间都有很强的依赖性,甚至可能没有整体设计

根据我的经验,做这类工作需要大量的努力、时间和技巧。了解如何使用遗留代码的一个非常好的资源是Michael Feather的书:

简言之,有一些安全的重构可以做,而不必冒破坏的风险,这可能会帮助您开始。还有其他重构需要测试来保护事物的工作方式。重构代码时,测试是必不可少的。当然,这并不能100%保证事情不会破裂,因为可能有太多隐藏的特性和复杂性,你在开始时无法意识到。根据代码库的不同,您需要做的工作量有很大差异,但是对于大型代码库,通常需要做大量的工作

您需要了解代码的功能,或者简单地了解它,或者了解当前代码的功能。在任何一种情况下,您都可以从编写更大的测试开始,这些测试不是真正的单元测试,它们只是保护当前代码。它们可能涵盖更大的部分,更像是集成/功能测试。当您开始重构代码时,这些是您的保护措施。当您有了这样的测试并且对代码所做的事情感到满意时,您可以开始重构大型测试所涵盖的部分。对于您更改的较小部分,请正确书写 单元测试。迭代进行各种重构在某种程度上会使最初的大型测试变得不必要,因为您现在有了更好的代码库和单元测试,或者您只是将它们作为功能测试

现在,回到你的问题上来

我理解你的问题的意思,但我还是想稍微改变一下,因为有比外部和内部更重要的方面。我认为一个更好的问题是,为了得到更好的设计和编写单元测试,我需要打破哪些依赖关系

这个问题的答案是,对于单个单元测试,您应该打破您无法控制的、缓慢的、不确定的或拉入过多状态的所有依赖关系。这些是所有外部文件系统、打印机、网络等。。还要注意,多线程不适用于单元测试,因为这是不确定的。对于内部依赖关系,我假定您是指具有成员或函数调用其他函数的类。答案可能是。你需要决定你是否在控制中,设计是否好。可能在您的情况下,您无法控制,代码也不好,因此在这里,您需要重构一些东西,使其处于控制之下,并进入更好的设计。Michael Feather的书在这里很好,但是您需要找到如何在couse的代码库中应用这些东西

打破依赖关系的一个非常好的技术是。简而言之,它改变了设计,这样您就可以传入类使用的成员,而不是让类本身实例化它们。对于这些,您为传入的依赖项提供了一个接口抽象基类,因此您可以轻松地更改传入的内容。例如,在生产环境中和进行单元测试时,可以使用它为类提供不同的成员实现。这是一项伟大的技术,如果使用得当,也会带来良好的设计


祝你好运,慢慢来

案例2:在我重构之前,我应该!必须创建一个单元测试,因为可能会创建一些新的bug。我指的是紧密耦合的更大的企业系统。我也在问自己怎么做?大卫,类型和类之间没有区别。类是一种类型。也许你的意思是对象和类之间的区别…@BrunoBrant:这是语义上的区别,但语义就是这里的一切。语言意义上的类型是类,但逻辑语言不可知论意义上的类型不一定是同一件事。例如,在C语言中,通过定义功能契约,接口或抽象类更像是一种类型。一个类提供它自己的契约,该契约声称满足类型契约的标准,也可能满足其他契约的标准,甚至更符合它自己的标准,最后的目标是契约的实现。在本例中,他正在测试每个契约,而不考虑类。您的第一个问题是您有一个使用2000 LOCOfc的方法。但这是别人做的。我和我的团队必须成为超级明星,把狗屎变成金子。。。或者说:easy said=当你有几千行代码的方法,这些方法调用的方法也有几千行代码:我的问题是,有一条规则告诉我只有在进行单元测试时才进行重构。但正如您所描述的,为这段胡说八道的代码创建单元测试几乎是不可能的,因为这是绝对不可能的。因此,如果没有单元测试,在重构时可以破坏功能,在这样一个代码噩梦中这样做的几率相对较高。欢迎来到现实世界:是的,现实世界有时真的很糟糕。可悲的是,我不得不承认我也创造了像你描述的那样的怪物,但再也没有了!!!如果先测试后重拍是一种有意义的政策,你一定要尽你所能去做。希望在重构完成后,您可以将此megatest重新用于集成目的,并且您是几十个新的、漂亮的单一责任类的骄傲之父:-