
前言
本指南介绍了如何下载、安装、配置和运行用于验证 Jakarta Bean Validation 2.0 实现的兼容性的技术兼容性套件 (TCK)。
Jakarta Bean Validation TCK 构建于 Arquillian 之上,后者是一种便于移植和配置的自动化测试套件,用于在 Jakarta EE 环境中编写单元和集成测试。
Jakarta Bean Validation TCK 根据 Apache Public License 2.0 提供服务。
阅读本指南前的准备工作
Jakarta Bean Validation TCK 基于 Jakarta Bean Validation 规范 2.0。有关规范的信息,请访问 Jakarta Bean Validation 页面。
本指南的组织结构
如果您是首次运行 Jakarta Bean Validation TCK,请全面阅读 简介,获取 TCK 所需的背景信息。在回顾完该材料后,执行后续章节中概述的步骤。
-
简介 概述了通常适用于所有技术兼容性套件 (TCK) 的原则,概述了上诉程序,并介绍了 Jakarta Bean Validation TCK 架构和组件。
-
上诉流程说明如果执行人员想挑战 TCK 中的任何测试时,应该遵循的流程。
-
安装说明如何获取 Jakarta Bean Validation TCK 所需的软件以及如何安装它。
-
报告说明 TCK 测试套件生成的测试报告,并介绍 TCK 审计报告,它是一种工具,用于衡量 TCK 在测试 Jakarta Bean Validation 规范方面和了解测试用例如何与规范相关的完整性。
-
运行 TCK 测试套件详细介绍测试工具的配置和记录如何创建 TCK 运行器,在独立或容器模式下执行 TCK 测试套件。
-
运行签名测试最后记录如何使用 SigTest 工具来确保
javax.validation
包中提供的类型与规范定义的官方 API 签名兼容。
1. 简介
本章说明 TCK 的目的,并确定 Jakarta Bean Validation TCK 的基础元素。
1.1. TCK 简介
TCK 或技术兼容性工具包是任何 JSR 的三个必需部分之一(另外两个是规范文档和参考实现)。TCK 是一组工具和测试,用于验证技术的实现是否符合规范。测试是主要组件,但工具发挥着同样至关重要的作用,为执行测试提供了一个框架和/或一套 SPI。
TCK 中的测试源自书面规范文档中的声明。声明在 XML 文档 (tck-audit.xml
) 中按项目列出,其中每个声明都分配了一个唯一标识符,并具体化为一组自动化测试,这些测试共同验证实现是否符合上述声明,进而符合规范。对于要认证的特定实现,所有必需的测试都必须通过(这意味着必须运行未修改的所提供测试套件)。
TCK 完全与实现无关。它应该通过查阅规范的公共 API 来验证断言。
1.2. 兼容性测试
任何规范的目标都是消除可移植性问题,只要使用实现的程序也符合规范中规定的规则即可。
执行 TCK 是一种兼容性测试形式。必须认识到兼容性测试与产品测试有明显不同。TCK 并不关心健壮性、性能或易用性,因此无法保证实现如何满足这些条件。TCK 可以做的是确保实现与规范相关的准确性。
任何功能的兼容性测试都依赖于一项完整的规范和一项完整的参考实现。参考实现展示了如何通过每项测试,并在开发过程中向执行者提供附加的上下文,以供相应的断言使用。
1.2.1. 兼容性为何重要
对 Java 技术涉足的不同群体而言,Java 平台兼容性出于不同原因而至关重要
-
兼容性测试是 JCP 确保 Java 平台在移植到不同的操作系统和硬件时不会出现碎片化的方式。
-
兼容性测试使开发人员受益,使其能够编写应用程序并将其部署到不同的计算环境,而无需移植。
-
兼容性测试使得应用程序用户能够从不同的来源获取应用程序并放心地对其进行部署。
-
一致性测试通过确保所有 Java 平台移植都具有相同的可靠性程度,使 Java 平台实现者受益。
Jakarta Bean Validation 规范尽力确保为 Jakarta EE 编写的程序兼容,TCK 严格执行该规范制定的规则。
1.3. 关于 Jakarta Bean Validation TCK
Jakarta Bean Validation TCK 被设计为一种可移植、可配置、自动化的测试套件,用于验证 Jakarta Bean Validation 实现的兼容性。该测试套件构建在 TestNG 之上,提供一系列扩展,可在容器内进行 Jakarta EE 构件的运行时程序包和部署(Arquillian)。
套件中的每个测试类别充当一个可部署单元。可部署单元或构件是使用注解以声明式方式定义的。
该声明式方案允许许多测试在 Jakarta Bean Validation 的独立实现中执行,从而提高了开发人员的工作效率。但是,只有当使用容器内执行模式通过所有测试时,实现才是有效的。独立模式仅仅是开发人员的便利之选。
Jakarta Bean Validation TCK 必须在 Jakarta EE 容器中运行的原因在于 Jakarta Bean Validation 本身就是 Jakarta EE 8 的一部分。 |
1.3.1. TCK 组件
Jakarta Bean Validation TCK 包括以下组件
-
测试套件,它是一组 TestNG 测试、TestNG 测试套件描述符和对 Jakarta Bean Validation 及其他软件组件进行配置的补充资源。
-
TCK 审计(
tck-audit.xml
)用于列出 Jakarta Bean Validation 规范中确定的断言。它通过唯一的标识符将断言与测试套件中的测试用例相匹配,并生成覆盖率报告。随 TCK 一起提供审计文档。每项断言都定义了一个对规范文档中章节、节和段落的引用,使实现者能够轻松找到规范文档中支持所测试要素的语言。
-
TCK 文档附有发布说明,标识了版本之间的更新内容。
-
TCK 容器适配器为开发人员在 Jakarta EE 容器之外运行和调试测试提供了便利。
-
设置示例演示了 Maven 和 Ant 设置,以运行 TCK 测试套件
1.3.2. 通过 Jakarta Bean Validation TCK
要通过 Jakarta Bean Validation TCK(这是成为经过认证的 Jakarta Bean Validation 提供程序的要求之一),您需要
-
通过 Jakarta Bean Validation 签名测试(请参阅 运行签名测试),断言使用的 Bean Validation API 的正确性。
-
运行并通过测试套件(请参阅 运行 TCK 测试套件)。必须在 Jakarta EE 8 容器中运行测试,并使用未修改的 TestNG 套件文件通过测试。
用于 Jakarta Bean Validation 规范兼容性测试的指定参考运行时是 Jakarta EE 8 参考实现 (RI),又名 Eclipse GlassFish 5.1+。 |
2. 上诉流程
虽然 Jakarta Bean Validation TCK 严格执行实现与 Jakarta Bean Validation 规范的一致性,但合理地认为,实现者可能会发现验证断言的新方法和/或更好的方法。上诉流程由 Jakarta EE Jakarta EE TCK Process 1.0 定义。
2.1. 可以提交哪些对 TCK 的质疑?
在质疑中可以对任何测试用例(即@Test
方法)、测试用例配置(例如@Deployment
、validation.xml)、测试实体、注释和其他资源提出质疑。
规范做出的断言通常是不可质疑的。规范文档由一个单独的流程控制,对它的质疑应通过向 bean-validation-dev@eclipse.org 发送电子邮件,经由 Jakarta Bean Validation EG 来处理。
2.2. 如何提交这些质疑?
要提交质疑,应针对 Hibernate JIRA 实例中的 BVTCK 创建一个 Bug 类型的新问题。上诉人应仅填写摘要、组件(TCK 质疑)、环境和描述字段。问题相关的任何通信都应添加到问题的评论中,以获得准确的记录。
若要提交 Hibernate JIRA 中的问题,您必须拥有一个(免费的)JIRA 会员帐户。您可以使用在线注册创建一个会员帐户。
2.3. 如何解决挑战?由谁解决?
挑战将由 Bean 验证 TCK 项目主管及时解决,该人由规范主管 Red Hat Inc. 或其指定者指定。上诉人也可通过关注对BVTCK提出的问题,监控流程。
现任 TCK 项目主管列在 Jakarta EE 上的 Bean Validation 项目摘要页 中。
2.4. 如何管理对 TCK 的已接受挑战?
TCK 挑战的工作流程在Jakarta EE TCK 流程 1.0中概述。
定期发布一个更新的 TCK,其中包含因挑战而更改的测试 - 不会添加新测试。实施必须通过更新后的 TCK。此发布流命名为 2.0.x,其中 x 将递增。
此外,TCK 中会添加新测试以改善规范覆盖范围。我们鼓励实施通过此 TCK,但不是必须的。此发布流命名为 2.y.z,其中 y >= 1。
3. 安装
本章解释如何获取 TCK 和支持软件,并提供有关如何在您的系统上安装/提取它的建议。
3.1. 获取软件
您可以通过官方Jakarta Bean 验证主页获取 Jakarta Bean 验证 TCK 项目的版本。Jakarta Bean 验证 TCK 以 ZIP 文件形式分发,其中包含 TCK 构件(测试套件二进制文件和源文件、测试套件描述符、审核源和报告)、/lib
中的 TCK 库依赖项以及 /doc
中的文档。内容应如下所示
artifacts/
changelog.txt
docs/
lib/
license.txt
setup-examples/
src/
readme.md
您还可从 GitHub 下载源代码 - https://github.com/eclipse-ee4j/beanvalidation-tck。
Jakarta Bean 验证参考实现 (RI) 项目的名称是 Hibernate Validator。您可以从 Hibernate Validator下载页获取用作参考实现的 Hibernate Validator 版本。
在运行 Bean 验证 TCK 时不必用 Hibernate 验证器,但可在测试您自己的 Jakarta Bean 验证实现前将其用作熟悉 TCK 的参考。 |
3.2. TCK 环境
TCK 需要以下两个 Java 运行时环境
-
Java 8(包括 JavaFX 实现)
-
Jakarta EE 8 或更高版本(例如 Eclipse GlassFish 5.1+)
如须了解安装运行时的说明,应 참阅供应商说明。
只需提取 TCK 软件的其余部分。建议创建一个专用文件夹来保存所有 Jakarta Bean Validation 相关工件。本指南假设该文件夹称为 jakarta-bean-validation
。将 TCK 分发版中的 src
文件夹解压缩到名为 tck
的子文件夹中,或使用以下 git 命令
git clone git://github.com/eclipse-ee4j/beanvalidation-tck tck
git checkout 2.0.6
您还可以将完整的 Hibernate Validator 源检出到 ri
子文件夹中。这将允许您针对 Hibernate Validator 运行 TCK。
git clone git://github.com/hibernate/hibernate-validator.git ri
git checkout 6.2.0.Final
生成的文件夹结构如下所示:
jakarta-bean-validation/
ri/
tck/
现在让我们来看看 TCK 中的一个具体测试,即 ConstraintInheritanceTest
(位于 tck/tests/src/main/java/org/hibernate/beanvalidation/tck/tests/constraints/inheritance/ConstraintInheritanceTest.java
)
package org.hibernate.beanvalidation.tck.tests.constraints.inheritance;
import static org.hibernate.beanvalidation.tck.util.ConstraintViolationAssert.assertCorrectConstraintTypes;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import javax.validation.ConstraintViolation;
import javax.validation.constraints.DecimalMin;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import javax.validation.metadata.BeanDescriptor;
import javax.validation.metadata.ConstraintDescriptor;
import javax.validation.metadata.PropertyDescriptor;
import org.hibernate.beanvalidation.tck.beanvalidation.Sections;
import org.hibernate.beanvalidation.tck.tests.AbstractTCKTest;
import org.jboss.arquillian.container.test.api.Deployment;
import org.jboss.shrinkwrap.api.spec.WebArchive;
import org.jboss.test.audit.annotations.SpecAssertion;
import org.jboss.test.audit.annotations.SpecVersion;
import org.testng.annotations.Test;
/**
* @author Hardy Ferentschik
*/
@SpecVersion(spec = "beanvalidation", version = "2.0.0")
public class ConstraintInheritanceTest extends AbstractTCKTest {
@Deployment
public static WebArchive createTestArchive() {
return webArchiveBuilder()
.withTestClassPackage( ConstraintInheritanceTest.class )
.build();
}
@Test
@SpecAssertion(section = Sections.CONSTRAINTDECLARATIONVALIDATIONPROCESS_INHERITANCE, id = "b")
public void testConstraintsOnSuperClassAreInherited() {
BeanDescriptor beanDescriptor = getValidator().getConstraintsForClass( Bar.class );
String propertyName = "foo";
assertTrue( beanDescriptor.getConstraintsForProperty( propertyName ) != null );
PropertyDescriptor propDescriptor = beanDescriptor.getConstraintsForProperty( propertyName );
Annotation constraintAnnotation = propDescriptor.getConstraintDescriptors()
.iterator()
.next().getAnnotation();
assertTrue( constraintAnnotation.annotationType() == NotNull.class );
}
@Test
@SpecAssertion(section = Sections.CONSTRAINTDECLARATIONVALIDATIONPROCESS_INHERITANCE, id = "a")
@SpecAssertion(section = Sections.CONSTRAINTDECLARATIONVALIDATIONPROCESS_INHERITANCE, id = "b")
public void testConstraintsOnInterfaceAreInherited() {
BeanDescriptor beanDescriptor = getValidator().getConstraintsForClass( Bar.class );
String propertyName = "fubar";
assertTrue( beanDescriptor.getConstraintsForProperty( propertyName ) != null );
PropertyDescriptor propDescriptor = beanDescriptor.getConstraintsForProperty( propertyName );
Annotation constraintAnnotation = propDescriptor.getConstraintDescriptors()
.iterator()
.next().getAnnotation();
assertTrue( constraintAnnotation.annotationType() == NotNull.class );
}
@Test
@SpecAssertion(section = Sections.CONSTRAINTDECLARATIONVALIDATIONPROCESS_INHERITANCE, id = "a")
@SpecAssertion(section = Sections.CONSTRAINTDECLARATIONVALIDATIONPROCESS_INHERITANCE, id = "c")
public void testConstraintsOnInterfaceAndImplementationAddUp() {
BeanDescriptor beanDescriptor = getValidator().getConstraintsForClass( Bar.class );
String propertyName = "name";
assertTrue( beanDescriptor.getConstraintsForProperty( propertyName ) != null );
PropertyDescriptor propDescriptor = beanDescriptor.getConstraintsForProperty( propertyName );
List<Class<? extends Annotation>> constraintTypes = getConstraintTypes( propDescriptor.getConstraintDescriptors() );
assertEquals( constraintTypes.size(), 2 );
assertTrue( constraintTypes.contains( DecimalMin.class ) );
assertTrue( constraintTypes.contains( Size.class ) );
}
@Test
@SpecAssertion(section = Sections.CONSTRAINTDECLARATIONVALIDATIONPROCESS_INHERITANCE, id = "a")
@SpecAssertion(section = Sections.CONSTRAINTDECLARATIONVALIDATIONPROCESS_INHERITANCE, id = "c")
public void testConstraintsOnSuperAndSubClassAddUp() {
BeanDescriptor beanDescriptor = getValidator().getConstraintsForClass( Bar.class );
String propertyName = "lastName";
assertTrue( beanDescriptor.getConstraintsForProperty( propertyName ) != null );
PropertyDescriptor propDescriptor = beanDescriptor.getConstraintsForProperty( propertyName );
List<Class<? extends Annotation>> constraintTypes = getConstraintTypes( propDescriptor.getConstraintDescriptors() );
assertEquals( constraintTypes.size(), 2 );
assertTrue( constraintTypes.contains( DecimalMin.class ) );
assertTrue( constraintTypes.contains( Size.class ) );
}
@Test
@SpecAssertion(section = Sections.CONSTRAINTDECLARATIONVALIDATIONPROCESS_VALIDATIONROUTINE, id = "a")
public void testValidationConsidersConstraintsFromSuperTypes() {
Set<ConstraintViolation<Bar>> violations = getValidator().validate( new Bar() );
assertCorrectConstraintTypes(
violations,
DecimalMin.class, DecimalMin.class, ValidBar.class, //Bar
NotNull.class, Size.class, ValidFoo.class, //Foo
NotNull.class, Size.class, ValidFubar.class //Fubar
);
}
private List<Class<? extends Annotation>> getConstraintTypes(Iterable<ConstraintDescriptor<?>> descriptors) {
List<Class<? extends Annotation>> constraintTypes = new ArrayList<Class<? extends Annotation>>();
for ( ConstraintDescriptor<?> constraintDescriptor : descriptors ) {
constraintTypes.add( constraintDescriptor.getAnnotation().annotationType() );
}
return constraintTypes;
}
}
每个测试类都作为单个工件处理(因此,对类使用了 @Deployment
注释)。在大多数测试中,创建的工件是通过 WebArchiveBuilder
构建的标准 Web 应用程序存档,进而构建 TCK 本身的帮助程序类,减轻了工件的创建。用 @Test
注释的所有方法都是要运行的实际测试。最后但并非最不重要的是,我们看到了使用 @SpecAssertion
注释,该注释创建了 tck-audit.xml 文档与实际测试之间的链接(请参见 TCK Primer)。
-
安装 Maven。您可以在 Maven 官方网站 上找到有关如何在 Maven 3 上安装 Maven 的文档。
-
切换到
ri/tck-runner
目录。 -
接下来,指示 Maven 运行 TCK
mvn test -Dincontainer
-
TestNG 将通过 Maven 报告运行结果,并在控制台上报告任何故障。详细信息可在
target/surefire-reports/TestSuite.txt
中找到。
4. 报告
本章涵盖了 TCK 可以生成的两种类型的报告,断言覆盖率报告和测试执行结果。
4.1. Jakarta Bean Validation TCK 覆盖率报告
规范可以分解为一组断言,这些断言定义了软件的行为。本节介绍 Bean Validation TCK 覆盖率报告,该报告记录了 Jakarta Bean Validation 规范文档中标识的断言与 TCK 测试套件中的测试之间的关系。
此报告的结构由断言文档控制,因此我们将从那里开始。
4.1.1. Jakarta Bean Validation TCK 断言
Jakarta Bean Validation TCK 开发人员分析了 Jakarta Bean Validation 规范文档,并确定了每一章中存在的断言。以下是第 2.1 节中找到的一个此类断言的示例:“每个约束注释都必须定义类型为 String 的消息元素”
断言在 Jakarta Bean 验证 TCK 分发版本中的 XML 文件 tck-audit.xml
中进行列示。每个断言通过其所处规范文档中的章节进行标识并分配一个唯一的段落标识符,以进一步缩小断言位置范围。继续之前的示例,以上显示的断言在 tck-audit.xml
文件中使用以下 XML 片段进行列示
<section id="constraintsdefinitionimplementation-constraintdefinition-properties-message" title="message" level="4">
...
<!-- 3.1.1.1 - CONSTRAINTSDEFINITIONIMPLEMENTATION_CONSTRAINTDEFINITION_PROPERTIES_MESSAGE -->
<assertion id="a">
<text>Every constraint annotation must define a message element of type String.</text>
</assertion>
...
</section>
Jakarta Bean 验证 TCK 的策略是编写一项测试,在执行对实现时验证此断言。测试用例(使用 @Test
进行注释的方法)与断言通过 @org.jboss.test.audit.annotations.SpecAssertion
进行批注关联,如下所示
@Test(expectedExceptions = ConstraintDefinitionException.class)
...
@SpecAssertion(section = Sections.CONSTRAINTSDEFINITIONIMPLEMENTATION_CONSTRAINTDEFINITION_PROPERTIES_MESSAGE, id = "a")
...
public void testConstraintDefinitionWithoutMessageParameter() {
getValidator().validate( new DummyEntityNoMessage() );
fail( "The used constraint does not define a message parameter. The validation should have failed." );
}
为了帮助评估这些断言的覆盖率分布,TCK 提供了详细的覆盖率报告。此报告还有助于帮助实现者将测试与规范中的语言进行匹配,以支持正在测试的行为。
4.1.2. 覆盖率报告
覆盖率报告是作为 TCK 项目构建的一部分生成的 HTML 报告。具体来说,它是由批注处理器生成的,该处理器附加到 TCK 测试套件中类的编译上。您可以在 GitHub 存储库中找到此处理器的源代码,网址为 https://github.com/jboss/jboss-test-audit 该报告写入文件 target/coverage-report/coverage-beanvalidation.html
。报告本身分为五个部分
-
章节总结 - 列示规范文档中的章节(包含断言)以及断言总数、测试和覆盖率百分比。
-
章节总结 - 列示规范文档中的章节(包含断言)以及断言总数、测试和覆盖率百分比。
-
覆盖率详情 - 每个断言,以及对其进行覆盖的测试(如果存在)。
-
不匹配的测试 - 列示没有匹配断言的测试(在 TCK 开发期间十分有用)。
-
未获得版本控制的测试 - 列示没有在测试类中执行
@SpecVersion
批注的测试(在 TCK 开发期间十分有用)。
覆盖率报告使用颜色编码指示断言或断言组的状态。状态代码如下
-
已覆盖 - 此断言存在测试
-
未覆盖 - 此断言不存在测试
-
未实现 - 存在测试,但尚未实现
-
不可测试 - 断言已被视为不可测试,通常会提供备注进行解释
由于 tck-audit.xml
文档中提供了原因并在覆盖率报告中进行说明,某些断言无法进行测试。
覆盖率报告不提供关于测试是否通过的任何指示。这就是 TestNG 报告发挥作用的地方。
4.2. 测试报告
您现在已经知道,Jakarta Bean Validation TCK 测试套件实际上就是一个 TestNG 测试套件。这意味着执行 Jakarta Bean Validation TCK 测试套件会生成 TestNG 生成的所有相同报告。本节将介绍这些报告并向您展示如何查找各个报告。
4.2.1. Maven、Surefire 和 TestNG
在 TCK 执行者的 Maven 测试阶段执行 Jakarta Bean Validation TCK 测试套件时,TestNG 会通过 Maven Surefire 插件间接调用。Surefire 是一个测试执行抽象层,能够执行为 JUnit、TestNG 和其他支持的测试框架编写的各种测试。
这有什么意义?这意味着两件事。首先,这意味着您将在命令行中获得测试运行的摘要。以下是使用独立模式运行测试时生成的结果。
-------------------------------------------------------
T E S T S
-------------------------------------------------------
Running TestSuite
Tests run: 976, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 35.288 sec - in TestSuite
Results :
Tests run: 976, Failures: 0, Errors: 0, Skipped: 0
当按照 Bean Validation TCK 的要求使用容器内模式运行测试时,执行的测试数、执行时间和输出将有所不同。 |
如果对 Surefire 进行补充的 Maven 报告插件配置得当,那么 Maven 还将生成一个常规 HTML 测试结果报告。该报告将写入 TCK 执行者的 target/surefire-reports
目录中的 test-report.html 文件中。它显示了运行的测试数、失败的测试数,以及测试运行的成功率。
5. 运行 TCK 测试套件
本章介绍了如何在给定的 Jakarta EE 容器中针对给定的 Jakarta Bean Validation 供应商运行和配置 TCK 构件。如果您现在还没有熟悉Arquillian 文档,这是了解它的好时机。它将使您对以下部分中描述的不同部分有更深入的了解。
5.1. 设置示例
TCK 发行版带有一个 setup-examples
目录,其中包含用于运行 TCK 的两个示例项目。如果您已按照安装中的说明进行操作,您将在 jakarta-bean-validation/tck/setup-examples
下找到该目录。两个设置都将 Hibernate 验证器用作 Jakarta Bean 验证提供程序并将 Eclipse GlassFish 5.1+ 用作 Jakarta EE 容器。但是,其中一个使用Maven作为构建工具运行 TCK,另一个使用Ant。根据您想要使用哪个示例,您需要安装相应的构建工具。
每个示例都带有一个 readme.md
,其中包含使用此设置的先决条件、如何针对 Hibernate 验证器和 Eclipse GlassFish 运行 TCK 的方法。setup-examples
中的 readme 本身包含有关更改内容以使用不同的 Jakarta Bean 验证提供程序和 Jakarta EE 容器的信息。
以下章节包含一些关于 TCK 一般结构的更多信息,这将使您对简单的自述文件有更深入的了解。
5.2. 配置 TestNG 以执行 TCK
Jakarta Bean Validation 测试工具构建在 TestNG 之上,TestNG 负责选择要执行的测试、执行顺序以及报告结果。有关 TestNG 的详细文档,请访问 testng.org。
TCK 分发版中提供的 tck-tests.xml
工件必须由 TestNG(在 TestNG 文档中描述为“使用 testng.xml
文件”)运行,且未经修改以便实现通过 TCK。当然,出于测试目的,对文件进行修改是可以的(另请参阅 TestNG documentation)
<suite name="Jakarta-Bean-Validation-TCK" verbose="1">
<test name="Jakarta-Bean-Validation-TCK">
<method-selectors>
<method-selector>
<selector-class name="org.hibernate.beanvalidation.tck.util.IntegrationTestsMethodSelector"/>
</method-selector>
</method-selectors>
<packages>
<package name="org.hibernate.beanvalidation.tck.tests"/>
</packages>
</test>
</suite>
TestNG 提供了广泛的报告信息。报告将采用不同的格式,具体取决于您使用的构建工具或 IDE。有关更多信息,请咨询 TestNG 文档和工具文档。
5.3. 选择 ValidationProvider
要运行 Jakarta Bean Validation TCK,您必须进行的最重要的配置是指定要针对其运行测试的 ValidationProvider
。为此,您需要将 Java 系统属性 validation.provider
设置为 ValidationProvider
的完全限定类名称。在 Maven 中,这是通过 maven-surefire-plugin 的 systemProperties
配置选项完成的,而在 Ant testng 任务中使用 sysproperty
。此系统属性将由 org.hibernate.beanvalidation.tck.util.TestUtil
提取,后者将实例化正在测试的 Validator
。这意味着测试工具不依赖于服务提供程序机制来实例化正在测试的 Jakarta Bean Validation 提供程序,部分原因是此选择机制也在测试中。
5.4. 选择 DeployableContainer
在设置好 ValidationProvider
后,您需要为正确的 DeployableContainer
做出选择。Arquillian 会选择用于部署测试存档并使用 Java 服务提供程序机制协商测试执行的容器。具体来说,Arquillian 会在类路径中查找 DeployableContainer
SPI 的实现。设置示例使用的是远程 Eclipse GlassFish 容器适配器,这意味着 Arquillian 会尝试将测试工件部署到指定的远程 Eclipse GlassFish 实例上,远程运行测试并向当前 JVM 报告结果。远程容器的安装目录在示例构建文件中通过 container.home
属性进行指定。为了更加轻松地开发、调试或测试 TCK,我们提供了作为发行版一部分的 JVM 内适配器(beanvalidation-standalone-container-adapter-2.0.6.jar
)。使用该适配器时,测试不会在远程 Jakarta EE 容器中执行,而是在当前 JVM 中执行。这可以进行轻松而快速的调试。但是,有些测试只能在 Jakarta EE 容器中运行,并且会在该 JVM 执行模式中失败。可以通过将 excludeIntegrationTests
属性设置为 true 来排除这些测试。
此适配器还可用作 GAV org.hibernate.beanvalidation.tck:beanvalidation-standalone-container-adapter:2.0.6.
下的 Maven 工件。您可以参阅 Hibernate Validator 的 tck-runner 模块中的 pom.xml
(在 jakarta-bean-validation/ri/tck-runner
目录中,如果您按照“安装”中的说明进行操作),了解它的使用方式。
5.5. arquillian.xml
配置拼图中的下一部分是 arquillian.xml
。此 XML 文件需要位于类路径的根目录中,并用于将其他选项传递到所选容器。我们来看一个示例
<arquillian xmlns="https://jboss.com.cn/schema/arquillian" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jboss.com.cn/schema/arquillian
https://jboss.com.cn/schema/arquillian/arquillian_1_0.xsd">
<defaultProtocol type="Servlet 3.0"/>
<engine>
<property name="deploymentExportPath">target/artifacts</property>
</engine>
<container qualifier="incontainer" default="true">
<configuration>
<property name="glassFishHome">@CONTAINER.HOME@</property>
<property name="adminHost">localhost</property>
<property name="adminPort">4848</property>
<property name="debug">true</property>
</configuration>
</container>
</arquillian>
最重要的容器配置选项是协议类型,它决定了 Arquillian 如何与所选容器通信。最常见的类型是 Servlet 3.0
和 Local
。前者用于连接到远程容器时使用,而后者用于 JVM 内模式时使用。
另一个有趣的属性是 deploymentExportPath
,它不是必需的,并指示 Arquillian 将测试工件转储到磁盘上的指定目录。在调试测试失败时,检查已部署的工件非常有用。
Jakarta Bean Validation 规范要求支持 JavaFX,只要类路径中提供了 JavaFX 即可。
虽然 JavaFX 包含在 Oracle JDK 中,并且一些其他 JDK 包含 OpenJFX,但它可能并不包含在所有 JDK 中。
在容器环境中提供 JavaFX 可能也不是直接的。
出于这些原因,在使用默认选项运行 TCK 时,JavaFX 测试处于禁用状态。
强烈建议至少在一个允许测试 JavaFX 支持的配置中运行 TCK。
使用此配置,可以通过将 -DincludeJavaFXTests=true
选项传给 TCK 来启用 JavaFX 测试。
6. 运行签名测试
通过 TCK 的实现的要求之一是通过 Jakarta Bean Validation 签名测试。本节介绍如何将此测试作为 Maven 构建的一部分针对您的实现运行该测试。
6.1. 执行签名检查
此 TCK 中捆绑的签名文件是使用 SigTest Maven 插件 创建的。可以使用同一插件来运行签名测试以检查是否存在任何不兼容的情况。我们来看一下如何将它作为 Maven 构建的一部分来完成。请注意,除了您希望测试的 API 工件之外,此项目不得声明任何依赖项。
在运行实际测试之前,您需要先获取签名文件。它打包在 beanvalidation-tck-tests 工件中,因此我们可以使用 maven-dependency-plugin
的 unpack
目标获取它,如下所示
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>3.0.0</version>
<executions>
<execution>
<id>copy-tck-bv-api-signature-file</id>
<phase>generate-test-sources</phase>
<goals>
<goal>unpack</goal>
</goals>
<configuration>
<artifactItems>
<artifactItem>
<groupId>org.hibernate.beanvalidation.tck</groupId>
<artifactId>beanvalidation-tck-tests</artifactId>
<version>${beanvalidation-tck-tests.version}</version>
<type>jar</type>
<overWrite>false</overWrite>
</artifactItem>
</artifactItems>
<!-- We just need the signature file and nothing else -->
<includes>**/*.sig</includes>
<outputDirectory>${project.build.directory}/api-signature</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
要实际运行签名测试,可以使用 sigtest-maven-plugin
的 check
目标。上面的插件配置将签名文件放置到项目构建目录的 api-signature 子目录中。将文件放在那里后,它可以通过 sigtest-maven-plugin
插件的 sigfile
参数引用,如下所示
<plugin>
<groupId>org.netbeans.tools</groupId>
<artifactId>sigtest-maven-plugin</artifactId>
<version>1.0</version>
<executions>
<execution>
<goals>
<goal>check</goal>
</goals>
</execution>
</executions>
<configuration>
<packages>javax.validation,javax.validation.bootstrap,javax.validation.constraints,
javax.validation.constraintvalidation,javax.validation.executable,javax.validation.groups,
javax.validation.metadata,javax.validation.spi,javax.validation.valueextraction
</packages>
<sigfile>${project.build.directory}/api-signature/validation-api-java8.sig</sigfile>
</configuration>
</plugin>
6.2. 强制签名测试失败
如果您想验证签名测试是否正确运行,请在本地文件系统上的某个位置复制签名文件并修改它。例如,让我们将 javax.validation.constraints.Max
的 value()
更改为 val()
,这将导致 SigTest 失败。
修改签名文件后,更新 sigtest-maven-plugin
的 sigfile
参数以指向修改后的文件
<sigfile>${path_to_folder_containing_your_modified_signature_file}/validation-api-java8.sig</sigfile>
如果一切正常,则在您项目的 mvn sigtest:check
上运行时,您应该会看到类似于以下内容的错误
[INFO] SignatureTest report
Base version: 2.0.0-SNAPSHOT
Tested version: 2.0.0-SNAPSHOT
Check mode: bin [throws removed]
Constant checking: on
Class javax.validation.constraints.Max
"E2.7 - Removing member from annotation type" : method public abstract long javax.validation.constraints.Max.val()