Built-in Fake Objects

The following text is a partial translation of the original English article, performed by ChatGPT (gpt-3.5-turbo) and this Jekyll plugin:

虽然模拟对象是进行单元测试的完美工具,但通过模拟框架进行模拟可能会使您的单元测试变得难以维护的混乱。正因为如此,我们经常听到“模拟是不好的”和“模拟是邪恶的”。

这种复杂性的根本原因是我们的对象太大了。它们有许多方法,而这些方法返回其他对象,这些对象也有方法。当我们将这样一个对象的模拟版本作为参数传递时,我们应该确保它的所有方法都返回有效的对象。

这导致了不可避免的复杂性,使得单元测试几乎不可能维护 浪费

jcabi-dynamo 中的 Region 接口为例(本文中的此代码段及其他代码段都经过简化,以便简洁明了)。

它的 table() 方法返回一个 Table 接口的实例,该接口有自己的方法:

Frame 接口是 frame() 方法返回的对象,它也有自己的方法。等等。为了创建一个正确模拟的 Region 接口实例,通常需要创建十几个其他的模拟对象。使用 Mockito,代码如下:

而所有这些只是实际测试之前的搭建工作。

假设你正在开发一个项目,该项目使用jcabi-dynamo来管理DynamoDB中的数据。你的类可能类似于这样:

你可以想象使用Mockito来对这个类进行单元测试会有多么困难。首先,我们必须模拟Region接口。然后,我们必须模拟一个Table接口,并确保它被table()方法返回。然后,我们还必须模拟一个Frame接口,等等。

这个单元测试的长度将比这个类本身要长得多。除此之外,它的真正目的,即测试员工工资的获取,对于读者来说并不明显。

此外,当我们需要测试类似类的类似方法时,我们必须从头开始重新模拟。再次编写多行代码,这些代码看起来与我们已经编写的代码非常相似。

解决方案是创建虚假类并将它们与真实类一起打包。这就是jcabi-dynamo正在做的。只需查看其JavaDoc。有一个名为com.jcabi.dynamo.mock的包,其中只包含虚假类,仅适用于单元测试。

尽管它们的唯一目的是优化单元测试,但我们将它们与生产代码一起打包,放在同一个JAR包中。

当使用虚假类MkRegion时,测试将如下所示:

这个测试对我来说很明显。首先,我们创建一个虚拟的DynamoDB区域,它使用H2Data存储(内存中的H2数据库)。该存储将准备好一个名为employees的表,具有哈希键name和一个单一的salary属性。

然后,我们向表中插入一条记录,哈希为Jeff,薪水为50000

最后,我们创建一个Employee类的实例,并检查它如何从DynamoDB中获取薪水。

我目前在我使用的几乎每个开源库中都在做同样的事情。我正在创建一组虚拟类,以简化库内部和用户的测试。

顺便说一句,有一篇非常好的关于同一主题的文章:tl;dw: 停止模拟,开始测试 by Ned Batchelder。

附注:看看这个,关于非常相似的主题:在Java中模拟HTTP服务器。

Translated by ChatGPT gpt-3.5-turbo/42 on 2023-11-18 at 05:31

sixnines availability badge   GitHub stars