Java 基于 JPA 的 JUnit 测试最佳实践

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1234763/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-12 01:11:39  来源:igfitidea点击:

JPA-based JUnit Test Best Practices

javamaven-2jpajunittestng

提问by mlaccetti

This is a bit of an odd question, but it has been bothering me for a few months now. I have built a JPA-based web application using Wicket + Hibernate (built with Maven), and want to test the DAO layer directly. I created a specific src/test/resources/META-INF/persistence.xml file that I used for testing, but have been running into conflicts with WTP and the like. To get around these issues, I created a separate test project where the unit tests live. Is there a better way to manage unit tests for a JPA project without having duels between persistence files?

这是一个有点奇怪的问题,但它已经困扰我几个月了。我已经使用 Wicket + Hibernate(使用 Maven 构建)构建了一个基于 JPA 的 Web 应用程序,并且想直接测试 DAO 层。我创建了一个特定的 src/test/resources/META-INF/persistence.xml 文件,用于测试,但遇到了与 WTP 等的冲突。为了解决这些问题,我创建了一个单独的测试项目,其中单元测试存在。有没有更好的方法来管理 JPA 项目的单元测试,而无需在持久性文件之间进行决斗?

Addendum: Would other test frameworks (TestNG, for example) make this any easier?

附录:其他测试框架(例如 TestNG)会使这更容易吗?

回答by Aaron Digulla

You may want to try mockito. The test works like this:

您可能想尝试mockito。测试是这样工作的:

You use mockito to "implement" EntityManager. Instead of the real code, you use the methods of mockito to say "if the application calls getReference(), then return this object". In the background, mockito will create a proxy instance which intercepts the Java method calls and returns the values which you specify. Calls to other methods will return null.

您使用 mockito 来“实现” EntityManager。代替真正的代码,您使用 mockito 的方法说“如果应用程序调用getReference(),则返回此对象”。在后台,mockito 将创建一个代理实例,它拦截 Java 方法调用并返回您指定的值。对其他方法的调用将返回null

Mocking things like createQuery()works the same way but you first need to create a mockup of Queryand then use the same approach as in getReference()(return the query mockup).

createQuery()模拟类似的工作方式相同,但您首先需要创建一个模型,Query然后使用与getReference()(返回查询模型)相同的方法。

Since you don't use a real EM, you don't need a real persistence.xml.

由于您不使用真正的 EM,因此您不需要真正的persistence.xml.

A much more simple solution would be if you could set some property to change the name of the persistence.xmlfile but I don't think that this is possible.

一个更简单的解决方案是,如果您可以设置一些属性来更改persistence.xml文件名,但我认为这是不可能的。

Some other links that may help:

其他一些可能有帮助的链接:

回答by topchef

We use dual persistence.xmlfiles for production and test runtimes but it is a classpath related issue only (we use Eclipse but do not rely on WTP plugins heavily). The only difference between the two is that the production version doesn't contain entity definitions.

我们对生产和测试运行时使用双重persistence.xml文件,但这只是一个与类路径相关的问题(我们使用Eclipse 但不严重依赖WTP 插件)。两者之间的唯一区别是生产版本不包含实体定义。

We don't use a mocking framework to test JPA as this wouldn't add any value to our tests. The tests do run real data access with JPA that talks to PostgreSQL database.

我们不使用模拟框架来测试 JPA,因为这不会为我们的测试增加任何价值。这些测试确实使用与 PostgreSQL 数据库对话的 JPA 运行实际数据访问。

Our approach to tests is based on Spring test framework for persistence layer: in-transaction testing. Our application is Spring-based but this approach is equally usable for arbitrary applications that want to take advantage of Spring test classes. The essence is that each test runs within a single transaction that never commits and at the end (in tearDown) it is automatically rolled back. This solves the problem of data pollution and test dependency in very nice unobtrusive and transparent way.

我们的测试方法基于持久层的 Spring 测试框架:事务中测试。我们的应用程序是基于 Spring 的,但这种方法同样适用于想要利用 Spring 测试类的任意应用程序。本质是每个测试都在一个从不提交的事务中运行,并且在最后(在tearDown 中)它会自动回滚。这以非常好的不引人注目和透明的方式解决了数据污染和测试依赖的问题。

The Spring test framework is flexible to allow multi-transaction testing but these are special cases that constitute not more than 10% of tests.

Spring 测试框架很灵活,可以允许多事务测试,但这些是构成不超过 10% 测试的特殊情况。

We still use legacy support for JUnit 3.8but new Spring TestContext Frameworkfor JUnit 4 looks very attractive.

我们仍然使用对 JUnit 3.8 的遗留支持,但新的 JUnit 4 的Spring TestContext 框架看起来非常有吸引力。

For setting up in-transaction test data we use in-house utility class that constructs business entities. Since it's shared between all tests the overhead to maintain and support it is greatly outweight by the benefits of having standard and reliable way to setup test data.

为了设置交易中测试数据,我们使用构建业务实体的内部实用程序类。由于它在所有测试之间共享,因此维护和支持的开销大大超过了设置测试数据的标准和可靠方法的好处。

Spring DI helps to make tests concise and self-descriptive but it's not a critical feature.

Spring DI 有助于使测试简洁且具有自我描述性,但它不是一个关键特性。

回答by Michael Wiles

Using Spring and Spring's unit testing is the best way to go. With spring, you don't require two persistence.xml's as your persistence.xml has nothing in it, everything is specified by spring (all we specify in our persistence.xml is the persistence-unit name) and thus you can change database configuration etc with spring.

使用 Spring 和 Spring 的单元测试是最好的方法。使用 spring,您不需要两个persistence.xml,因为您的persistence.xml 中没有任何内容,一切都由spring 指定(我们在persistence.xml 中指定的只是持久性单元名称),因此您可以更改数据库配置等春天。

And as topchef pointed out, spring's transaction based unit testing is great.

正如 topchef 指出的,spring 基于事务的单元测试很棒。

回答by FabienM

As mentioned here : http://www.devx.com/java/Article/36785/1954, you can remove the following lines from your project's .settings/org.eclipse.wst.common.componentto avoid deploying test resources with the web app.

如此处所述:http: //www.devx.com/java/Article/36785/1954,您可以从项目中删除以下几行,.settings/org.eclipse.wst.common.component以避免使用 Web 应用程序部署测试资源。

<wb-resource deploy-path="/WEB-INF/classes" source-path="/src/test/java"/>
<wb-resource deploy-path="/WEB-INF/classes" source-path="/src/test/resources"/>

回答by Emilio M.

You can:

你可以:

  1. Have several persistence units
  2. Have several persistence.xml and copy them on test, and restore them later
  3. Setup your own properties on testing, and use mockito to return your custom entity manager factory
  4. Use spring: https://www.baeldung.com/spring-testing-separate-data-source
  1. 有几个持久化单元
  2. 有几个persistence.xml 并在测试时复制它们,稍后恢复它们
  3. 设置您自己的测试属性,并使用 mockito 返回您的自定义实体管理器工厂
  4. 使用弹簧:https: //www.baeldung.com/spring-testing-separate-data-source

The first two options are the most discussed in all suggested questions, and are by far the ones I like the least.

前两个选项是所有建议问题中讨论最多的选项,也是迄今为止我最不喜欢的选项。

Solution 3. would look like this:

解决方案 3. 看起来像这样:

private EntityManager entityManager;

private static EntityManagerFactory entityManagerFactory;

@BeforeClass
public static void mainTestInitClass() {
    Properties pros = new Properties();
    // Override production properties
    pros.setProperty("hibernate.dialect", "org.hibernate.dialect.H2Dialect");
    pros.setProperty("hibernate.connection.driver_class", "org.h2.Driver");
    pros.setProperty("hibernate.connection.username", "sa");
    pros.setProperty("hibernate.connection.url", "jdbc:h2:mem:some_test_db;DB_CLOSE_DELAY=-1;MVCC=TRUE;DATABASE_TO_UPPER=false");
    pros.setProperty("hibernate.hbm2ddl.auto", "create");

    entityManagerFactory = Persistence.createEntityManagerFactory("your_unit", pros);
}

@Before
public void mainTestORMSetUp() throws Exception {
    this.entityManager = entityManagerFactory.createEntityManager();
}

Now you have an entity manager available for every test. Use mockito to inject it where needed.

现在您有一个可用于每个测试的实体管理器。使用 mockito 在需要的地方注入它。

Solution 4: Use Spring Data+Spring Boot to setup the JPA, so you don't need the Entity Factory anymore, you simply use two different application.properties (one for main, and one for test) and then you use your defined Spring Entity Repository. Alternatively you can use different spring profiles (one for tests, other for production) which would end up allowing you to do the same. This solution is the one I use. Check the URL above for more details.

方案 4:使用 Spring Data+Spring Boot 设置 JPA,这样你就不再需要实体工厂了,你只需使用两个不同的 application.properties(一个用于主,一个用于测试),然后使用你定义的 Spring实体存储库。或者,您可以使用不同的弹簧配置文件(一个用于测试,另一个用于生产),这最终会允许您执行相同的操作。这个解决方案是我使用的。检查上面的 URL 以获取更多详细信息。