中的一些去耦技术。网
中的一些去耦技术。网
原文:https://medium.com/hackernoon/a-couple-of-decoupling-techniques-for-net-17ea7e0f2065
对我来说,遗留代码就是没有测试的代码。
——Michael c . Feathers,作者“有效地使用遗留代码”
我们大多数人都会在某个阶段使用这种类型的遗留代码。尝试添加测试可能非常困难。如果代码是在没有测试的情况下编写的,那么它很可能与系统的其他部分紧密耦合。为了有效地工作并产生健壮的解决方案,我们需要有一些技术来使代码可测试。在阅读了 Roy Osherove 的《单元测试的艺术》之后,我一直在尝试书中推荐的技术。下面,我将介绍我最常用的三种技巧。
示例:去耦报告生成器
这里我们有一个在遗留应用程序中可能遇到的紧密耦合的例子。ReportGenerator用于获取包含用户名、结果和报告生成日期的文本块。
输出将如下所示:
Joe Bloggs
A,B,A,C
Date: 29/03/2017
该类包含 3 个影响我们编写测试能力的紧密耦合。UserService和ResultService都向我们的数据库发出请求,我们必须能够模拟这些请求。对DateTime.Now的调用也会影响我们编写测试的能力,因为每次调用它都会返回不同的字符串。
技巧 1。依赖注入
我们可以通过构造函数传入接口,而不是在我们的GetUserReport方法中构造服务。首先,我们需要为我们的服务提取接口(有点 resharper 的魔力…):
internal interface IUserService {
User GetUser(int userId);
} internal interface IResultService {
string[] GetUserResults(int userId);
}
然后修改ResultGenerator来注入它的依赖项:
现在我们能够模拟我们的服务并测试ResultGenerator。这是实现解耦的一种常见方式,在简明编码指南和关于坚实原则的讨论中经常被推荐。
虽然这解决了我们的耦合问题,但有时会导致大量的工作。如果从许多不同的位置调用我们的结果生成器会怎样?然后,我们必须在使用服务的每个文件中创建服务的实例;突然间,我们从编写一个简单的测试变成了修改大量的文件。此外,如果类中有许多依赖项,构造函数会因为不断增长的参数列表而变得难以使用。这种技术没有帮助我们打破对系统调用DateTime.Now的依赖。
技巧二。可覆盖的属性
为了最小化引入测试对ResultGenerator的影响,我们可能希望避免修改构造函数。通过将我们的服务调用包装在 CLR 属性中并延迟加载实现,我们仍然可以实现提供模拟实现的能力。
使用这种技术,我们可以让大部分代码保持原样,只需在测试代码中覆盖服务实现。
当我们想要限制我们的变更范围时,这个解决方案是更好的。然而,它仍然不能帮助我们解决DateTime问题。
技巧三。受保护的虚拟方法
这是在单元测试的艺术中推荐的一个聪明的方法。它类似于技术 2,但也解决了我们模拟对DateTime的系统调用的需要。如果我们将依赖关系包装在受保护的虚拟方法中,我们可以通过创建覆盖这些虚拟方法的派生TestableResultGenerator来提供模拟:
然后我们在我们的测试代码中创建一个TestableResultGenerator,它从ResultGenerator派生并覆盖可用的虚拟方法:
这个可测试的实现允许我们在测试中提供模拟实现。
这有点有争议,因为我们不是直接访问测试中的代码,而是通过代理。
结论
分离遗留代码可能是一个困难的问题。它需要大量的前期工作,但收效甚微。当面临最后期限的压力时,对管理层来说,这可能是一个特别困难的推销;然而,从长远来看,代码库的质量和可维护性的提高有非常明显的好处。
掌握这些技术将简化脱钩过程。使用这些技术的组合将为您遇到的大多数问题提供优雅的解决方案。