30

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?

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

matt b
  • 138,234
  • 66
  • 282
  • 345
mlaccetti
  • 1,726
  • 2
  • 18
  • 17
  • This type of test you mentioned is not unit testing. I think that's of type integration test. When you write a unit test you test a class with all dependencies mocked out. So using a real database (even in-memory databases) in unit testing is not valid. – Mostafa Rezaei Jan 15 '14 at 10:19
  • It is neither a full integration test. It is valid! It just isn't unit test. – Gilberto Mar 07 '16 at 13:05

5 Answers5

15

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

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.

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

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

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

Some other links that may help:

Community
  • 1
  • 1
Aaron Digulla
  • 321,842
  • 108
  • 597
  • 820
  • I have looked into using Mock objects (have done it for LDAP-based tests), and it is certainly an option. In this specific case I'd like to actually query the DB to validate things end-to-end, rather than just ensuring that my DAO returns information. – mlaccetti Aug 06 '09 at 02:08
  • 2
    In that case, there is a solution in the first link: You can specify several "persistence units" in the persistence.xml and select a different one in your unit tests. – Aaron Digulla Aug 06 '09 at 07:47
5

We use dual persistence.xml files 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.

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.

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.

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

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

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.

topchef
  • 19,091
  • 9
  • 63
  • 102
  • I have been using JUnit 4.x (4.6, at last count, I believe) and the Spring test extensions. They do help wonderfully in setting up my JPA environment, but I still have issues since my production persistence.xml references WEB-INF/lib/common-code.jar which doesn't work very well with testing. – mlaccetti Aug 06 '09 at 02:11
  • "We don't use a mocking framework to test JPA as this wouldn't add any value to our tests.". I applaud it for to say the truth. – magallanes Apr 06 '18 at 11:49
4

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.

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

topchef
  • 19,091
  • 9
  • 63
  • 102
Michael Wiles
  • 20,902
  • 18
  • 71
  • 101
  • How do you specify what classes to load and what jars to dig code out of in Spring? I seem to have missed something important. – mlaccetti Aug 07 '09 at 17:32
  • I use OpenJPA which requires -javaagent enabled in runtime and uses persistence.xml. How should I say to OpenJPA agent to search among classes mentioned in spring config, not in persistence.xml? – Askar Kalykov Sep 12 '13 at 08:32
  • hmmm... I think that answer is maybe a bit out of date. You do need to specify in your persistence.xml a list of the your persistent classes – Michael Wiles Sep 17 '13 at 09:33
  • @AskarKalykov - I would advise you to rather use compile time instrumentation than doing it at runtime - and as I mentioned - it is necessary to include the list of persistent classes in your persitence.xml – Michael Wiles Sep 17 '13 at 09:35
  • To be honest, we had some other caveats with openjpa+spring+junit, so we decided to move to hibernate. Till now, everything works fine with jpa. – Askar Kalykov Oct 20 '13 at 08:22
0

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

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:

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.

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.

Emilio
  • 2,526
  • 22
  • 26
0

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.component to avoid deploying test resources with the web app.

<wb-resource deploy-path="/WEB-INF/classes" source-path="/src/test/java"/>
<wb-resource deploy-path="/WEB-INF/classes" source-path="/src/test/resources"/>
FabienM
  • 96
  • 1
  • 7