From e979de7cc448a7f1ddf0ac27bad3b9fec9686064 Mon Sep 17 00:00:00 2001 From: wizardforcel <562826179@qq.com> Date: Fri, 1 Oct 2021 21:15:47 +0800 Subject: [PATCH] 2021-10-01 21:15:47 --- docs/java11-cb/00.md | 2 +- docs/java11-cb/14.md | 2 +- docs/master-soft-test-junit5/1.md | 104 +++++++++++++++--------------- docs/master-soft-test-junit5/5.md | 38 +++++------ docs/test-driven-java-dev/01.md | 2 +- docs/test-driven-java-dev/02.md | 2 +- docs/test-driven-java-dev/06.md | 6 +- docs/test-driven-java-dev/11.md | 2 +- 8 files changed, 79 insertions(+), 79 deletions(-) diff --git a/docs/java11-cb/00.md b/docs/java11-cb/00.md index fe9349b..5b12ccb 100644 --- a/docs/java11-cb/00.md +++ b/docs/java11-cb/00.md @@ -36,7 +36,7 @@ 第 13 章“使用新的日期和时间 API*”演示了如何构造时区相关和独立的日期和时间实例,如何在日期实例之间创建基于日期和时间的时段,如何表示历元时间,如何操作和比较日期和时间实例,如何使用不同的日历系统,以及如何使用`DateTimeFormatter`设置日期格式。 -第 14 章“测试”解释了如何在 API 与其他组件集成之前对其进行单元测试,包括使用一些虚拟数据的存根依赖和模拟依赖。我们还将向您展示如何编写 fixture 来填充测试数据,以及如何通过集成不同的 api 并测试它们来测试您的应用程序行为。 +第 14 章“测试”解释了如何在 API 与其他组件集成之前对其进行单元测试,包括使用一些虚拟数据的桩依赖和模拟依赖。我们还将向您展示如何编写 fixture 来填充测试数据,以及如何通过集成不同的 api 并测试它们来测试您的应用程序行为。 第 15 章“使用 Java 10 和 Java 11 进行编码的新方法”,演示了如何使用局部变量类型推断,以及何时和如何使用局部变量语法进行 lambda 参数。 diff --git a/docs/java11-cb/14.md b/docs/java11-cb/14.md index e4ee861..055ff36 100644 --- a/docs/java11-cb/14.md +++ b/docs/java11-cb/14.md @@ -1,6 +1,6 @@ # 测试 -本章介绍如何测试应用程序如何捕获和自动测试用例,如何在 API 与其他组件集成之前对其进行单元测试,以及如何集成所有单元。我们将向您介绍**行为驱动开发**(**BDD**),并展示它如何成为您应用程序开发的起点。我们还将演示 JUnit 框架如何用于单元测试。有时,在单元测试期间,我们必须使用一些虚拟数据来存根依赖项,这可以通过模拟依赖项来完成。我们将向您展示如何使用模拟库来实现这一点。我们还将向您展示如何编写 fixture 来填充测试数据,以及如何通过集成不同的 api 并将它们测试在一起来测试应用程序的行为。我们将介绍以下配方: +本章介绍如何测试应用程序如何捕获和自动测试用例,如何在 API 与其他组件集成之前对其进行单元测试,以及如何集成所有单元。我们将向您介绍**行为驱动开发**(**BDD**),并展示它如何成为您应用程序开发的起点。我们还将演示 JUnit 框架如何用于单元测试。有时,在单元测试期间,我们必须使用一些虚拟数据来插桩依赖项,这可以通过模拟依赖项来完成。我们将向您展示如何使用模拟库来实现这一点。我们还将向您展示如何编写 fixture 来填充测试数据,以及如何通过集成不同的 api 并将它们测试在一起来测试应用程序的行为。我们将介绍以下配方: * 黄瓜行为测试 * 使用 JUnit 对 API 进行单元测试 diff --git a/docs/master-soft-test-junit5/1.md b/docs/master-soft-test-junit5/1.md index 797d165..dea0910 100644 --- a/docs/master-soft-test-junit5/1.md +++ b/docs/master-soft-test-junit5/1.md @@ -6,18 +6,18 @@ 著名的测试框架 JUnit 自 1995 年诞生以来已经走过了漫长的道路。2017 年 9 月 10 日,项目生命周期中的一个重要里程碑发生了,即 JUnit 5.0.0 的发布。在深入了解 JUnit5 的细节之前,有必要回顾一下软件测试的现状,以便了解我们的发展方向。为了达到这个目的,本章对软件质量、软件测试和 Java 测试的背景进行了高层次的回顾。具体而言,本章由三节组成: -* **软件质量**:第一节回顾了质量工程的现状:质量保证、ISO/IEC-2500、**V****验证&验证**(**V&V**)和软件缺陷(*bug*)。 +* **软件质量**:第一节回顾了质量工程的现状:质量保证、ISO/IEC-2500、**验证和确认**(**V&V**)和软件缺陷(*BUG*)。 * **软件测试**:这是保证软件质量、减少软件缺陷数量最常用的活动。本节提供了软件测试级别(单元、集成、系统和验收)、方法(黑盒、白盒和非功能)、自动化和手动软件测试的理论背景。 -* **针对****Java 虚拟机**(**JVM**)的测试框架:本节总结了 JUnit 框架遗留版本(即版本 3 和版本 4)的主要特性。最后,简要描述了 JUnit 的替代测试框架和增强器。 +* 针对 **Java 虚拟机**(**JVM**)的测试框架:本节总结了 JUnit 框架遗留版本(即版本 3 和版本 4)的主要特性。最后,简要描述了 JUnit 的替代测试框架和增强器。 # 软件质量 -软件是为特定客户或一般市场开发的计算机程序、相关数据和相关文档的集合。它是现代世界的一个重要组成部分,在电信、公用事业、商业、文化、娱乐等领域已经变得无处不在。问题*什么是软件质量?*可以根据相关从业者在软件系统中的角色生成不同的答案。软件产品或服务涉及两个主要群体: +软件是为特定客户或一般市场开发的计算机程序、相关数据和相关文档的集合。它是现代世界的一个重要组成部分,在电信、公用事业、商业、文化、娱乐等领域已经变得无处不在。问题“什么是软件质量?”可以根据相关从业者在软件系统中的角色生成不同的答案。软件产品或服务涉及两个主要群体: * **消费者**:使用软件的人。在该组中,我们可以区分*客户*(即负责购买软件产品或服务的人员)和*用户*(即出于各种目的使用软件产品或服务的人员)。然而,客户和用户的双重角色非常常见。 * **生产者**:从事软件产品开发、管理、维护、营销和服务的人员。 -消费者的质量期望是软件系统按照规定执行有用的功能。对于软件生产商来说,基本的质量问题是通过生产符合**服务水平协议**(**SLA**的软件产品来履行其合同义务。著名软件工程师 Roger Pressman 对软件质量的定义包括两种观点: +消费者的质量期望是软件系统按照规定执行有用的功能。对于软件生产商来说,基本的质量问题是通过生产符合**服务水平协议**(**SLA**)的软件产品来履行其合同义务。著名软件工程师 Roger Pressman 对软件质量的定义包括两种观点: 以创建有用产品的方式应用的有效软件过程,为生产和使用该产品的人提供可测量的价值。 @@ -28,7 +28,7 @@ 1. **质量策划**:该阶段通过在项目成本和预算约束下管理客户期望,建立整体质量目标。该质量计划还包括策略,即选择要执行的活动以及提供反馈和评估的适当质量度量。 2. **质量保证(QA)**:通过规划和执行一系列活动,确保项目生命周期中的软件产品和过程满足其规定的要求,以提供足够的信心,确保软件中存在质量。主要的 QA 活动是验证&确认,但也有其他活动,如软件质量度量、质量标准的使用、配置管理、文档管理或专家意见。 -3. **QA 后**:该阶段包括质量量化和改进测量、分析、反馈和后续活动。这些活动的目的是提供产品质量的定量评估和改进机会的识别。 +3. **QA 之后**:该阶段包括质量量化和改进测量、分析、反馈和后续活动。这些活动的目的是提供产品质量的定量评估和改进机会的识别。 这些阶段如下图所示: @@ -47,15 +47,15 @@ # 质量管理 -**质量保证****质量保证**主要涉及定义或选择应用于软件开发过程或软件产品的标准。《软件质量保证》*(2004)一书的作者 Daniel Galin 将质量保证定义为:* +**质量保证**主要涉及定义或选择应用于软件开发过程或软件产品的标准。《软件质量保证》(2004)一书的作者 Daniel Galin 将质量保证定义为: - *系统的、有计划的一组必要行动,以提供足够的信心,确保软件系统产品的软件开发和维护过程符合既定规范以及保持进度和在预算范围内运行的管理要求。 +系统的、有计划的一组必要行动,以提供足够的信心,确保软件系统产品的软件开发和维护过程符合既定规范以及保持进度和在预算范围内运行的管理要求。 质量保证过程选择验证和确认活动、工具和方法,以支持选定的质量标准。V&V 是一系列活动,其主要目的是在产品不合格的情况下扣留产品。相比之下,QA 旨在通过在整个开发和维护过程中引入各种活动来最小化质量成本,以防止错误的原因,检测错误,并在开发的早期阶段纠正错误。因此,QA 大大降低了不合格产品的比率。总之,V&V 活动只是 QA 活动总范围的一部分。 # ISO/IEC-25000 -提出了各种质量标准,以适应这些不同的质量观点和期望。标准**ISO/IEC-9126**是软件工程界最具影响力的标准之一。然而,研究人员和从业者发现了该标准中的一些问题和弱点。因此,ISO/IEC-9126 国际标准被关于**软件产品质量要求和评估**(**广场**的**ISO/IEC-25000**系列国际标准所取代。本节提供了本标准的高级概述。 +提出了各种质量标准,以适应这些不同的质量观点和期望。标准**ISO/IEC-9126**是软件工程界最具影响力的标准之一。然而,研究人员和从业者发现了该标准中的一些问题和弱点。因此,ISO/IEC-9126 国际标准被关于**软件产品质量要求和评估**(**SQuaRE**)的**ISO/IEC-25000**系列国际标准所取代。本节提供了本标准的高级概述。 ISO/IEC-2500 质量参考模型区分了软件质量的不同观点: @@ -63,30 +63,30 @@ ISO/IEC-2500 质量参考模型区分了软件质量的不同观点: * **外部质量**:这涉及系统的性能,在执行过程中可以观察到。 * **使用中的质量**:这与用户在系统运行和维护期间所体验到的性能有关。 -理想情况下,开发(*过程质量*影响内部质量;然后,内部质量决定了外部质量。最后,外部质量决定使用中的质量。此链如下图所示: +理想情况下,开发(*过程质量*)影响内部质量;然后,内部质量决定了外部质量。最后,外部质量决定使用中的质量。此链如下图所示: ![](img/00006.jpeg) ISO/IEC-2500 产品质量参考模型 -ISO/IEC-25000 的质量模型将产品质量模型(即内部和外部属性)划分为八个顶级质量特征:*功能适用性、**性能效率、兼容性*、*可用性*、*可靠性*、*安全性**可维护性*和*可移植性*。以下定义直接取自本标准: +ISO/IEC-25000 的质量模型将产品质量模型(即内部和外部属性)划分为八个顶级质量特征:*功能适用性*、*性能效率*、*兼容性*、*可用性*、*可靠性*、*安全性*、*可维护性*和*可移植性*。以下定义直接取自本标准: -* **功能适用性:**这表示产品或系统在特定条件下使用时提供满足规定和隐含需求的功能的程度。 -* **性能效率:**表示在规定条件下相对于所使用资源量的性能。 -* **兼容性:**这是指产品、系统或组件在共享相同硬件或软件环境的情况下,与其他产品、系统或组件交换信息和/或执行其所需功能的程度。 -* **可用性:**指产品或系统在特定的使用环境下,被特定的用户使用以实现特定目标的有效性、效率和满意度。 -* **可靠性:**这是指系统、产品或组件在特定条件下在特定时间段内执行特定功能的程度。 -* **安全性:**这是指产品或系统保护信息和数据的程度,以使个人或其他产品或系统具有与其授权类型和级别相适应的数据访问程度 -* **可维护性:**这表示产品或系统可被修改以改进、纠正或适应环境和需求变化的有效性和效率程度 -* **可移植性:**这是系统、产品或组件可以从一个硬件、软件或其他操作或使用环境转移到另一个硬件、软件或其他操作或使用环境的有效性和效率 +* **功能适用性**:这表示产品或系统在特定条件下使用时提供满足规定和隐含需求的功能的程度。 +* **性能效率**:表示在规定条件下相对于所使用资源量的性能。 +* **兼容性**:这是指产品、系统或组件在共享相同硬件或软件环境的情况下,与其他产品、系统或组件交换信息和/或执行其所需功能的程度。 +* **可用性**:指产品或系统在特定的使用环境下,被特定的用户使用以实现特定目标的有效性、效率和满意度。 +* **可靠性**:这是指系统、产品或组件在特定条件下在特定时间段内执行特定功能的程度。 +* **安全性**:这是指产品或系统保护信息和数据的程度,以使个人或其他产品或系统具有与其授权类型和级别相适应的数据访问程度 +* **可维护性**:这表示产品或系统可被修改以改进、纠正或适应环境和需求变化的有效性和效率程度 +* **可移植性**:这是系统、产品或组件可以从一个硬件、软件或其他操作或使用环境转移到另一个硬件、软件或其他操作或使用环境的有效性和效率 另一方面,使用中的质量属性可分为以下五个特征: -* **有效性:**这是用户实现特定目标的准确性和完整性。 -* **效率:**这些是与用户实现目标的准确性和完整性相关的资源消耗。 -* **满意度:**当产品或系统在特定的使用环境中使用时,满足用户需求的程度。 -* **无风险:**这是指产品或系统减轻经济状况、人类生命、健康或环境潜在风险的程度。 -* **上下文覆盖率:**这是指产品或系统在指定使用上下文和最初明确确定的上下文以外的上下文中能够有效、高效、无风险和满意地使用的程度。 +* **有效性**:这是用户实现特定目标的准确性和完整性。 +* **效率**:这些是与用户实现目标的准确性和完整性相关的资源消耗。 +* **满意度**:当产品或系统在特定的使用环境中使用时,满足用户需求的程度。 +* **无风险**:这是指产品或系统减轻经济状况、人类生命、健康或环境潜在风险的程度。 +* **上下文覆盖率**:这是指产品或系统在指定使用上下文和最初明确确定的上下文以外的上下文中能够有效、高效、无风险和满意地使用的程度。 # 验证和确认 @@ -94,8 +94,8 @@ ISO/IEC-25000 的质量模型将产品质量模型(即内部和外部属性) 杰出的计算机科学教授巴里·博姆(Barry Boehm)早在 1979 年就表达了他们之间的差异: -* **验证***我们是否在正确构建产品?*验证的目的是检查软件是否满足其规定的功能和非功能需求(即规范)。 -* **验证***我们是否在生产正确的产品?*验证的目的是确保软件满足消费者的期望。这是一个比验证更一般的过程,因为规范并不总是反映消费者的真实愿望或需求。 +* **验证**:“我们是否在正确构建产品?”验证的目的是检查软件是否满足其规定的功能和非功能需求(即规范)。 +* **确认**:“我们是否在生产正确的产品?”验证的目的是确保软件满足消费者的期望。这是一个比验证更一般的过程,因为规范并不总是反映消费者的真实愿望或需求。 验证和确认活动包括一系列广泛的质量保证活动。尽管软件测试在 V&V 中扮演着极其重要的角色,但其他活动也是必要的。在验证和确认过程中,可使用两大类系统检查和分析技术: @@ -106,17 +106,17 @@ ISO/IEC-25000 的质量模型将产品质量模型(即内部和外部属性) # 软件缺陷 -验证与确认正确性方面的关键是软件缺陷的概念。术语**缺陷**(也称为*缺陷*)是指一个通用软件问题。IEEE 标准 610.12 提出了以下与软件缺陷相关的分类法: +验证与确认正确性方面的关键是软件缺陷的概念。术语**缺陷**(也称为*BUG*)是指一个通用软件问题。IEEE 标准 610.12 提出了以下与软件缺陷相关的分类法: * **错误**:产生错误结果的人为行为。错误可分为两类: 1. 语法错误(违反所用语言的一条或多条规则的程序语句)。 2. 逻辑错误(不正确的数据字段、超出范围的术语或无效的组合)。 * **故障**:软件系统中出现错误的表现称为故障。例如,错误的步骤、过程或数据定义。 -* **故障**:软件系统无法执行其所需功能称为(系统)故障。 +* **失败**:软件系统无法执行其所需功能称为(系统)故障。 -术语*bug*最早由软件先驱格蕾丝·霍珀(Grace Hooper)于 1946 年提出,当时一只蛾子被困在机电计算机中导致系统故障。在这十年中,术语*调试*也被引入,作为检测和纠正系统缺陷的过程。 +术语*缺陷*最早由软件先驱格蕾丝·霍珀(Grace Hooper)于 1946 年提出,当时一只蛾子被困在机电计算机中导致系统故障。在这十年中,术语*调试*(DEBUG)也被引入,作为检测和纠正系统缺陷的过程。 -除了缺陷的粒度级别外,还可以将**事件**视为与软件消费者感知到的故障相关的症状。总之,错误、故障、失败和事件是软件缺陷的不同方面。缺陷的这四个方面之间存在因果关系。错误可能会导致将故障注入软件,并且在执行软件时,故障可能会导致故障。最后,当最终用户或客户遇到故障时,就会发生事故。可以执行不同的 QA 活动,以尽量减少软件系统中的缺陷数量。根据 Jeff Tian 在其著作*软件质量工程*(2005)中的定义,备选方案可分为以下三类: +除了缺陷的粒度级别外,还可以将**事件**视为与软件消费者感知到的故障相关的症状。总之,错误、故障、失败和事件是软件缺陷的不同方面。缺陷的这四个方面之间存在因果关系。错误可能会导致将故障注入软件,并且在执行软件时,故障可能会导致故障。最后,当最终用户或客户遇到故障时,就会发生事故。可以执行不同的 QA 活动,以尽量减少软件系统中的缺陷数量。根据 Jeff Tian 在其著作《软件质量工程》(2005)中的定义,备选方案可分为以下三类: * 通过错误消除预防缺陷:例如,使用某些过程和产品标准可以帮助将某些类型的故障注入软件的可能性降至最低。 * 通过故障检测和排除减少缺陷:传统的测试和静态分析活动就是这一类的例子。我们将在本章正文中发现这些机制的具体类型。 @@ -137,7 +137,7 @@ ISO/IEC-25000 的质量模型将产品质量模型(即内部和外部属性) 有不同的方法可以被识别为静态分析: * **检查**(Michael Fagan 于 1976 年首次提出)是由人工检查员对软件工件进行的检查,旨在发现和修复软件系统中的故障。所有类型的软件资产都要接受检查,例如规范、设计模型等。存在检查的主要原因是,在开始执行检查之前,没有等待可执行程序(如在测试中)的可用性。 -* **评审**是一组人员检查软件及其相关文档,寻找潜在问题和不符合标准以及其他潜在问题或遗漏的过程。如今,在合并到共享源代码存储库之前,经常会对新代码进行审查。通常,评审由同一团队中的不同人员对代码作者进行(**同行评审**。这个过程在时间和精力上都非常昂贵,但另一方面,当正确执行时,它有助于确保较高的内部代码质量,从而降低潜在风险。 +* **评审**是一组人员检查软件及其相关文档,寻找潜在问题和不符合标准以及其他潜在问题或遗漏的过程。如今,在合并到共享源代码存储库之前,经常会对新代码进行审查。通常,评审由同一团队中的不同人员对代码作者进行(**同行评审**)。这个过程在时间和精力上都非常昂贵,但另一方面,当正确执行时,它有助于确保较高的内部代码质量,从而降低潜在风险。 **演练**是一种特殊形式的复习。根据 IEEE 软件评审标准,演练是软件同行评审的一种形式,其中设计师或程序员通过软件产品带领开发团队成员和其他相关方,参与者就可能出现的错误、违反开发标准的情况提出问题和评论,以及其他问题。 @@ -154,7 +154,7 @@ ISO/IEC-25000 的质量模型将产品质量模型(即内部和外部属性) 软件测试包括在有限的测试用例集上对程序的行为进行动态评估,这些测试用例从通常无限的执行域中选择,与预期的行为相对应。该定义的关键概念描述如下: -* **动态**:测试中的**系统**(**SUT**以特定的输入值执行,以发现其行为中的故障。因此,实际的 SUT 应该确保设计和代码是正确的,同时还要确保环境(如库、操作系统和网络支持等)是正确的。 +* **动态**:测试中的**系统**(**SUT**)以特定的输入值执行,以发现其行为中的故障。因此,实际的 SUT 应该确保设计和代码是正确的,同时还要确保环境(如库、操作系统和网络支持等)是正确的。 * **有限**:对于大多数实际程序,穷举测试是不可能的或不实用的。它们通常对每个操作都有大量允许的输入,加上更多无效或意外的输入,并且可能的操作序列通常也是无限的。测试人员必须选择一些测试,以便我们能够在可用时间内运行测试。 * **选择**:由于存在大量或无限可能的测试,我们只能运行其中的一小部分,因此测试的关键挑战是如何选择最有可能暴露系统故障的测试。 * **预期**:每次测试执行后,必须确定观察到的系统行为是否为故障。 @@ -190,8 +190,8 @@ ISO/IEC-25000 的质量模型将产品质量模型(即内部和外部属性) 单元测试是一种方法,通过这种方法,可以测试单个源代码片段,以验证该单元的设计和实现是否已正确实现。单元测试用例中按顺序执行四个阶段,如下所示: -* **设置**:测试用例初始化*测试夹具*,即 SUT 显示预期行为所需的图片之前的*。* -* **练习**:测试用例与 SUT 交互,从而从中获得一些结果。SUT 通常查询另一个组件,名为**依赖于组件**(**文档**)。 +* **装配**:测试用例初始化*测试夹具*,即 SUT 显示预期行为所需的前置步骤。 +* **实践**:测试用例与 SUT 交互,从而从中获得一些结果。SUT 通常查询另一个组件,名为**依赖组件**(**DOC**)。 * **验证**:测试用例确定是否使用断言(也称为谓词)获得了预期结果。 * **拆卸**:测试用例撕裂测试夹具,使 SUT 回到初始状态。 @@ -201,19 +201,19 @@ ISO/IEC-25000 的质量模型将产品质量模型(即内部和外部属性) 单元测试通用结构 -单元测试是与被测单元隔离进行的,也就是说,不与其文档交互。为此,使用*测试双精度*替换 SUT 所依赖的任何组件。有几种类型的双重测试: +单元测试是与被测单元隔离进行的,也就是说,不与其文档交互。为此,使用*测试替身*替换 SUT 所依赖的任何组件。有几种类型的双重测试: * **虚拟**对象仅满足真实对象 API,但从未实际使用过。虚拟对象的典型用例是当它们作为参数传递以满足方法签名时,但是虚拟对象没有实际使用。 * 一个**假**对象用一个更简单的实现(例如,内存中的数据库)替换了真实对象。 -* **存根**对象替换提供硬编码值作为响应的真实对象。 +* **桩**对象替换提供硬编码值作为响应的真实对象。 * 一个**模拟**对象也替换了真实对象,但这次用编程期望作为响应。 -* **spy**对象是一个部分模拟对象,这意味着它的一些方法是按照预期编程的,而其他方法使用真实对象的实现。 +* **间谍**对象是一个部分模拟对象,这意味着它的一些方法是按照预期编程的,而其他方法使用真实对象的实现。 # 集成测试 集成测试应暴露接口中的缺陷,以及集成组件或模块之间的交互。执行集成测试有不同的策略。这些策略描述了单元集成的顺序,假设单元已经单独测试过。常见的集成策略示例如下: -* **自上而下集成**:该策略从主单元(模块)开始,即过程树的根。主单元调用的任何较低级别模块都应替换为测试双精度模块。一旦测试人员确信主单元逻辑是正确的,存根就会逐渐被实际代码所取代。对于程序树中较低单元的其余部分,重复此过程。这种方法的主要优点是更容易发现缺陷。 +* **自上而下集成**:该策略从主单元(模块)开始,即过程树的根。主单元调用的任何较低级别模块都应替换为测试替身模块。一旦测试人员确信主单元逻辑是正确的,桩就会逐渐被实际代码所取代。对于程序树中较低单元的其余部分,重复此过程。这种方法的主要优点是更容易发现缺陷。 * **自底向上集成**:该策略以最基本的单元开始测试过程。较大的子系统由测试组件组装而成。这种类型的主要优点是不需要双重测试。 * **即席集成**:组件按照完成的自然顺序进行集成。它允许对系统进行早期测试。通常需要双倍测试。 * **主干集成**:构建组件骨架,其他组件逐步集成。这种方法的主要缺点是创建主干,这可能是劳动密集型的。 @@ -228,7 +228,7 @@ ISO/IEC-25000 的质量模型将产品质量模型(即内部和外部属性) # 测试方法 -测试方法(或策略)定义了设计测试用例的方法。它们可以是基于责任的(**黑框**)、基于实现的(**白框**)或**非功能性**。黑盒技术基于待测试项的指定功能设计测试用例。白盒测试依赖于源代码分析来开发测试用例。混合技术(灰盒)测试使用基于责任和基于实现的方法设计测试用例。 +测试方法(或策略)定义了设计测试用例的方法。它们可以是基于责任的(**黑盒**)、基于实现的(**白盒**)或**非功能性**。黑盒技术基于待测试项的指定功能设计测试用例。白盒测试依赖于源代码分析来开发测试用例。混合技术(灰盒)测试使用基于责任和基于实现的方法设计测试用例。 # 黑盒测试 @@ -236,17 +236,17 @@ ISO/IEC-25000 的质量模型将产品质量模型(即内部和外部属性) * **系统测试**:这是指一种完整的测试方法,在这种方法中,SUT 完全符合规范,符合测试假设。它只在每个域点都是一个单子域的有限意义上生成测试用例。在这一类别中,最常用的是*等价划分*和*边界值分析*,以及基于逻辑的技术,如*因果图*、*决策表*或*成对测试*。 * **随机测试**:这实际上是系统测试的对立面——抽样覆盖整个输入域。*模糊测试*是黑盒随机测试的一种形式,它随机变异格式良好的输入,并在结果数据上测试程序。它将随机顺序和/或结构错误的数据发送到系统,以查看是否发生故障。 -* **图形用户界面****GUI****测试**:这是通过与用户交互的图形界面来确保软件规范的过程。GUI 测试是事件驱动的(例如,鼠标移动或菜单选择),并通过消息或方法调用为底层应用程序代码提供前端。单元级的 GUI 测试通常用于按钮级。系统级 GUI 测试练习 SUT 的事件驱动特性。 +* **图形用户界面(GUI)测试**:这是通过与用户交互的图形界面来确保软件规范的过程。GUI 测试是事件驱动的(例如,鼠标移动或菜单选择),并通过消息或方法调用为底层应用程序代码提供前端。单元级的 GUI 测试通常用于按钮级。系统级 GUI 测试练习 SUT 的事件驱动特性。 * **基于模型的测试**(**MBT**):这是一种测试策略,其中测试用例部分源自描述 SUT 某些(如果不是全部)方面的模型。MBT 是黑盒测试的一种形式,因为测试是从一个模型生成的,该模型来自需求文档。它可以在不同的层次上完成(单元、集成或系统)。 -* **烟雾测试**:这是确保 SUT 关键功能的过程。冒烟测试用例是测试人员在接受构建进行进一步测试之前运行的第一个测试用例。烟雾测试用例的失败将意味着软件构建被拒绝。*冒烟测试*的名称来源于电气系统测试,第一个测试是打开电源,看看它是否冒烟。 +* **冒烟测试**:这是确保 SUT 关键功能的过程。冒烟测试用例是测试人员在接受构建进行进一步测试之前运行的第一个测试用例。冒烟测试用例的失败将意味着软件构建被拒绝。*冒烟测试*的名称来源于电气系统测试,第一个测试是打开电源,看看它是否冒烟。 -* **健全性测试**:这是确保 SUT 基本功能的过程。与烟雾测试类似,卫生测试在测试过程开始时进行,但其目标不同。在进行更详尽的测试之前,健全性测试应确保 SUT 的基本功能继续按预期工作(即 SUT 的*合理性*。 +* **健全性测试**:这是确保 SUT 基本功能的过程。与冒烟测试类似,卫生测试在测试过程开始时进行,但其目标不同。在进行更详尽的测试之前,健全性测试应确保 SUT 的基本功能继续按预期工作(即 SUT 的*合理性*)。 在软件测试社区中,冒烟测试和健全测试通常是令人困惑的术语。通常认为,这两种测试的执行都是为了避免在这些测试失败时在严格测试中浪费精力,这是它们的主要区别(关键功能与基本功能)。 -# 白盒试验 +# 白盒测试 -**白盒****测试**(也称为**结构**测试)基于对应用程序代码内部逻辑的了解。它确定程序代码结构和逻辑是否有故障。只有当测试人员知道程序应该做什么时,白盒测试用例才是准确的。 +**白盒测试**(也称为**结构**测试)基于对应用程序代码内部逻辑的了解。它确定程序代码结构和逻辑是否有故障。只有当测试人员知道程序应该做什么时,白盒测试用例才是准确的。 黑盒测试仅使用规范来识别用例,而白盒测试使用程序源代码(实现)作为测试用例识别的基础。为了为 SUT 选择一组好的测试用例,结合使用这两种方法是必要的。一些最重要的白盒技术如下: @@ -273,13 +273,13 @@ ISO/IEC-25000 的质量模型将产品质量模型(即内部和外部属性) 执行软件测试主要有两种类型: * **手动测试**:这是评估 SUT 的过程,由人工完成,通常是软件工程师或最终消费者。在这种类型的测试中,我们可以找到所谓的*探索性测试*,这是一种手动测试,其中人类测试人员通过调查和自由评估系统,使用其个人感知来评估系统。 -* **自动测试**:这是评估 SUT 的过程,测试过程(测试执行、报告等)使用专用软件和测试基础设施进行。Elfriede Dustin,在其著作*实施自动化软件测试:如何在提高质量的同时节省时间和降低成本*(2009)*中将**自动化软件测试**(**AST**定义为:* +* **自动测试**:这是评估 SUT 的过程,测试过程(测试执行、报告等)使用专用软件和测试基础设施进行。Elfriede Dustin,在其著作《实施自动化软件测试:如何在提高质量的同时节省时间和降低成本》(2009)中将**自动化软件测试**(**AST**)定义为: *在整个软件测试生命周期中应用和实施软件技术,以提高效率和有效性。 AST 的主要好处是:预期成本节约、缩短测试持续时间、提高测试的彻底性、提高测试准确性、改进结果报告和统计处理以及后续报告。 -自动测试通常在**持续集成**(**CI**过程的上下文中在构建服务器中执行。更多详情请参见第 7 章、*测试管理。* +自动测试通常在**持续集成**(**CI**)过程的上下文中在构建服务器中执行。更多详情请参见第 7 章“测试管理”。 AST 在*框架*中实现时最有效。测试框架可以定义为一组抽象概念、过程、过程和环境,在这些抽象概念、过程、过程和环境中设计、创建和实现自动化测试。该框架定义包括用于测试创建和实现的物理结构,以及这些组件之间的逻辑交互。 @@ -301,7 +301,7 @@ AST 在*框架*中实现时最有效。测试框架可以定义为一组抽象 *用户或客户测试*是测试过程中的一个阶段,用户或客户为系统测试提供输入和建议。*验收测试*是一种用户测试,但也可以有不同类型的*用户测试*: -* **阿尔法测试**:在软件发布给外部用户或客户之前,在开发人员的网站上与软件的消费者一起进行测试。 +* **Alpha 测试**:在软件发布给外部用户或客户之前,在开发人员的网站上与软件的消费者一起进行测试。 * **Beta 测试**:在客户现场进行测试,包括一群客户在自己的位置使用系统并提供反馈,然后再将系统发布给其他客户。 * **运行测试**:由最终用户在其正常运行环境下进行。 @@ -315,7 +315,7 @@ JUnit 被设计成一个单元测试框架。然而,它不仅可以用于实 # JUnit 3 -由于 JUnit3 的早期版本,该框架可以与 Java2 及更高版本一起工作。JUnit3 是开源软件,在**公共许可证**(**CPL**版本)1.0 下发布,托管在 [SourceForge](https://sourceforge.net/projects/junit/) 上。JUnit3 的最新版本是 JUnit3.8.2,发布于 2007 年 5 月 14 日。JUnit 在测试框架领域引入的主要需求如下: +由于 JUnit3 的早期版本,该框架可以与 Java2 及更高版本一起工作。JUnit3 是开源软件,在**公共许可证**(**CPL**)版本 1.0 下发布,托管在 [SourceForge](https://sourceforge.net/projects/junit/) 上。JUnit3 的最新版本是 JUnit3.8.2,发布于 2007 年 5 月 14 日。JUnit 在测试框架领域引入的主要需求如下: 1. 定义将运行哪些测试应该很容易。 2. 框架应该能够独立于所有其他测试运行测试。 @@ -392,7 +392,7 @@ public class TestSimple extends TestCase { JUnit3 允许通过称为 TestRunner 的 Java 应用程序运行测试用例。JUnit3.8.2 提供了三种不同的开箱即用的测试运行程序:两个图形(基于 Swing 和 AWT)和一个可从命令行使用的文本。JUnit 框架为每个测试提供单独的类装入器,以避免测试之间的副作用。 -构建工具(如 Ant 或 Maven)和**集成开发环境**-**IDE**-(如 Eclipse 和 IntelliJ)实现自己的 JUnit 测试运行程序是一种常见做法。 +构建工具(如 Ant 或 Maven)和**集成开发环境**(**IDE**)(如 Eclipse 和 IntelliJ)实现自己的 JUnit 测试运行程序是一种常见做法。 下图显示了当我们使用 JUnit Swing runner 时,以及当我们使用 Eclipse 运行相同的测试用例时,前面的测试是什么样子的。 @@ -437,7 +437,7 @@ public class TestAll { # JUnit 4 -JUnit4 仍然是一个开源框架,尽管许可证相对于 JUnit3 有所改变,从 CPL 到**Eclipse 公共许可证**(**EPL**版本 1.0。JUnit4 的源代码托管[在 GitHub 上](https://github.com/junit-team/junit4/)。 +JUnit4 仍然是一个开源框架,尽管许可证相对于 JUnit3 有所改变,从 CPL 到**Eclipse 公共许可证**(**EPL**)版本 1.0。JUnit4 的源代码托管[在 GitHub 上](https://github.com/junit-team/junit4/)。 2006 年 2 月 18 日,JUnit4.0 发布。它遵循与 JUnit3 相同的高级指导原则,即轻松定义测试,框架独立运行测试,框架通过测试检测和报告错误。 @@ -459,7 +459,7 @@ JUnit4 测试生命周期 下表总结了迄今为止 JUnit 3 和 JUnit 4 之间的主要差异: -| **特征** | **JUnit 3** | **JUnit 4** | +| **功能** | **JUnit 3** | **JUnit 4** | | --- | --- | | 测试定义 | `testXXX`模式 | `@Test`注释 | | 在第一次测试之前运行 | 不支持 | `@BeforeClass`注释 | @@ -681,7 +681,7 @@ JUnit 是 JVM 最流行的测试框架之一,被认为是软件工程中最有 * [Mockito](http://site.mockito.org/):这是模拟框架,可以与 JUnit 结合使用。 * [AssertJ](http://joel-costigliola.github.io/assertj/):这是 Java 的 fluent 断言库。 * [Hamcrest](http://hamcrest.org/):这是一个带有匹配器的库,可以组合这些匹配器来创建灵活且可读的断言。 -* [Cucumber](https://cucumber.io/):这是一个测试框架,允许运行以**行为驱动开发**(**BDD**风格编写的自动验收测试。 +* [Cucumber](https://cucumber.io/):这是一个测试框架,允许运行以**行为驱动开发**(**BDD**)风格编写的自动验收测试。 * [FitNesse](http://www.fitnesse.org/):这是一个测试框架,旨在通过促进系统功能的详细可读描述来支持验收测试。 虽然 JUnit 是 JVM 最大的测试框架,但它不是唯一的。JVM 还可以使用其他几个测试框架。例如: @@ -705,4 +705,4 @@ JUnit 是 JVM 最流行的测试框架之一,被认为是软件工程中最有 *软件质量*是软件工程中的一个关键概念,因为它决定了软件系统满足其需求和用户期望的程度。验证和确认是用于评估软件系统的一组活动的名称。V&V 的目标是确保软件的质量,同时减少缺陷的数量。V&V 中的两项核心活动是*软件测试*(对运行中的软件进行评估)和*静态分析*(对未执行的软件人工制品进行评估)。 -*自动化软件测试*在过去几十年中取得了最大的进步。在这个领域,*JUnit 框架*有着显著的地位。JUnit 被设计为 JVM 的单元框架。现在,JUnit 是 Java 社区中最流行的测试框架,它提供了一个全面的编程模型来创建和执行测试用例。在下一节中,我们将发现新版本的框架 JUnit5 提供的特性和功能。** \ No newline at end of file +*自动化软件测试*在过去几十年中取得了最大的进步。在这个领域,*JUnit 框架*有着显著的地位。JUnit 被设计为 JVM 的单元框架。现在,JUnit 是 Java 社区中最流行的测试框架,它提供了一个全面的编程模型来创建和执行测试用例。在下一节中,我们将发现新版本的框架 JUnit5 提供的特性和功能。 \ No newline at end of file diff --git a/docs/master-soft-test-junit5/5.md b/docs/master-soft-test-junit5/5.md index a6624e9..e4f4bed 100644 --- a/docs/master-soft-test-junit5/5.md +++ b/docs/master-soft-test-junit5/5.md @@ -38,14 +38,14 @@ 换句话说,我们想要测试被测的**系统**(**SUT**),而不是它的**依赖于组件**(**文档**)。为了实现这种隔离,我们通常使用*测试加倍*来替换这些文档。模拟对象是一种双重测试,它是按照对真实文档的期望进行编程的。 -简单地说,Mockito 是一个测试框架,允许创建、存根和验证模拟对象。为此,Mockito 提供了一个 API 来隔离 SUT 及其文档。一般来说,使用 Mockito 包括三个不同的步骤: +简单地说,Mockito 是一个测试框架,允许创建、插桩和验证模拟对象。为此,Mockito 提供了一个 API 来隔离 SUT 及其文档。一般来说,使用 Mockito 包括三个不同的步骤: 1. **Mocking objects**:为了隔离我们的 SUT,我们使用 Mockito API 创建其关联文档的 mock。这样,我们保证 SUT 不依赖于它的实际文档,而我们的单元测试实际上关注于 SUT。 -2. **设置期望值**:mock 对象与其他测试双重对象(如存根)的区别在于,可以根据单元测试的需要,使用自定义期望值对 mock 对象进行编程。Mockito 术语中的这个过程称为 stubing 方法,其中这些方法属于 mock。默认情况下,模拟对象模仿真实对象的行为。实际上,这意味着模拟对象返回适当的伪值,例如布尔类型为 false,对象为 null,整数或长返回类型为 0,等等。Mockito 允许我们使用一个丰富的 API 来改变这种行为,它允许存根在调用方法时返回一个特定的值。 +2. **设置期望值**:mock 对象与其他测试替身对象(如桩)的区别在于,可以根据单元测试的需要,使用自定义期望值对 mock 对象进行编程。Mockito 术语中的这个过程称为 stubing 方法,其中这些方法属于 mock。默认情况下,模拟对象模仿真实对象的行为。实际上,这意味着模拟对象返回适当的伪值,例如布尔类型为 false,对象为 null,整数或长返回类型为 0,等等。Mockito 允许我们使用一个丰富的 API 来改变这种行为,它允许桩在调用方法时返回一个特定的值。 -当一个模拟对象没有任何预期(即没有*存根方法*时,从技术上讲,它不是*模拟*对象,而是*虚拟*对象(请看第一章、*软件质量和 Java 测试回顾*对于定义)。 +当一个模拟对象没有任何预期(即没有*桩方法*时,从技术上讲,它不是*模拟*对象,而是*虚拟*对象(请看第一章、*软件质量和 Java 测试回顾*对于定义)。 -3. **验证**:最后,我们正在创建测试,因此,我们需要对 SUT 进行某种验证。Mockito 提供了一个强大的 API 来执行不同类型的验证。使用此 API,我们评估与 SUT 和文档的交互,使用模拟验证调用顺序,或者捕获并验证传递给存根方法的参数。此外,可以使用 JUnit 的内置断言功能或使用第三方断言库(例如,Hamcrest、AssertJ 或 Truth)来补充 Mockito 的验证功能。参见第 3 章*JUnit 5 标准测试*中的*断言*部分。 +3. **验证**:最后,我们正在创建测试,因此,我们需要对 SUT 进行某种验证。Mockito 提供了一个强大的 API 来执行不同类型的验证。使用此 API,我们评估与 SUT 和文档的交互,使用模拟验证调用顺序,或者捕获并验证传递给桩方法的参数。此外,可以使用 JUnit 的内置断言功能或使用第三方断言库(例如,Hamcrest、AssertJ 或 Truth)来补充 Mockito 的验证功能。参见第 3 章*JUnit 5 标准测试*中的*断言*部分。 下表总结了按上述阶段分组的 Mockito API: @@ -53,23 +53,23 @@ | --- | --- | --- | | `@Mock` | 此注释标识由 Mockito 创建的模拟对象。这通常用于单据。 | 1.模拟对象 | | `@InjectMocks` | 此注释标识将在其中注入模拟的对象。这通常用于我们要测试的单元,即我们的 SUT。 | 1.模拟对象 | -| `@Spy` | 除了 mock 之外,Mockito 还允许我们创建 spy 对象(即部分 mock 实现,因为它们在非存根方法中使用真实实现)。 | 1.模拟对象 | -| `Mockito.when(x).thenReturn(y)``Mockito.doReturn(y).when(x)` | 这些方法允许我们指定给定模拟对象的存根方法(`x`应该返回的值(`y`。 | 2.设定期望值(*存根方式*) | -| `Mockito.when(x).thenThrow(e)``Mockito.doThrow(e).when(x)` | 这些方法允许我们指定调用给定模拟对象的存根方法(`x`时应引发的异常(`e`)。 | 2.设定期望值(*存根方式*) | -| `Mockito.when(x).thenAnswer(a)``Mockito.doAnswer(a).when(x)` | 与返回硬编码值不同,当调用模拟的给定方法(`x`时,将执行动态用户定义逻辑(`Answer a`)。 | 2.设定期望值(*存根方式*) | -| `Mockito.when(x).thenCallRealMethod()``Mockito.doCallRealMethod().when(x)` | 这个方法允许我们真正实现一个方法,而不是模拟的方法。 | 2.设定期望值(*存根方式*) | -| `Mockito.doNothing().when(x)` | 使用间谍时,默认行为是调用对象的实际方法。为了避免执行`void`方法`x`,使用此方法。 | 2.设定期望值(*存根方式*) | -| `BDDMockito.given(x).willReturn(y)``BDDMockito.given(x).willThrow(e)``BDDMockito.given(x).willAnswer(a)``BDDMockito.given(x).willCallRealMethod()` | 行为驱动开发是一种测试方法,在这种方法中,测试是根据场景指定的,并在给定的(初始上下文)、(事件发生时的*和*然后*(确保某些结果)时实施。Mockito 通过类`BDDMockito`支持这种类型的测试。存根方法(`x`的行为等同于`Mockito.when(x)`。* | 2.设定期望值(*存根方式*) | +| `@Spy` | 除了 mock 之外,Mockito 还允许我们创建 spy 对象(即部分 mock 实现,因为它们在非桩方法中使用真实实现)。 | 1.模拟对象 | +| `Mockito.when(x).thenReturn(y)``Mockito.doReturn(y).when(x)` | 这些方法允许我们指定给定模拟对象的桩方法(`x`应该返回的值(`y`。 | 2.设定期望值(*插桩方式*) | +| `Mockito.when(x).thenThrow(e)``Mockito.doThrow(e).when(x)` | 这些方法允许我们指定调用给定模拟对象的桩方法(`x`时应引发的异常(`e`)。 | 2.设定期望值(*插桩方式*) | +| `Mockito.when(x).thenAnswer(a)``Mockito.doAnswer(a).when(x)` | 与返回硬编码值不同,当调用模拟的给定方法(`x`时,将执行动态用户定义逻辑(`Answer a`)。 | 2.设定期望值(*插桩方式*) | +| `Mockito.when(x).thenCallRealMethod()``Mockito.doCallRealMethod().when(x)` | 这个方法允许我们真正实现一个方法,而不是模拟的方法。 | 2.设定期望值(*插桩方式*) | +| `Mockito.doNothing().when(x)` | 使用间谍时,默认行为是调用对象的实际方法。为了避免执行`void`方法`x`,使用此方法。 | 2.设定期望值(*插桩方式*) | +| `BDDMockito.given(x).willReturn(y)``BDDMockito.given(x).willThrow(e)``BDDMockito.given(x).willAnswer(a)``BDDMockito.given(x).willCallRealMethod()` | 行为驱动开发是一种测试方法,在这种方法中,测试是根据场景指定的,并在给定的(初始上下文)、(事件发生时的*和*然后*(确保某些结果)时实施。Mockito 通过类`BDDMockito`支持这种类型的测试。桩方法(`x`的行为等同于`Mockito.when(x)`。* | 2.设定期望值(*插桩方式*) | | `Mockito.verify()` | 此方法验证模拟对象的调用。可以选择使用以下方法增强此验证: | 3.核查 | | | `times(n)`:stubbed 方法被精确调用`n`次。 | | -| | `never()`:从未调用存根方法。 | | +| | `never()`:从未调用桩方法。 | | | | `atLeastOnce()`:stubbed 方法至少调用一次。 | | | | `atLeast(n)`:stubbed 方法至少被调用 n 次。 | | | | `atMost(n)`:stubbed 方法最多调用 n 次。 | | | | `only()`:如果对 mock 对象调用任何其他方法,mock 将失败。 | | | | `timeout(m)`:此方法最多在`m`毫秒内调用。 | | -| `Mockito.verifyZeroInteractions()``Mockito.verifyNoMoreInteractions()` | 这两个方法验证存根方法没有交互。在内部,它们使用相同的实现。 | 3.核查 | -| `@Captor` | 这个注释允许我们定义一个`ArgumentChaptor`对象,目的是验证传递给存根方法的参数。 | 3.核查 | +| `Mockito.verifyZeroInteractions()``Mockito.verifyNoMoreInteractions()` | 这两个方法验证桩方法没有交互。在内部,它们使用相同的实现。 | 3.核查 | +| `@Captor` | 这个注释允许我们定义一个`ArgumentChaptor`对象,目的是验证传递给桩方法的参数。 | 3.核查 | | `Mockito.inOrder` | 它有助于验证与模拟的交互是否按给定顺序执行。 | 3.核查 | 使用上表中描述的不同注释(`@Mock`、`@InjectMocks`、`@Spy`和`@Captor`)是可选的,尽管对于测试可读性的影响是推荐的。换句话说,除了使用不同的 Mockito 类使用注释外,还有其他选择。例如,为了创建一个`Mock`,我们可以使用注释`@Mock`,如下所示: @@ -338,7 +338,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`指定调用 mock`loginService`的方法 login 时,该方法应返回 true。之后,实际执行 SUT,并验证其响应(在这种情况下,返回的字符串必须是`OK`。此外,Mockito API 再次用于评估是否不再与 mock`LoginService`进行交互。第二个测试(`testLoginKo`)是等效的,但是将方法 login 插桩为返回 false,因此在这种情况下 SUT`(LoginController)`的响应必须是`KO`: ```java package io.github.bonigarcia; @@ -632,7 +632,7 @@ class LoginServiceChaptorTest { 使用 JUnit 5 和 Mockito 执行*LoginServiceChaptorTest*的单元测试 -我们在本章中看到的最后一个与 Mockito 有关的例子与间谍的使用有关。正如前面介绍的,默认情况下,间谍在非存根方法中使用实际实现。因此,如果我们不在 spy 对象中存根方法,我们得到的就是测试中的真实对象。这就是下一个示例中发生的情况。如我们所见,我们使用`LoginService`作为我们的 SUT,然后我们监视对象`LoginRepository`。由于在测试主体中,我们没有在 spy 对象中编程期望,因此我们正在测试中评估真实系统。 +我们在本章中看到的最后一个与 Mockito 有关的例子与间谍的使用有关。正如前面介绍的,默认情况下,间谍在非桩方法中使用实际实现。因此,如果我们不在 spy 对象中桩方法,我们得到的就是测试中的真实对象。这就是下一个示例中发生的情况。如我们所见,我们使用`LoginService`作为我们的 SUT,然后我们监视对象`LoginRepository`。由于在测试主体中,我们没有在 spy 对象中编程期望,因此我们正在测试中评估真实系统。 总之,测试数据准备好获得正确的登录(使用用户名为`user`,密码为`p1`,这在`LoginRepository`的实际实现中存在于硬编码值中),然后获得一些不成功登录的伪值: @@ -1258,7 +1258,7 @@ class IndexTest { 容器内首次测试的控制台输出 -第二个测试类似,但作为一个差异因素,它使用测试能力`@MockBean`通过模拟覆盖Spring组件(在本例中为`PageService`)。在测试主体中,首先我们将模拟的方法`getPage`存根,以将组件的默认响应更改为`redirect:/page.html`。因此,当使用对象`MockMvc`在测试中请求资源`/`时,我们将获得一个 HTTP 302 响应(重定向)到资源`/page.html`(实际上是一个现有页面,如项目截图所示): +第二个测试类似,但作为一个差异因素,它使用测试能力`@MockBean`通过模拟覆盖Spring组件(在本例中为`PageService`)。在测试主体中,首先我们将模拟的方法`getPage`插桩,以将组件的默认响应更改为`redirect:/page.html`。因此,当使用对象`MockMvc`在测试中请求资源`/`时,我们将获得一个 HTTP 302 响应(重定向)到资源`/page.html`(实际上是一个现有页面,如项目截图所示): ```java package io.github.bonigarcia; @@ -2150,7 +2150,7 @@ class SpringBootRestTest { 使用 TestRestTemplate 验证 REST 服务的 Jupiter 测试的输出。 -在本节结束时,我们将看到一个示例,其中使用 [WireMock](http://wiremock.org/) 库。该库允许模拟 REST 服务,即所谓的 HTTP*模拟服务器*。此模拟服务器捕获对服务的传入请求,并提供存根响应。此功能对于测试使用 REST 服务的系统非常有用,但该服务在测试期间不可用(或者我们可以测试单独调用该服务的组件)。 +在本节结束时,我们将看到一个示例,其中使用 [WireMock](http://wiremock.org/) 库。该库允许模拟 REST 服务,即所谓的 HTTP*模拟服务器*。此模拟服务器捕获对服务的传入请求,并提供桩响应。此功能对于测试使用 REST 服务的系统非常有用,但该服务在测试期间不可用(或者我们可以测试单独调用该服务的组件)。 像往常一样,我们看到一个示例来演示它的用法。假设我们有一个使用远程 REST 服务的系统。为了实现该服务的客户机,我们使用 [Retrofit2](http://square.github.io/retrofit/),这是一个高度可配置的 Java HTTP 客户端。我们定义了使用此服务的接口,如下面的类所示。请注意,该服务公开了三个用于读取远程文件的端点(打开文件、读取流和关闭流): @@ -2218,7 +2218,7 @@ import retrofit2.converter.gson.GsonConverterFactory; } ``` -最后,我们实现了一个 JUnit5 测试来验证我们的服务。注意,我们正在创建模拟服务器(`**new** WireMockServer`,并使用 WireMock 在测试设置(`@BeforeEach`中提供的静态方法`stubFor(...)`对 REST 服务调用进行存根。由于在本例中,SUT 非常简单,并且没有文档,因此我们也在每个测试的设置中直接实例化了类`RemoteFileService`,使用模拟服务器 URL 作为构造函数参数。最后,我们测试我们的服务(使用模拟服务器),在本例中,通过调用方法`getFile`并评估其输出,简单地运行名为`wireMockServer`的对象。 +最后,我们实现了一个 JUnit5 测试来验证我们的服务。注意,我们正在创建模拟服务器(`**new** WireMockServer`,并使用 WireMock 在测试设置(`@BeforeEach`中提供的静态方法`stubFor(...)`对 REST 服务调用进行插桩。由于在本例中,SUT 非常简单,并且没有文档,因此我们也在每个测试的设置中直接实例化了类`RemoteFileService`,使用模拟服务器 URL 作为构造函数参数。最后,我们测试我们的服务(使用模拟服务器),在本例中,通过调用方法`getFile`并评估其输出,简单地运行名为`wireMockServer`的对象。 ```java diff --git a/docs/test-driven-java-dev/01.md b/docs/test-driven-java-dev/01.md index 379c611..9f0b98d 100644 --- a/docs/test-driven-java-dev/01.md +++ b/docs/test-driven-java-dev/01.md @@ -162,7 +162,7 @@ TDD 的主要目标是可测试的代码设计,将测试作为非常有用的 # 嘲笑 -为了让测试快速运行并提供持续的反馈,代码需要以这样一种方式组织:方法、函数和类可以很容易地替换为 mock 和 stub。这种类型的实际代码替换的常用词是**测试双精度**。外部依赖性会严重影响执行速度;例如,我们的代码可能需要与数据库通信。通过模仿外部依赖关系,我们能够大幅提高速度。整个单元测试套件的执行应该以分钟为单位,如果不是秒的话。以一种易于模仿和存根的方式设计代码迫使我们通过应用关注点分离来更好地构建代码。 +为了让测试快速运行并提供持续的反馈,代码需要以这样一种方式组织:方法、函数和类可以很容易地替换为 mock 和 stub。这种类型的实际代码替换的常用词是**测试替身**。外部依赖性会严重影响执行速度;例如,我们的代码可能需要与数据库通信。通过模仿外部依赖关系,我们能够大幅提高速度。整个单元测试套件的执行应该以分钟为单位,如果不是秒的话。以一种易于模仿和插桩的方式设计代码迫使我们通过应用关注点分离来更好地构建代码。 比速度更重要的是去除外部因素的好处。设置数据库、web 服务器、外部 API 和代码可能需要的其他依赖项既耗时又不可靠。在许多情况下,这些依赖项甚至可能不可用。例如,我们可能需要创建一个与数据库通信的代码,并让其他人创建一个模式。如果没有 mock,我们将需要等待该模式被设置。 diff --git a/docs/test-driven-java-dev/02.md b/docs/test-driven-java-dev/02.md index 499944d..7c7ad7c 100644 --- a/docs/test-driven-java-dev/02.md +++ b/docs/test-driven-java-dev/02.md @@ -647,7 +647,7 @@ public class FriendshipsMongo { # 莫基托 -Mockito 是一个 Java 框架,允许轻松创建测试双精度。 +Mockito 是一个 Java 框架,允许轻松创建测试替身。 渐变相关性如下所示: diff --git a/docs/test-driven-java-dev/06.md b/docs/test-driven-java-dev/06.md index f7251d4..6dc1c66 100644 --- a/docs/test-driven-java-dev/06.md +++ b/docs/test-driven-java-dev/06.md @@ -57,14 +57,14 @@ date.getTime(); // What is the result this method returns? **双重测试**是以下所有类型的通用名称: * 虚拟对象的目的是充当实方法参数的替代品 -* 测试存根可用于将真实对象替换为特定于测试的对象,该对象将所需的间接输入馈送到被测系统中 +* 测试桩可用于将真实对象替换为特定于测试的对象,该对象将所需的间接输入馈送到被测系统中 * **Test Spy**捕获被测**系统**(**SUT**)对另一个组件的间接输出调用,供测试人员稍后验证 * Mock 对象将 SUT 所依赖的对象替换为测试特定的对象,以验证 SUT 是否正确使用该对象 * Fake 对象用更轻的实现替换 SUT 所依赖的组件 如果你感到困惑,它可能会帮助你知道你不是唯一的一个。事情甚至比这更复杂,因为框架或作者之间没有明确的协议,也没有命名标准。术语令人困惑且不一致,前面提到的术语并不是每个人都接受的。 -为了简化,在本书中,我们将使用 Mockito(我们选择的框架)使用的相同名称。这样,您将使用的方法将与您将进一步阅读的术语相对应。我们将继续使用模拟作为其他人可能称之为**测试双精度**的通用术语。此外,我们将使用模拟或间谍术语来指代`Mockito`方法。 +为了简化,在本书中,我们将使用 Mockito(我们选择的框架)使用的相同名称。这样,您将使用的方法将与您将进一步阅读的术语相对应。我们将继续使用模拟作为其他人可能称之为**测试替身**的通用术语。此外,我们将使用模拟或间谍术语来指代`Mockito`方法。 # 模拟对象 @@ -710,7 +710,7 @@ public final void before() throws UnknownHostException { } ``` -现在我们已经用我们认为是默认的行为(调用`saveMove`时返回`true`)对模拟集合进行了存根,我们可以继续编写规范: +现在我们已经用我们认为是默认的行为(调用`saveMove`时返回`true`)对模拟集合进行了插桩,我们可以继续编写规范: ```java @Test diff --git a/docs/test-driven-java-dev/11.md b/docs/test-driven-java-dev/11.md index 1298a3b..ed768ff 100644 --- a/docs/test-driven-java-dev/11.md +++ b/docs/test-driven-java-dev/11.md @@ -21,7 +21,7 @@ 虽然在**TDD**中有**测试**这个词,但这不是主要的好处,也不是目的。TDD 首先是一种更好的代码设计方法的概念。除此之外,我们还将进行一些测试,这些测试应用于持续检查应用程序是否继续按预期工作。 -以前经常提到速度的重要性。虽然我们越来越精通 TDD 可以部分实现这一点,但另一个贡献者是**测试加倍**(模仿、存根、间谍等)。通过这些,我们可以消除对外部依赖关系的需求,如数据库、文件系统、第三方服务等。 +以前经常提到速度的重要性。虽然我们越来越精通 TDD 可以部分实现这一点,但另一个贡献者是**测试加倍**(模仿、桩、间谍等)。通过这些,我们可以消除对外部依赖关系的需求,如数据库、文件系统、第三方服务等。 TDD 的其他好处是什么?文档就是其中之一。由于代码本身是我们正在处理的应用程序的唯一准确且始终是最新的表示形式,因此当我们需要更好地理解一段代码的功能时,使用 TDD 编写的规范(也是代码)是我们应该首先求助的地方。 -- GitLab