该帖子与+ Aslak Knutsen ( @aslakknutsen )一起撰写。
JPA类型转换器为定义实体属性如何持久存储到数据库提供了一种简便的方法。 您可以使用它们来实现许多不同的功能,例如,如上一篇文章中所示:加密数据: 如何使用JPA Type Converter加密数据 。
但是编写类型转换器还不够。 我们还需要确保其正常运行。
通常,有两种方法可以测试类型转换器。 我们可以编写一个单元测试来检查转换是否正常工作。 但是单元测试不会对隔离的类进行测试,而无需将其放入实际的执行环境中。 这意味着我们仍然不知道转换器是否可以在我们的应用程序之一中工作。 如果一切设置正确,则持久性提供程序将在写入数据库和从数据库读取之后调用转换器。 因此,我们还需要检查持久性提供程序是否调用了类型转换器,以及在这种情况下一切是否正常。 我们需要在要用于我们的应用程序的容器内测试转换器。
我们将看看如何使用Arquillian及其持久性扩展来完成此任务。
关于Arqillian
如果您已经熟悉Arquillian,则可以跳过此部分。 对于到目前为止从未与Arquillian合作的所有人,我只想提供一些基本信息。 您可以在《 Arquillian 入门指南》中找到更详细的描述。
Arquillian是用于容器测试的测试框架。 想法是不要模拟您要使用的容器,而是在其中测试您的代码。 这提供了一个优势,即您可以测试代码是否还可以在执行环境中工作,而不仅是在模拟测试场景中也可以工作。 Arquillian提供了许多功能来管理容器,注入所需的资源(如EJB或EntityManager),并使您的工作变得更加轻松。
Arquillian测试由junit执行。 这很棒,因为您可以在可以执行junit测试的任何地方使用它们。 这意味着在您的IDE中,作为构建过程的一部分,您可以在CI服务器上的任何地方。
被测对象
下面的代码片段显示了此示例中的测试对象。 它是一个类型转换器,用于加密和解密String属性。 在写入数据库和从数据库读取之后,持久性提供程序将调用转换器。 如果您想了解有关此类型转换器如何工作的更多信息,请查看我的相关文章 。
@Converter
public class CryptoConverter implements AttributeConverter<String, String> {
private static final String ALGORITHM = "AES/ECB/PKCS5Padding";
private static final byte[] KEY = "MySuperSecretKey".getBytes();
@Override
public String convertToDatabaseColumn(String ccNumber) {
// do some encryption
Key key = new SecretKeySpec(KEY, "AES");
try {
Cipher c = Cipher.getInstance(ALGORITHM);
c.init(Cipher.ENCRYPT_MODE, key);
return Base64.encodeBytes(c.doFinal(ccNumber.getBytes()));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@Override
public String convertToEntityAttribute(String dbData) {
// do some decryption
Key key = new SecretKeySpec(KEY, "AES");
try {
Cipher c = Cipher.getInstance(ALGORITHM);
c.init(Cipher.DECRYPT_MODE, key);
return new String(c.doFinal(Base64.decode(dbData)));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
设定
在开始编写测试之前,我们需要定义一些依赖关系。 我将仅展示如何配置此示例所需的依赖项。 如果您尚未为项目设置Arquillian测试,则必须做更多的工作。 请查看“ 入门指南”以了解如何为您的项目设置Arquillian。 不要害怕,没有太多的事情要做。
如下面的代码片段所示,我们将使用JUnit 4.11 , Arquillian 1.1.3.Final , Arquillian Persistence Extension 1.0.0.Alpha7和WildFly Application Server 8.1.0.Final 。
<?xml version="1.0" encoding="UTF-8"?>
<project
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
...
<properties>
<version.junit>4.11</version.junit>
<version.arquillian>1.1.3.Final</version.arquillian>
<version.arquillian_persistence>1.0.0.Alpha7</version.arquillian_persistence>
<version.wildfly>8.1.0.Final</version.wildfly>
</properties>
<dependencyManagement>
<dependencies>
...
<dependency>
<groupId>org.jboss.arquillian</groupId>
<artifactId>arquillian-bom</artifactId>
<version>${version.arquillian}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
...
<dependency>
<groupId>javax.enterprise</groupId>
<artifactId>cdi-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${version.junit}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jboss.arquillian.junit</groupId>
<artifactId>arquillian-junit-container</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jboss.arquillian.extension</groupId>
<artifactId>arquillian-persistence-dbunit</artifactId>
<version>${version.arquillian_persistence}</version>
<scope>test</scope>
</dependency>
</dependencies>
编写测试
要设置测试环境,我们需要做两件事。 首先,我们需要告诉junit该测试应作为junit测试执行。 这是通过@RunWith(Arquillian.class)
。
此外,我们需要创建测试部署,该部署将部署到容器中。 因此,我们需要实现至少一种方法,并使用@Deployment
对其进行@Deployment
。 正如您在以下代码片段中所看到的,我们使用ShrinkWrap创建jar存档部署。 存档包含CreditCard
实体, CryptoConverter
类型转换器和测试类。 无需包括任何实现业务逻辑的EJB或其他类。 我们可以将EntityManager注入我们的测试用例中,并直接使用它来持久化和读取实体。 稍后我们将对其进行更详细的介绍。
另外,我们需要添加一些清单资源来创建一个持久性单元,注册类型转换器并添加一个空的beans.xml来激活CDI。 请查看入门指南,以获取有关ShrinkWrap和创建部署的更多信息。
@RunWith(Arquillian.class)
public class TestCryptoConverter {
@Deployment
public static JavaArchive createDeployment() {
return ShrinkWrap
.create(JavaArchive.class)
.addClasses(CreditCard.class, CryptoConverter.class,
TestCryptoConverter.class)
.addAsManifestResource("META-INF/persistence.xml",
"persistence.xml")
.addAsManifestResource("META-INF/orm.xml", "orm.xml")
.addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml");
}
完成之后,我们可以开始编写测试用例。 首先,我们将保留一个CreditCard
实体并检查信用卡号是否已由CryptoConverter
加密。 因此,我们注入EntityManager,创建一个CreditCard
实体,并将其传递给EntityManager的persist方法。 持久化数据的验证由Arquillian持久性扩展完成。 我们只需要定义我们希望写入数据库的数据即可。 预期数据在cc.yml文件中定义,该文件在@ShouldMatchDataSet
批注中引用。 由于id属性是由数据库生成的,因此我们希望将其从验证中排除。 这可以通过在注释的excludeColumns属性中引用它来完成。
@PersistenceContext
private EntityManager em;
@Test
@ShouldMatchDataSet(value = "data/cc.yml", excludeColumns = "id")
public void testEncryption() {
CreditCard cc = new CreditCard();
cc.setName("My Name");
cc.setCcNumber("123456789");
this.em.persist(cc);
}
cc.yml包含以下信息。
CreditCard:
- id: 1
name: My Name
ccNumber: egFfkhd8cRh82tvsh3VVUg==
在第二个测试中,我们将检查是否可以在数据库中搜索具有给定信用卡号的CreditCard
实体。 因此,我们使用@UsingDataSet
批注为cc.yml文件中定义的数据播种数据库。 现在,我们可以使用注入的EntityManager调用命名查询,以使用给定的编号搜索CreditCard
实体。
@Test
@UsingDataSet("data/cc.yml")
public void testRead() {
CreditCard cc = this.em
.createNamedQuery(CreditCard.BY_NUMBER, CreditCard.class)
.setParameter("number", "123456789").getSingleResult();
Assert.assertEquals("My Name", cc.getName());
}
结论
我们使用Arquillian和Arquillian Persistence扩展来测试JPA类型转换器。 因此,我们注入了EntityManager并使用了@ShouldMatchData
和@UsingDataSet
批注来验证数据库并使用yml文件设置种子。
如果您想自己尝试,可以在github上找到源代码。
您可以通过调用以下命令来运行它:git clone https://github.com/thjanssen/JPA2.1.git && cd JPA2.1 / CryptoConverter && mvn测试。
使用Arquillian测试Java EE应用程序有什么经验? 请对此发表评论。
想要了解有关Arquillian的更多信息,请参阅Arquillian Guides: http ://arquillian.org/guides/