ObjectMother 设计模式

Korea Data Forum Fosters Collaboration and Growth
Post Reply
samiul51
Posts: 65
Joined: Wed Dec 11, 2024 10:18 am

ObjectMother 设计模式

Post by samiul51 »

我们应该如何测试代码中的随机性?

假设我们有一个PersonGenerator必须generateRandom生成随机Person实例。

我们首先 卡塔尔电话号码库 编写以下内容:

类 PersonGeneratorShould {

私有 PersonGenerator 生成器 = 新的 PersonGenerator();

@测试
void generateValidPerson() {
人 person = 生成器.generateRandom();
断言(人)。
}
}
然后我们应该问自己:

Image

我在这里想证明什么?这个功能需要做什么?
我是否应该验证生成的人是一个非空实例?
我需要证明它是随机的吗?
生成的实例是否必须遵循一些业务规则?
我们可以使用依赖注入来简化我们的测试。

公共接口 RandomGenerator {
字符串生成随机字符串();
int 生成随机整数();
}
现在PersonGenerator有另一个构造函数,它也接受该接口的实例。默认情况下,它JavaRandomGenerator使用生成随机值的实现java.Random。

然而,在测试中,我们可以编写另一个更可预测的实现。

@测试
void generateValidPerson() {
RandomGenerator randomGenerator = new PredictableGenerator("John Doe", 20);
PersonGenerator 生成器 = 新的 PersonGenerator (randomGenerator);
人 person = 生成器.generateRandom();
断言(person).isEqualTo(新Person("John Doe", 20));
}
此测试证明PersonGenerator生成了 所指定的随机实例,RandomGenerator而无需涉及 的任何细节RandomGenerator。

测试JavaRandomGenerator并没有真正增加任何价值,因为它只是 的简单包装器java.Random。通过测试它,您实际上是java.Random在测试 Java 标准库。编写明显的测试只会导致额外的维护,而几乎没有任何好处。

为了避免为了测试目的而编写实现,例如PredictableGenerator,您应该使用模拟库,例如 Mockito。

当我们编写时PredictableGenerator,我们实际上是RandomGenerator手动对类进行存根。 您也可以使用 Mockito 对它进行存根:

@测试
void generateValidPerson() {
随机生成器 randomGenerator = mock(RandomGenerator.class);
当(randomGenerator.generateRandomString())时,然后返回(“John Doe”);
当(randomGenerator.generateRandomInteger())时,返回(20);

PersonGenerator 生成器 = 新的 PersonGenerator (randomGenerator);
人 person = 生成器.generateRandom();
断言(person).isEqualTo(新Person("John Doe", 20));
}
这种编写测试的方式更具表现力,并且减少了特定测试的实现。

Mockito 是一个用于编写模拟和存根的 Java 库。它在测试依赖于您无法轻松实例化的外部库的代码时非常有用。它允许您为这些类编写行为,而无需直接实现它们。

当我们有多个类似于我们习惯的测试时,Mockito 还允许使用另一种语法来创建和注入模拟以减少样板:

@ExtendWith(MockitoExtension.class)// 1
类 PersonGeneratorShould {

@Mock // 2
随机生成器 随机生成器;

@InjectMocks // 3
私人 PersonGenerator 生成器;

@测试
void generateValidPerson() {
当(randomGenerator.generateRandomString())时,然后返回(“John Doe”);
当(randomGenerator.generateRandomInteger())时,返回(20);

人 person = 生成器.generateRandom();
断言(person).isEqualTo(新Person("John Doe", 20));
}
}
1. JUnit 5 可以使用“扩展”来扩展其功能。此注解允许它通过注解识别模拟并正确注入它们。

2.注释创建该字段的模拟实例。这与在测试方法主体中@Mock编写相同。mock(RandomGenerator.class)

3.@InjectMocks注释将创建一个新的实例PersonGenerator,并在该实例中注入模拟generator。

有关 JUnit 5 扩展的更多详细信息请参见此处。

有关 Mockito 注入的更多详细信息请参见此处。

使用 有一个缺陷@InjectMocks。它可能消除了手动声明对象实例的需要,但我们失去了构造函数的编译时安全性。如果在任何时候有人向构造函数添加另一个依赖项,我们不会在这里得到编译时错误。这可能会导致难以检测的测试失败。我更喜欢使用@BeforeEach来手动设置实例
Post Reply