我认为,您必须在此处与 H2 或其他内存数据库进行集成测试。正如您所说,如果您只使用模拟,您可以看到对象之间如何交互,但您永远不知道您会得到什么结果列表。
我在同一页面上,不是 Restriction 左右,而是 JPA 2.0 CriteriaQuery 和 CriteriaBuilder。我在持久层中构建了复杂的谓词,最后,我发现使用 db 中的数据进行测试变得不可避免,因为没有人知道 SQL 中的最终查询是什么。我决定在系统的这一部分需要集成,所以我就去做了。
最后构建这样的测试并不难。你需要 H2 依赖,像这样的persistence.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!-- For H2 database integration tests. -->
<!-- For each int test, define unique name PU in this file and include SQL files in different paths. -->
<persistence version="2.0"
xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
<persistence-unit name="test-item-history-service-bean" transaction-type="RESOURCE_LOCAL">
<provider>org.hibernate.ejb.HibernatePersistence</provider> <!-- mind here: must be this! cannot be JPA provider! -->
<class>com.data.company.Company</class>
<class>com.data.company.ItemHistory</class>
<exclude-unlisted-classes>true</exclude-unlisted-classes>
<properties>
<property name="javax.persistence.jdbc.url"
value="jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;MODE=Oracle;INIT=RUNSCRIPT FROM 'src/test/resources/db/item-history/create.sql'\;RUNSCRIPT FROM 'src/test/resources/db/item-history/populate.sql'"/>
<property name="javax.persistence.jdbc.driver" value="org.h2.Driver"/>
<property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/>
<property name="hibernate.id.new_generator_mappings" value="true"/>
<property name="hibernate.hbm2ddl.auto" value="update"/> <!-- mind here! Can only be "update"! "create-drop" will prevent data insertion! -->
<property name="hibernate.format_sql" value="true"/>
<property name="hibernate.show_sql" value="true"/>
<property name="hibernate.default_schema" value="main"/>
</properties>
</persistence-unit>
</persistence>
(请注意上面XML中的注释,我花了一周的时间终于解决了)
关于提供者的注意事项:请参阅此处:How to configure JPA for testing in Maven
而在两个sql文件中,你CREATE TABLE ...和INSERT INTO ...。插入您喜欢的任何内容,因为数据是测试的一部分。
还有,像这样的测试:
/**
* Integration tests with in-memory H2 DB. Created because:
* - In-memory DB are relatively cheap to create and destroy, so these tests are quick
* - When using {@link javax.persistence.criteria.CriteriaQuery}, we inevitably introduce complex perdicates'
* construction into persistence layer, which is a drawback of it, but we cannot trade it with repetitive queries
* per id, which is a performance issue, so we need to find a way to test it
* - JBehave tests are for the user story flows, here we only want to check with the complex queries, certain
* records are returned; performance can be verified in UAM.
*/
@RunWith(MockitoJUnitRunner.class)
public class ItemHistoryPersistenceServiceBeanDBIntegrationTest {
private static EntityManagerFactory factory;
private EntityManager realEntityManager;
private ItemHistoryPersistenceServiceBean serviceBean;
private Query<String> inputQuery;
@BeforeClass
public static void prepare() {
factory = Persistence.createEntityManagerFactory("test-item-history-service-bean");
}
@Before
public void setup() {
realEntityManager = factory.createEntityManager();
EntityManager spy = spy(realEntityManager);
serviceBean = new ItemHistoryPersistenceServiceBean();
try {
// inject the real entity manager, instead of using mocks
Field entityManagerField = serviceBean.getClass().getDeclaredField("entityManager");
entityManagerField.setAccessible(true);
entityManagerField.set(serviceBean, spy);
} catch (NoSuchFieldException | IllegalAccessException e) {
throw new AssertionError("should not reach here");
}
inputQuery = new Query<>();
inputQuery.setObjectId("itemId");
}
@After
public void teardown() {
realEntityManager.close();
}
@Test
public void findByIdAndToken_shouldReturnRecordsMatchingOnlyTokenFilter() {
try {
// when
List<ItemHistory> actual = serviceBean.findByIdAndToken(inputQuery);
// then
assertEquals(2, actual.size());
assertThat(actual.get(0).getItemPackageName(), anyOf(is("orgId 3.88"), is("orgId 3.99.3")));
assertThat(actual.get(1).getItemPackageName(), anyOf(is("orgId 3.88"), is("orgId 3.99.3")));
} catch (DataLookupException e) {
throw new AssertionError("should not reach here");
}
}
}