重构项目测试规范_重构以允许测试双打

重构项目测试规范

有时,当您创建一个类时,它会直接实例化一个对象以在其方法中使用。 例如:

public void myFunc()
{
   MyType object = new MyType();
   object.doSomething();
   this.thingy = object.getSomething();
}

通常认为这是不好的,因为您将类与实例化的类紧密结合在一起。 “但是,”您说,“我知道我永远不会使用任何其他类,并且我当然不希望该类的用户提供替代使用。” 这些顾虑大部分是有效的,但它们却忽略了一件非常重要的事情要注意:测试。

出于所有通常的原因,您应该能够使用测试double来替换该对象(通常将其错误地称为模拟; mock是double的特定类型)。 但是,按原样,您无法替换它。 这样做将允许班上的所有用户也这样做,不是吗?

如果我告诉过你也可以吃蛋糕怎么办? (这样一个奇怪的短语……)好吧,我会为您提供一个简要的解释,然后详细介绍。

请记住,它不需要任何类型的注释或DI框架(我不是坚信“魔术”可以做我应该做的事;我对做这些的人没有什么反对,但是我只是反对”不喜欢这样做)。

果壳

假设您要进行测试的典型方法是将它们与要测试的程序包放在相同的程序包中,但是放在不同的目录中,则实际上可以非常轻松地进行设置,以用测试倍数替换“不可更改”的对象。 您所需要做的就是使用package-private (默认访问)方式为类提供double值,并使用private方式访问该类。 然后,您可以在测试中使用package-private调用为类提供测试倍数。

有几种方法可以完成其中的每个部分,因此我将对其进行深入研究。

供应商

我可以想到三种不同的方式来告诉被测对象使用哪种双重方式,事实证明,它们与进行典型依赖注入的三种基本方式完全相同:

  1. 建设者
  2. 塞特犬
  3. 领域

您可能会注意到,我使用数字而不是要点。 这是因为我认为有些优于其他。 您应该按照它们出现的顺序考虑每种方法。

构造函数注入

在您的类上创建一个package-private构造函数,该构造函数接受正常的参数以及测试的两倍。 然后,您现有的构造函数和/或静态工厂方法可以使用默认对象委托给该构造函数。

这通常是最佳选择,尤其是对于不可变类型。 通过将其传递给构造函数,该对象将不需要更改,不可变类型不应该这样做。 当然,在以后传递它不会破坏类的有效不变性,因为在生产代码中不可能进行您要应用的更改,但是它消除了我在本文结尾处提到的“很棒的附带好处”。

使用构造函数还可以减少测试所需的行数,因为这是您已经要键入的行。

构造函数注入的唯一真正的缺点是,它不允许在不创建新对象的情况下更改测试倍数的功能。 不过,这很少有问题,因为每个测试通常不需要多个,并且每个测试都应创建一个新实例以确保测试独立性。

二传手注射

创建一个package-private setter方法,测试可以调用该方法以将字段设置为测试double。 构造函数将字段设置为默认值或null(访问测试双精度的技术可以测试null并使用默认值)。

这不是一个可怕的选择,但是如上所述,对于不可变的类,绝对不是理想的选择。 最好的部分是它的明显性。 看到设置器被调用,可以使测试倍数在此处的操作更加清晰,但通常不会更加清晰。 如上一节所述,它的另一个好处是无需创建此类的新实例即可更改double的功能。 再说一遍,这确实不是那么有用。

场注入

这是通过将存储double package-private的字段设置package-private并将其直接分配给double来完成的。

出于与未公开字段相同的原因,您几乎不应该这样做。 您可能会认为这样做是无害的,因为它仅用于测试,但在本文结尾处却消除了“令人敬畏的附带好处”。

从技术上讲,这是一个可行的选项,但这是我什至不愿意将其包括在文章中的唯一原因。 这是糟糕的设计; 不惜一切代价避免它。 请。

存取器

访问器几乎总是private 。 这通常是因为我们甚至在开始重构代码以使用测试倍数之前都不想要它们。 我只能想到两种访问器,它们是供应商的镜像。 由于您无法创建构造函数的镜像,因此只有两个:

  1. 吸气剂
  2. 领域

同样,两者处于首选顺序,尽管我偏爱一个偏爱另一个之间的差异要小得多。

吸气剂访问

创建一个private getter方法,该方法返回测试double或默认对象,任何最初使用默认对象的代码都可以调用该代码来获取其对象。

实话实说,我发现使用吸气剂比直接进行现场访问几乎没有好处。 它提供的最大帮助是,您将字段设置为null表示使用默认值时,它本身通常不是最佳设计,因为null检查比直接访问要慢。 有时可能并非如此,这在实例和工厂部分中提到。

现场访问

仅在需要使用double或默认值时使用private字段。 真的很简单。

之所以可以这样做,是因为访问主要是因为它仅在类中发生,并且允许使用其自己的实现详细信息。

过载包装

该技术忽略了使用package-private供应商和private访问者的想法,并且以非常不同的方式工作,甚至不需要在类中添加字段。 如果只有一个方法使用需要两倍的对象,则此功能特别有用(尽管这种情况可能表明您的类正在破坏SRP)。

该技术涉及使用要接受依赖关系作为参数的package-private方法重载要测试的方法。 然后,将所有逻辑从原始逻辑移到重载,将锁定的依赖关系替换为参数传递的逻辑。 然后,原始方法仅调用新的重载并传入原始对象。 例如,您可以将其

public void myFunc()
{
   MyType object = new MyType();
   object.doSomething();
   this.thingy = object.getSomething();
}

进入这个

public void myFunc()
{
   MyType object = new MyType();
   myFunc(object);
}

void myFunc(MyType object)
{
   object.doSomething();
   this.thingy = object.getSomething();
}

现在,您可以通过测试双打并隔离myFunc()的功能。

实例和工厂

这样重构时,您需要注意的一件事是:需要多长时间执行一次新实例。 如果每次运行功能时都需要依赖对象的新实例,则所有供应商都需要传入工厂来创建对象,而不是对象的实例。 使用重载包装,通常可以在单个实例中传递,除非单个方法本身制作了该对象的多个副本。

如果您还没有阅读它,我早些时候写了一篇文章,关于用Java和Python创建简单的函数工厂

测试默认值

不要忘记:您仍然需要测试它是否也可以与默认实例一起使用。 如果运行缓慢,我会将其放入您的集成测试中。

令人失望的副作用

您的测试应该是有关如何使用类的实时文档。 不幸的是,使用这些技术会教给读者一些实际上并不可用的方法。 也许,也许,您应该考虑不要对内部使用的是哪种特定类那么固执?

这完全取决于您; 我只是提供免责声明(和提示:))。

很棒的附带好处吗?

这样的重构的一个非常酷的副作用是,如果您改变主意并希望允许代码用户注入他们自己的依赖版本,那么您要做的就是设置供应商并将其重载给public

实际上,您可以阅读整篇文章,并将每个“ package-private ”实例替换为“ public ”,以使其成为有关提供依赖项注入的一般文章。

翻译自: https://www.javacodegeeks.com/2015/02/refactoring-allow-test-doubles.html

重构项目测试规范

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值