From 0d23dae8efd44ce1489ff7ecf4b39ee3b5e28008 Mon Sep 17 00:00:00 2001 From: wizardforcel <562826179@qq.com> Date: Tue, 5 Oct 2021 23:45:16 +0800 Subject: [PATCH] 2021-10-05 23:45:16 --- docs/master-soft-test-junit5/4.md | 20 +++++----- docs/master-soft-test-junit5/5.md | 62 +++++++++++++++---------------- docs/master-soft-test-junit5/6.md | 2 +- docs/test-driven-java-dev/01.md | 4 +- docs/test-driven-java-dev/02.md | 10 ++--- docs/test-driven-java-dev/06.md | 2 +- docs/test-driven-java-dev/11.md | 2 +- 7 files changed, 51 insertions(+), 51 deletions(-) diff --git a/docs/master-soft-test-junit5/4.md b/docs/master-soft-test-junit5/4.md index a60370f..1769c3f 100644 --- a/docs/master-soft-test-junit5/4.md +++ b/docs/master-soft-test-junit5/4.md @@ -81,7 +81,7 @@ class TestInfoTest { `TestInfo`对象依赖注入控制台输出 -# 重复信息参数 Terresolver +# `RepetitionInfoParameterResolver` JUnit5 中开箱即用的第二个解析器称为`RepetitionInfoParameterResolver`。给定一个测试类,如果`@RepeatedTest`、`@BeforeEach`或`@AfterEach`方法中的方法参数类型为`RepetitionInfo`,则`RepetitionInfoParameterResolver`将提供`RepetitionInfo`的实例。 @@ -647,13 +647,13 @@ import org.junit.jupiter.params.provider.ValueSource; } ``` -此类的方法(`testWithStrings`定义了一个参数化测试,其中指定了字符串数组。由于注释`@ValueSource`(在本例中为`"Hello"`和`"World"`中指定了两个字符串参数,测试逻辑将执行两次,每个值一次。此数据使用方法的参数注入测试方法,在本例中,通过名为 argument 的`String`变量注入。总之,当执行该测试类时,输出如下: +此类的方法(`testWithStrings`定义了一个参数化测试,其中指定了字符串数组。由于注释`@ValueSource`(在本例中为`"Hello"`和`"World"`中指定了两个字符串参数,测试逻辑将执行两次,每个值一次。此数据使用方法的参数注入测试方法,在本例中,通过名为`argument`的`String`变量注入。总之,当执行该测试类时,输出如下: ![](img/00078.gif) 使用`@ValueSource`和字符串参数提供程序执行参数化测试 -我们还可以在`@ValueSource`注释中使用整数基元类型(`int`、`long`和`double`。下面的示例演示了如何。这个示例类的方法(名为`testWithInts`、`testWithLongs`和`testWithDoubles`分别使用原语类型 int、long 和 double,使用注释`@ValueSource`以整数值的形式定义参数。为此,需要指定`@ValueSource`的`ints`、`longs`和`doubles`元素: +我们还可以在`@ValueSource`注释中使用整数基元类型(`int`、`long`和`double`。下面的示例演示了如何。这个示例类的方法(名为`testWithInts`、`testWithLongs`和`testWithDoubles`分别使用原语类型`int`、`long`和`double`,使用注释`@ValueSource`以整数值的形式定义参数。为此,需要指定`@ValueSource`的`ints`、`longs`和`doubles`元素: ```java package io.github.bonigarcia; @@ -703,7 +703,7 @@ class ValueSourcePrimitiveTypesParameterizedTest { 注释`@EnumSource`允许指定参数化测试,其中参数源是 Java 枚举类。默认情况下,枚举的每个值都将用于为参数化测试提供数据,每次一个。 -例如,在下面的测试类中,方法`testWithEnum`被注释为`@ParameterizedTest`与`@EnumSource`结合。正如我们所看到的,这个注释的值是`TimeUnit.class`,这是一个标准的 Java 注释(包 Java.util.concurrent),用于表示持续时间。此枚举中定义的可能值为`NANOSECONDS`、`MICROSECONDS`、`MILLISECONDS`、`SECONDS`、`MINUTES`、`HOURS`和`DAYS`: +例如,在下面的测试类中,方法`testWithEnum`被注释为`@ParameterizedTest`与`@EnumSource`结合。正如我们所看到的,这个注释的值是`TimeUnit.class`,这是一个标准的 Java 注释(包`Java.util.concurrent`),用于表示持续时间。此枚举中定义的可能值为`NANOSECONDS`、`MICROSECONDS`、`MILLISECONDS`、`SECONDS`、`MINUTES`、`HOURS`和`DAYS`: ```java package io.github.bonigarcia; @@ -962,7 +962,7 @@ class MethodSourcePrimitiveTypesParameterizedTest { 使用`@MethodSource`和原语类型参数提供程序执行参数化测试 -最后,关于`@MethodSource`参数化测试,值得一提的是,允许方法提供者返回不同类型(对象或基本类型)的流。这对于真实世界的测试用例非常方便。例如,下面的类实现了一个参数化测试,其中参数提供程序是一个返回混合类型参数的方法:`String`和`int`。这些参数作为方法参数(在示例中称为 first 和 second)注入测试中。 +最后,关于`@MethodSource`参数化测试,值得一提的是,允许方法提供者返回不同类型(对象或基本类型)的流。这对于真实世界的测试用例非常方便。例如,下面的类实现了一个参数化测试,其中参数提供程序是一个返回混合类型参数的方法:`String`和`int`。这些参数作为方法参数(在示例中称为`first`和`second`)注入测试中。 ```java package io.github.bonigarcia; @@ -999,13 +999,13 @@ class MethodSourceMixedTypesParameterizedTest { 使用具有不同类型参数的`@MethodSource`执行参数化测试 -# @CsvSource 和@CsvFileSource +# `@CsvSource`和`@CsvFileSource` 为参数化测试指定参数源的另一种方法是使用逗号分隔值(CSV)。这可以使用注释`@CsvSource`完成,该注释允许将 CSV 内容作为字符串嵌入注释的值中。 考虑下面的例子。它包含一个 Jupiter 参数化测试(名为`testWithCsvSource`,使用注释`@CsvSource`。此批注包含字符串数组。在数组的每个元素中,我们可以看到有一个由逗号分隔的不同值。 -CSV 的内容自动转换为字符串和 int。要了解 JUnit 5 在参数中进行的隐式类型转换的更多信息,请参阅本章中的“参数转换”一节。 +CSV 的内容自动转换为字符串和`int`。要了解 JUnit 5 在参数中进行的隐式类型转换的更多信息,请参阅本章中的“参数转换”一节。 ```java package io.github.bonigarcia; @@ -1290,7 +1290,7 @@ class ExplicitConversionParameterizedTest { } ``` -我们的自定义转换是一个扩展 JUnit5 的`SimpleArgumentConverter`的类。此类重写实际转换发生的 convert 方法。在本例中,我们只需将任何参数源转换为`String`。 +我们的自定义转换是一个扩展 JUnit5 的`SimpleArgumentConverter`的类。此类重写实际转换发生的`convert`方法。在本例中,我们只需将任何参数源转换为`String`。 ```java package io.github.bonigarcia; @@ -1315,7 +1315,7 @@ import org.junit.jupiter.params.converter.SimpleArgumentConverter; JUnit5 中与参数化测试相关的最后一个特性与每次执行测试的显示名称有关。正如我们所了解的,参数化测试通常作为几个单独的测试执行。因此,为了保证可追溯性,最好将每个测试执行与参数源链接起来。 -为此,注释`@ParameterizedTest`接受一个名为 name 的元素,我们可以在其中为测试执行指定一个自定义名称(`String`。此外,在这个字符串中,我们可以使用几个内置占位符,如下表所述: +为此,注释`@ParameterizedTest`接受一个名为`name`的元素,我们可以在其中为测试执行指定一个自定义名称(`String`。此外,在这个字符串中,我们可以使用几个内置占位符,如下表所述: | **占位符** | **说明** | | --- | --- | @@ -1355,7 +1355,7 @@ import org.junit.jupiter.params.provider.CsvSource; Java 9 于 2017 年 9 月 21 日发布供**通用**(**GA**)使用。Java9 附带了许多新特性。其中,模块化是 Java9 的定义特性。 -到目前为止,Java 中存在一个模块化问题,对于大型代码库来说尤其重要。每个公共类都可以被类路径中的任何其他类访问,从而导致类的意外使用。此外,类路径还存在潜在的问题,例如无法知道是否存在重复的 JAR。为了解决这些问题,Java9 提供了 Java 平台模块系统,它允许创建模块化 JAR 文件。这种类型的模块包含一个名为`module-info.java`的附加模块描述符。这些文件的内容非常简单:它使用关键字 requires 声明对其他模块的依赖关系,并使用关键字`exports`导出自己的包。默认情况下,所有未导出的包都封装在模块中,例如: +到目前为止,Java 中存在一个模块化问题,对于大型代码库来说尤其重要。每个公共类都可以被类路径中的任何其他类访问,从而导致类的意外使用。此外,类路径还存在潜在的问题,例如无法知道是否存在重复的 JAR。为了解决这些问题,Java9 提供了 Java 平台模块系统,它允许创建模块化 JAR 文件。这种类型的模块包含一个名为`module-info.java`的附加模块描述符。这些文件的内容非常简单:它使用关键字`requires`声明对其他模块的依赖关系,并使用关键字`exports`导出自己的包。默认情况下,所有未导出的包都封装在模块中,例如: ```java module mymodule { diff --git a/docs/master-soft-test-junit5/5.md b/docs/master-soft-test-junit5/5.md index 34311e4..dfca738 100644 --- a/docs/master-soft-test-junit5/5.md +++ b/docs/master-soft-test-junit5/5.md @@ -6,7 +6,7 @@ 如第 2 章所述,JUnit 5 的扩展模型允许我们通过第三方(工具供应商、开发人员等)扩展 JUnit 5 的核心功能。在 Jupiter 扩展模型中,扩展点是一个回调接口,扩展实现该接口,然后在 JUnit 5 框架中注册(激活)。正如我们将在本章中发现的,JUnit5 扩展模型可用于提供与现有第三方框架的无缝集成。具体地说,在本章中,我们将回顾 JUnit 5 扩展的以下技术: -* **Mockito**:Mock(test-double)单元测试框架。 +* **Mockito**:模拟(测试替身)单元测试框架。 * **Spring**:用于构建企业应用程序的 Java 框架。 * **Selenium**:一个自动化 Web 应用程序导航和评估的测试框架。 * **Cumber**:测试框架,允许我们创建按照**行为驱动开发**(**BDD**)风格编写的验收测试。 @@ -41,7 +41,7 @@ 简单地说,Mockito 是一个测试框架,允许创建、插桩和验证模拟对象。为此,Mockito 提供了一个 API 来隔离 SUT 及其文档。一般来说,使用 Mockito 包括三个不同的步骤: 1. **模拟对象**:为了隔离我们的 SUT,我们使用 Mockito API 创建其关联文档的 mock。这样,我们保证 SUT 不依赖于它的实际文档,而我们的单元测试实际上关注于 SUT。 -2. **设置期望值**:mock 对象与其他测试替身对象(如桩)的区别在于,可以根据单元测试的需要,使用自定义期望值对 mock 对象进行编程。Mockito 术语中的这个过程称为 stubing 方法,其中这些方法属于 mock。默认情况下,模拟对象模仿真实对象的行为。实际上,这意味着模拟对象返回适当的伪值,例如布尔类型为`false`,对象为`null`,整数或长返回类型为 0,等等。Mockito 允许我们使用一个丰富的 API 来改变这种行为,它允许桩在调用方法时返回一个特定的值。 +2. **设置期望值**:模拟对象与其他测试替身对象(如桩)的区别在于,可以根据单元测试的需要,使用自定义期望值对模拟对象进行编程。Mockito 术语中的这个过程称为 stubing 方法,其中这些方法属于模拟。默认情况下,模拟对象模仿真实对象的行为。实际上,这意味着模拟对象返回适当的伪值,例如布尔类型为`false`,对象为`null`,整数或长返回类型为 0,等等。Mockito 允许我们使用一个丰富的 API 来改变这种行为,它允许桩在调用方法时返回一个特定的值。 当一个模拟对象没有任何预期(即没有*桩方法*时,从技术上讲,它不是*模拟*对象,而是*虚拟*对象(请看第一章、“软件质量和 Java 测试回顾”对于定义)。 @@ -53,7 +53,7 @@ | --- | --- | --- | | `@Mock` | 此注释标识由 Mockito 创建的模拟对象。这通常用于单据。 | 1.模拟对象 | | `@InjectMocks` | 此注释标识将在其中注入模拟的对象。这通常用于我们要测试的单元,即我们的 SUT。 | 1.模拟对象 | -| `@Spy` | 除了 mock 之外,Mockito 还允许我们创建 spy 对象(即部分 mock 实现,因为它们在非桩方法中使用真实实现)。 | 1.模拟对象 | +| `@Spy` | 除了模拟之外,Mockito 还允许我们创建间谍对象(即部分模拟实现,因为它们在非桩方法中使用真实实现)。 | 1.模拟对象 | | `Mockito.when(x).thenReturn(y)`| 这些方法允许我们指定给定模拟对象的桩方法(`x`应该返回的值(`y`。 | 2.设定期望值(*插桩方式*) | | `Mockito.doReturn(y).when(x)` | | | | `Mockito.when(x).thenThrow(e)` | 这些方法允许我们指定调用给定模拟对象的桩方法(`x`时应引发的异常(`e`)。 | 2.设定期望值(*插桩方式*) | @@ -68,12 +68,12 @@ | `BDDMockito.given(x).willAnswer(a)` | | | | `BDDMockito.given(x).willCallRealMethod()` | | | | `Mockito.verify()` | 此方法验证模拟对象的调用。可以选择使用以下方法增强此验证: | 3.核查 | -| | `times(n)`:stubbed 方法被精确调用`n`次。 | | +| | `times(n)`:插桩方法被精确调用`n`次。 | | | | `never()`:从未调用桩方法。 | | -| | `atLeastOnce()`:stubbed 方法至少调用一次。 | | -| | `atLeast(n)`:stubbed 方法至少被调用 n 次。 | | -| | `atMost(n)`:stubbed 方法最多调用 n 次。 | | -| | `only()`:如果对 mock 对象调用任何其他方法,mock 将失败。 | | +| | `atLeastOnce()`:插桩方法至少调用一次。 | | +| | `atLeast(n)`:插桩方法至少被调用 n 次。 | | +| | `atMost(n)`:插桩方法最多调用 n 次。 | | +| | `only()`:如果对模拟对象调用任何其他方法,模拟将失败。 | | | | `timeout(m)`:此方法最多在`m`毫秒内调用。 | | | `Mockito.verifyZeroInteractions()``Mockito.verifyNoMoreInteractions()` | 这两个方法验证桩方法没有交互。在内部,它们使用相同的实现。 | 3.核查 | | `@Captor` | 这个注释允许我们定义一个`ArgumentChaptor`对象,目的是验证传递给桩方法的参数。 | 3.核查 | @@ -165,7 +165,7 @@ public class MockitoExtension 该扩展(以及其他扩展)计划在开源项目 [JUnit Pioneer](http://junit-pioneer.org/) 中发布。该项目由 Java 开发者、博客 [CodeFX](https://blog.codefx.org/) 的作者 Nicolai Palog 负责维护。 -检查前面的类,我们可以检查它是否只是 Jupiter 扩展模型的一个用例(在本书的第 2 章、“JUnit 5 中的新增内容”中描述),它实现了扩展回调`TestInstancePostProcessor`和`ParameterResolver`。首先,在测试用例实例化后,调用`postProcessTestInstance`方法,并在该方法的主体中进行 mock 的初始化: +检查前面的类,我们可以检查它是否只是 Jupiter 扩展模型的一个用例(在本书的第 2 章、“JUnit 5 中的新增内容”中描述),它实现了扩展回调`TestInstancePostProcessor`和`ParameterResolver`。首先,在测试用例实例化后,调用`postProcessTestInstance`方法,并在该方法的主体中进行模拟的初始化: ```java MockitoAnnotations.initMocks(testInstance) @@ -345,7 +345,7 @@ public class LoginRepository { 现在,我们将使用 JUnit5 和 Mockito 测试我们的系统。首先,我们测试控制器组件。因为我们正在进行单元测试,所以需要将`LoginController`登录与系统的其余部分隔离开来。要做到这一点,我们需要模拟它的依赖关系,在本例中是`LoginService`组件。使用前面解释的 SUT/DOC 术语,在这个测试中,我们的 SUT 是类`LoginController`,它的 DOC 是类`LoginService`。 -为了用 JUnit5 实现我们的测试,首先我们需要用`@ExtendWith`注册`MockitoExtension`。然后,我们用`@InjectMocks`(类别`LoginController`)声明 SUT,用`@Mock`(类别`LoginService`声明其 DOC。我们实施了两个测试(`@Test`。第一个(`testLoginOk`指定调用 mock`loginService`的方法 login 时,该方法应返回`true`。之后,实际执行 SUT,并验证其响应(在这种情况下,返回的字符串必须是`OK`。此外,Mockito API 再次用于评估是否不再与 mock`LoginService`进行交互。第二个测试(`testLoginKo`)是等效的,但是将方法 login 插桩为返回`false`,因此在这种情况下 SUT`(LoginController)`的响应必须是`KO`: +为了用 JUnit5 实现我们的测试,首先我们需要用`@ExtendWith`注册`MockitoExtension`。然后,我们用`@InjectMocks`(类别`LoginController`)声明 SUT,用`@Mock`(类别`LoginService`声明其 DOC。我们实施了两个测试(`@Test`。第一个(`testLoginOk`指定调用模拟`loginService`的方法`login`时,该方法应返回`true`。之后,实际执行 SUT,并验证其响应(在这种情况下,返回的字符串必须是`OK`。此外,Mockito API 再次用于评估是否不再与模拟`LoginService`进行交互。第二个测试(`testLoginKo`)是等效的,但是将方法`login`插桩为返回`false`,因此在这种情况下 SUT`(LoginController)`的响应必须是`KO`: ```java package io.github.bonigarcia; @@ -414,7 +414,7 @@ class LoginControllerLoginTest { 现在让我们来看另一个例子,在这个例子中,对组件`LoginController`测试了负面场景(即错误情况)。下面的类包含两个测试,第一个(`testLoginError`)用于评估使用空表单时系统的响应(应该是`ERROR`)。在第二个测试(`testLoginException`中,我们对模拟`loginService`的方法登录进行编程,以在首次使用任何表单时引发异常。然后,我们练习 SUT(`LoginController`并评估该响应实际上是一个`ERROR`: -注意,在设置 mock 方法的期望值时,我们使用的是参数匹配器 any(由 Mockito 提供)。 +注意,在设置模拟方法的期望值时,我们使用的是参数匹配器`any`(由 Mockito 提供)。 ```java package io.github.bonigarcia; @@ -593,7 +593,7 @@ class LoginServiceTest { 使用 JUnit 5 和 Mockito 执行`LoginServiceTest`的单元测试 -下面的类提供了一个捕获模拟对象参数的简单示例。我们定义了一个类型为`ArgumentCaptor`的类属性,该类属性用`@Captor`注释。然后,在测试主体中,执行 SUT(本例中为`LoginService`,并捕获方法 login 的参数。最后,评估该论点的价值: +下面的类提供了一个捕获模拟对象参数的简单示例。我们定义了一个类型为`ArgumentCaptor`的类属性,该类属性用`@Captor`注释。然后,在测试主体中,执行 SUT(本例中为`LoginService`,并捕获方法`login`的参数。最后,评估该论点的价值: ```java package io.github.bonigarcia; @@ -639,7 +639,7 @@ class LoginServiceChaptorTest { 使用 JUnit 5 和 Mockito 执行`LoginServiceChaptorTest`的单元测试 -我们在本章中看到的最后一个与 Mockito 有关的例子与间谍的使用有关。正如前面介绍的,默认情况下,间谍在非桩方法中使用实际实现。因此,如果我们不在 spy 对象中桩方法,我们得到的就是测试中的真实对象。这就是下一个示例中发生的情况。如我们所见,我们使用`LoginService`作为我们的 SUT,然后我们监视对象`LoginRepository`。由于在测试主体中,我们没有在 spy 对象中编程期望,因此我们正在测试中评估真实系统。 +我们在本章中看到的最后一个与 Mockito 有关的例子与间谍的使用有关。正如前面介绍的,默认情况下,间谍在非桩方法中使用实际实现。因此,如果我们不在间谍对象中桩方法,我们得到的就是测试中的真实对象。这就是下一个示例中发生的情况。如我们所见,我们使用`LoginService`作为我们的 SUT,然后我们监视对象`LoginRepository`。由于在测试主体中,我们没有在间谍对象中编程期望,因此我们正在测试中评估真实系统。 总之,测试数据准备好获得正确的登录(使用用户名为`user`,密码为`p1`,这在`LoginRepository`的实际实现中存在于硬编码值中),然后获得一些不成功登录的伪值: @@ -769,7 +769,7 @@ public class MessageComponent { 此时,值得回顾一下执行 Spring 组件依赖项注入的不同方式: 1. 字段注入:注入的组件是一个带有`@Autowired`注释的类字段,就像前面的示例一样。作为一个好处,这种注入可以消除杂乱的代码,例如设置器方法或构造函数参数。 -2. Setter 注入:注入的组件被声明为类中的一个字段,然后为该字段创建一个 Setter 并用`@Autowired`注释。 +2. 设置器注入:注入的组件被声明为类中的一个字段,然后为该字段创建一个设置器并用`@Autowired`注释。 3. 构造函数注入:在类构造函数中注入依赖项,并用`@Autowired`(图中为 3-a)注释。这是前面示例中显示的方式。从 Spring 4.3 开始,不再需要用`@Autowired`注释构造函数来执行注入(3-b)。 @@ -851,7 +851,7 @@ Spring 是一个名为`spring-test`的模块,支持 Spring 组件的单元测 * `org.springframework.test.jdbc`包包含类`JdbcTestUtils`,它是 JDBC 实用程序函数的集合,旨在简化标准数据库访问。 * `org.springframework.test.util`包包含类`ReflectionTestUtils`,它是一组实用方法的集合,用于在测试应用程序代码时设置非公共字段或调用私有/受保护的设置器方法。 -# 测试 Spring 启动应用程序 +# 测试 SpringBoot应用程序 如前所述,Spring Boot 是 Spring 产品组合的一个项目,旨在简化 Spring 应用程序的开发。使用 Spring Boot 的主要好处总结如下: @@ -865,21 +865,21 @@ Spring 是一个名为`spring-test`的模块,支持 Spring 组件的单元测 | **名称** | **说明** | | --- | --- | | `spring-boot-starter` | 核心启动器,包括自动配置支持和日志记录 | -| `spring-boot-starter-batch` | Spring批用起动器 | -| `spring-boot-starter-cloud-connectors` | 使用 SpringCloudConnectors 的初学者,它简化了与云平台(如 CloudFoundry 和 Heroku)中的服务的连接 | +| `spring-boot-starter-batch` | SpringBatch起动器 | +| `spring-boot-starter-cloud-connectors` | 使用`SpringCloudConnectors`的启动器,它简化了与云平台(如 CloudFoundry 和 Heroku)中的服务的连接 | | `spring-boot-starter-data-jpa` | 将 Spring 数据 JPA 与 Hibernate 结合使用的启动程序 | | `spring-boot-starter-integration` | 使用 Spring 集成的启动器 | -| `spring-boot-starter-jdbc` | 将 JDBC 与 Tomcat JDBC 连接池一起使用的初学者 | -| `spring-boot-starter-test` | 用于使用库(包括 JUnit、Hamcrest 和 Mockito)测试 SpringBoot应用程序的初学者 | -| `spring-boot-starter-thymeleaf` | 用于使用 IELAF 视图构建 MVC Web 应用程序的初学者 | -| `spring-boot-starter-web` | 使用 SpringMVC 构建 Web(包括 REST)应用程序的初学者。使用 Tomcat 作为默认的嵌入式容器 | -| `spring-boot-starter-websocket` | 使用 Spring 框架的 WebSocket 支持构建 WebSocket 应用程序的初学者 | +| `spring-boot-starter-jdbc` | 将 JDBC 与 Tomcat JDBC 连接池一起使用的启动器 | +| `spring-boot-starter-test` | 用于使用库(包括 JUnit、Hamcrest 和 Mockito)测试 SpringBoot应用程序的启动器 | +| `spring-boot-starter-thymeleaf` | 用于使用 IELAF 视图构建 MVC Web 应用程序的启动器 | +| `spring-boot-starter-web` | 使用 SpringMVC 构建 Web(包括 REST)应用程序的启动器。使用 Tomcat 作为默认的嵌入式容器 | +| `spring-boot-starter-websocket` | 使用 Spring 框架的 WebSocket 支持构建 WebSocket 应用程序的启动器 | 有关 Spring Boot 的完整信息,请访问[官方参考资料](https://projects.spring.io/spring-boot/)。 SpringBoot 提供了不同的功能来简化测试。例如,它提供了`@SpringBootTest`注释,用于测试类的类级别。此注释将为这些测试创建`ApplicationContext`(与`@ContextConfiguration`类似,但用于基于 SpringBoot的应用程序)。正如我们在前面的章节中所看到的,`spring-test`模块中,我们使用注释`@ContextConfiguration(classes=… )`来指定要加载的 Bean 定义(Spring`@Configuration`。在测试 SpringBoot应用程序时,这通常不是必需的。SpringBoot 的测试注释将自动搜索主配置(如果没有明确定义)。搜索算法从包含测试的包开始,直到找到一个`@SpringBootApplication`注释类。 -SpringBoot 还促进了对 Spring 组件使用模拟。为此,提供了注释`@MockBean`。此注释允许在`ApplicationContext`中为 Bean 定义 Mockito mock。它可以是新的 Bean,但也可以替换单个现有的 Bean 定义。模拟 Bean 在每个测试方法之后自动重置。这种方法通常被称为容器内测试,与容器外测试相对应,其中使用模拟库(例如,Mockito)对Spring组件进行单元测试,以隔离,而不需要Spring`ApplicationContext`。例如,Spring 应用程序的两种单元测试的示例将在下一节中显示。 +SpringBoot 还促进了对 Spring 组件使用模拟。为此,提供了注释`@MockBean`。此注释允许在`ApplicationContext`中为 Bean 定义 Mockito 模拟。它可以是新的 Bean,但也可以替换单个现有的 Bean 定义。模拟 Bean 在每个测试方法之后自动重置。这种方法通常被称为容器内测试,与容器外测试相对应,其中使用模拟库(例如,Mockito)对Spring组件进行单元测试,以隔离,而不需要Spring`ApplicationContext`。例如,Spring 应用程序的两种单元测试的示例将在下一节中显示。 # 用于 Spring 的 JUnit5 扩展 @@ -946,7 +946,7 @@ class SimpleSpringTest { ``` -另一方面,如果我们使用 Gradle,dependencies 子句将如下所示: +另一方面,如果我们使用 Gradle,`dependencies`子句将如下所示: ```java dependencies { @@ -1386,7 +1386,7 @@ Selenium Grid schema 下表总结了 WebDriver API 的主要功能: -WebDriver 对象创建:它允许创建 WebDriver 实例,这些实例从测试代码中用于远程控制浏览器。 +`WebDriver`对象创建:它允许创建`WebDriver`实例,这些实例从测试代码中用于远程控制浏览器。 ```java WebDriver driver = new FirefoxDriver(); @@ -1402,7 +1402,7 @@ WebDriver driver = new OperaDriver(); driver.get("http://junit.org/junit5/"); ``` -定位元素:它允许使用不同的策略识别网页(WebElement)中的元素:按 id、名称、类名、CSS 选择器、链接文本、标记名或 XPath +定位元素:它允许使用不同的策略识别网页(WebElement)中的元素:按 ID、名称、类名、CSS 选择器、链接文本、标记名或 XPath ```java WebElement webElement = driver.findElement(By.id("id")); @@ -1429,7 +1429,7 @@ boolean selected = webElement.isSelected(); boolean displayed = webElement.isDisplayed(); ``` -句柄等待:WebDriver 可以显式和隐式地处理等待。 +句柄等待:`WebDriver`可以显式和隐式地处理等待。 ```java // Explicit @@ -1476,7 +1476,7 @@ XPath(XML 路径语言)是一种用于构建表达式以解析和处理类 * `RemoteWebDriver`:用于控制远程浏览器(Selenium Grid)。 * `AppiumDriver`:用于控制移动设备(Android 和 iOS)。 -考虑下面的类,它使用了胡 T0T,即使用 MultT1。本例定义了三个测试,它们将使用本地浏览器执行。第一个(名为`testWithChrome`)使用 Chrome 作为浏览器。为此,由于`selenium-jupiter`的依赖注入特性,方法只需使用`ChromeDriver`类型声明一个方法参数。然后,在测试主体中,在该对象中调用`WebDriver`API。请注意,此简单测试将打开一个网页,并断言标题与预期一致。接下来,test(`testWithFirefoxAndOpera`类似,但这次同时使用两种不同的浏览器:Firefox(使用`FirefoxDriver`实例)和 Opera(使用`OperaDriver`实例)。第三个也是最后一个测试(`testWithHeadlessBrowsers`声明并使用两个无头浏览器(`HtmlUnit`和`PhantomJS`): +考虑下面的类,它使用了胡 T0T,即使用 MultT1。本例定义了三个测试,它们将使用本地浏览器执行。第一个(名为`testWithChrome`)使用 Chrome 作为浏览器。为此,由于`selenium-jupiter`的依赖注入特性,方法只需使用`ChromeDriver`类型声明一个方法参数。然后,在测试主体中,在该对象中调用`WebDriver`API。请注意,此简单测试将打开一个网页,并断言标题与预期一致。接下来,测试(`testWithFirefoxAndOpera`类似,但这次同时使用两种不同的浏览器:Firefox(使用`FirefoxDriver`实例)和 Opera(使用`OperaDriver`实例)。第三个也是最后一个测试(`testWithHeadlessBrowsers`声明并使用两个无头浏览器(`HtmlUnit`和`PhantomJS`): ```java package io.github.bonigarcia; @@ -1600,7 +1600,7 @@ public class AppiumTest { [Cucumber](https://cucumber.io/) 是一个测试框架,旨在自动化按照**行为驱动开发**(**BDD**)风格编写的验收测试。Cucumber 是用 Ruby 编写的,不过也有其他语言(包括 Java、JavaScript 和 Python)的实现。 -# Cucumber壳 +# Cucumber Cucumber 执行用名为 Gherkin 的语言编写的指定测试。它是一种具有给定结构的纯文本自然语言(例如,英语或 Cucumber 支持的其他 60 多种语言之一)。Cucumber被设计成供非程序员使用,通常是客户、业务分析、经理等等。 @@ -1798,13 +1798,13 @@ JUnit5 Docker 的使用非常简单。我们只需要用`@Docker`注释我们的 * `image`:待启动的 Docker 镜像。 * `ports`:Docker 容器的端口映射。这是必需的,因为容器必须至少有一个端口可见才能使用。 -* `environments`:传递给 docker 容器的可选环境变量。默认值:`{}`。 +* `environments`:传递给 Docker 容器的可选环境变量。默认值:`{}`。 * `waitFor`:运行测试前等待的可选日志。默认值:`@WaitFor(NOTHING)`。 * `newForEachCase`:布尔标志,用于确定是否应为每个测试用例重新创建容器。如果只为测试类创建一次,则该值将为`false`。默认值:`true`。 考虑下面的例子。这个测试类使用`@Docker`注释来启动 MySql 容器(容器图像 MySql)和每个测试的开始。内部集装箱端口为`3306`,将映射到主机端口`8801`。然后,定义了几个环境属性(MySql 根密码、默认数据库以及用户名和密码)。直到容器日志中出现跟踪`mysqld:ready for connections`(表示 MySql 实例已启动并正在运行)后,测试才会开始执行。在测试主体中,我们针对容器中运行的 MySQL 实例启动 JDBC 连接。 -此测试已在 Windows 计算机上执行。因此,JDBCURL 的主机是 192.168.99.100,这是 Docker 机器的 IP。它是一个允许在虚拟主机上安装 Docker 引擎的工具,如 Windows 或 p[Mac](https://docs.docker.com/machine/)。在 Linux 机器中,该 IP 可以是 127.0.0.1(localhost)。 +此测试已在 Windows 计算机上执行。因此,JDBCURL 的主机是`192.168.99.100`,这是 Docker 机器的 IP。它是一个允许在虚拟主机上安装 Docker 引擎的工具,如 Windows 或 p[Mac](https://docs.docker.com/machine/)。在 Linux 机器中,该 IP 可以是`127.0.0.1`(localhost)。 ```java package io.github.bonigarcia; diff --git a/docs/master-soft-test-junit5/6.md b/docs/master-soft-test-junit5/6.md index 196da5f..485b8b6 100644 --- a/docs/master-soft-test-junit5/6.md +++ b/docs/master-soft-test-junit5/6.md @@ -72,7 +72,7 @@ 为了正确地设计测试,我们需要明确定义需要实现什么。为此,重要的是记住测试的一般结构是什么,已经在第 1 章、“软件质量和 Java 测试回顾”中解释过。因此,对于每个测试,我们需要定义: * 什么是测试夹具,即 SUT 中执行测试所需的状态?这是在测试开始时在称为设置的阶段完成的。在测试结束时,测试夹具可能会在称为拆卸的阶段释放。 -* 什么是 SUT,如果我们正在进行单元测试,哪些是它的文档?单元测试应该是独立的,因此我们需要为 DOC 定义替身测试(通常是 mock 或 spie)。 +* 什么是 SUT,如果我们正在进行单元测试,哪些是它的文档?单元测试应该是独立的,因此我们需要为 DOC 定义替身测试(通常是模拟或 spie)。 * 这些主张是什么?这是测试的关键部分。如果没有断言,我们就不能声称实际进行了测试。为了设计断言,值得回忆一下它的一般结构。简言之,断言包括一些预期值(测试数据)和从 SUT 获得的实际结果的比较。如果任何断言为否定,测试将被宣布为失败(测试裁决): ![](img/00138.jpeg) diff --git a/docs/test-driven-java-dev/01.md b/docs/test-driven-java-dev/01.md index 94a8700..dceb6a7 100644 --- a/docs/test-driven-java-dev/01.md +++ b/docs/test-driven-java-dev/01.md @@ -162,7 +162,7 @@ TDD 的主要目标是可测试的代码设计,将测试作为非常有用的 # 嘲笑 -为了让测试快速运行并提供持续的反馈,代码需要以这样一种方式组织:方法、函数和类可以很容易地替换为 mock 和 stub。这种类型的实际代码替换的常用词是**测试替身**。外部依赖性会严重影响执行速度;例如,我们的代码可能需要与数据库通信。通过模仿外部依赖关系,我们能够大幅提高速度。整个单元测试套件的执行应该以分钟为单位,如果不是秒的话。以一种易于模仿和插桩的方式设计代码迫使我们通过应用关注点分离来更好地构建代码。 +为了让测试快速运行并提供持续的反馈,代码需要以这样一种方式组织:方法、函数和类可以很容易地替换为模拟和 stub。这种类型的实际代码替换的常用词是**测试替身**。外部依赖性会严重影响执行速度;例如,我们的代码可能需要与数据库通信。通过模仿外部依赖关系,我们能够大幅提高速度。整个单元测试套件的执行应该以分钟为单位,如果不是秒的话。以一种易于模仿和插桩的方式设计代码迫使我们通过应用关注点分离来更好地构建代码。 比速度更重要的是去除外部因素的好处。设置数据库、Web 服务器、外部 API 和代码可能需要的其他依赖项既耗时又不可靠。在许多情况下,这些依赖项甚至可能不可用。例如,我们可能需要创建一个与数据库通信的代码,并让其他人创建一个模式。如果没有 mock,我们将需要等待该模式被设置。 @@ -216,6 +216,6 @@ BDD 可以为非编码人员提供必要的文档,同时仍然保持 TDD 和 虽然代码设计是主要目标,但在整个 TDD 过程中创建的测试是一项宝贵的资产,应该加以利用,并严重影响我们对传统测试实践的看法。我们经历了最常见的实践,如白盒测试和黑盒测试,试图将它们放到 TDD 的角度,并展示了它们可以相互带来的好处。 -您发现 mock 是非常重要的工具,在编写测试时经常是必须的。最后,我们讨论了如何将测试用作可执行文档,以及 TDD 如何减少调试的必要性。 +您发现模拟是非常重要的工具,在编写测试时经常是必须的。最后,我们讨论了如何将测试用作可执行文档,以及 TDD 如何减少调试的必要性。 现在我们已经掌握了理论知识,是时候建立开发环境并对不同的测试框架和工具进行概述和比较了。 \ No newline at end of file diff --git a/docs/test-driven-java-dev/02.md b/docs/test-driven-java-dev/02.md index f97471f..de55ecc 100644 --- a/docs/test-driven-java-dev/02.md +++ b/docs/test-driven-java-dev/02.md @@ -289,7 +289,7 @@ public void before() { `@Before`注释指定了将在每个测试方法之前运行的方法。我们可以使用它来设置测试数据,而不用担心之后运行的测试会改变数据的状态。在前面的示例中,我们实例化了`Friendships`类,并向`Friendships`列表中添加了五个示例条目。无论每个单独的测试将执行什么更改,这些数据都将一次又一次地重新创建,直到执行所有测试为止。 -使用这两个注释的常见示例是设置数据库数据、创建测试所需的文件等。稍后,我们将看到如何使用 mock 避免外部依赖。然而,功能或集成测试可能仍然需要这些依赖项,`@Before`和`@BeforeClass`注释是设置它们的好方法。 +使用这两个注释的常见示例是设置数据库数据、创建测试所需的文件等。稍后,我们将看到如何使用模拟避免外部依赖。然而,功能或集成测试可能仍然需要这些依赖项,`@Before`和`@BeforeClass`注释是设置它们的好方法。 一旦建立了数据,我们就可以进行实际测试: @@ -538,7 +538,7 @@ gradle test jacocoTestReport 测试使用外部资源或第三方库的代码的常用方法是什么?嘲笑是答案。模拟对象或简单的模拟对象是可以用来替换真实对象的模拟对象。当依赖外部资源的对象被剥夺时,它们非常有用。 -事实上,在开发应用程序时根本不需要数据库。相反,您可以使用 mock 来加速开发和测试,并且只在运行时使用真正的数据库连接。我们不必花时间建立数据库和准备测试数据,而可以专注于编写类,并在以后的集成期间考虑它们。 +事实上,在开发应用程序时根本不需要数据库。相反,您可以使用模拟来加速开发和测试,并且只在运行时使用真正的数据库连接。我们不必花时间建立数据库和准备测试数据,而可以专注于编写类,并在以后的集成期间考虑它们。 出于演示目的,我们将引入两个新类:`Person`类和`FriendCollection`类,用于表示人员和数据库对象映射。将使用 [MongoDB](https://www.mongodb.org/) 进行持久化。 @@ -668,14 +668,14 @@ public class FriendshipsTest { } ``` -在您的测试类中,测试类应该用`@InjectMocks`注释。这将告诉 Mockito 要将 mock 注入到哪个类中: +在您的测试类中,测试类应该用`@InjectMocks`注释。这将告诉 Mockito 要将模拟注入到哪个类中: ```java @InjectMocks FriendshipsMongo friendships; ``` -从那时起,我们可以指定类中的哪些特定方法或对象(在本例中为`FriendshipsMongo`)将被 mock 替换: +从那时起,我们可以指定类中的哪些特定方法或对象(在本例中为`FriendshipsMongo`)将被模拟替换: ```java @Mock @@ -744,7 +744,7 @@ FriendshipsMongo friendships = new FriendshipsMongo(); FriendsCollection friends; ``` -`@TestSubject`注释类似于 Mockito 的`@InjectMocks`,而`@Mock`注释表示要以类似于 Mockito 的`@Mock`的方式模拟的对象。此外,类型`NICE`告诉 mock 返回空。 +`@TestSubject`注释类似于 Mockito 的`@InjectMocks`,而`@Mock`注释表示要以类似于 Mockito 的`@Mock`的方式模拟的对象。此外,类型`NICE`告诉模拟返回空。 让我们比较一下我们对 Mockito 的一个断言: diff --git a/docs/test-driven-java-dev/06.md b/docs/test-driven-java-dev/06.md index 06a10be..98e0a4e 100644 --- a/docs/test-driven-java-dev/06.md +++ b/docs/test-driven-java-dev/06.md @@ -179,7 +179,7 @@ public TicTacToeCollection() throws UnknownHostException { 在实例化`TicTacToeCollection`类时,我们使用指定的 DB 名称(`tic-tac-toe`创建一个新的`MongoCollection`并将其分配给局部变量。 -请容忍我们。在我们进入有趣的部分之前,只剩下一个规范了,我们将使用 mock 和 spies。 +请容忍我们。在我们进入有趣的部分之前,只剩下一个规范了,我们将使用模拟和 spies。 # 规范–Mongo 集合的名称 diff --git a/docs/test-driven-java-dev/11.md b/docs/test-driven-java-dev/11.md index e328fdb..cebbac7 100644 --- a/docs/test-driven-java-dev/11.md +++ b/docs/test-driven-java-dev/11.md @@ -225,7 +225,7 @@ public final void whenAddIsUsedThenItWorks() { 好处:这减少了代码依赖性并且测试执行会更快。 -模拟是快速执行测试和集中于单个功能单元的能力的先决条件。通过模拟正在测试的方法外部的依赖关系,开发人员可以专注于手头的任务,而无需花费时间进行设置。在大型团队的情况下,这些依赖关系甚至可能无法开发。此外,没有模拟的测试执行往往很慢。数据库、其他产品、服务等是 mock 的最佳候选对象。 +模拟是快速执行测试和集中于单个功能单元的能力的先决条件。通过模拟正在测试的方法外部的依赖关系,开发人员可以专注于手头的任务,而无需花费时间进行设置。在大型团队的情况下,这些依赖关系甚至可能无法开发。此外,没有模拟的测试执行往往很慢。数据库、其他产品、服务等是模拟的最佳候选对象。 使用设置和拆卸方法。 -- GitLab