Spring Data JPA Tutorial: Integration Testing

My Spring Data JPA tutorial has taught us that we can create database queries and persist entities in the database by using special repository interfaces.

This raises an interesting question:

How can we write integration tests for our Spring Data JPA repositories because they are just interfaces?

This blog post answers to that question. During this blog post we will write integration tests for a Spring Data JPA repository that manages the information of todo entries (Todo objects). To be more specific, we will write integration tests for the findBySearchTerm() method of TodoRepository interface. That method ignores case and returns todo entries whose title or description contains the given search term.

Let's start by getting the required dependencies with Maven.

There are two things that you should know before you read this blog post:

  • If you are not familiar with Spring Data JPA, you should take a look at my Spring Data JPA Tutorial before you continue reading this blog post.
  • If you want to write integration tests for Spring powered repositories that do not use Spring Data JPA, you can still use the approach described in this blog post.

Getting the Required Dependencies With Maven

We can get the required dependencies with Maven by declaring the following dependencies in our pom.xml file:

  • JUnit (version 4.11).
  • AssertJ Core (version 3.2.0). We use AssertJ for ensuring that the tested method returns the correct information.
  • Spring Test (version 4.1.6.RELEASE).
  • DbUnit (version 2.5.1). Remember to exclude the JUnit dependency. We use DbUnit for initializing our database into a known state before each test case is invoked.
  • Spring Test DbUnit (version 1.2.1) integrates DbUnit with the Spring Test framework.

The relevant part of our pom.xml file looks as follows:

<dependency>
	<groupId>junit</groupId>
	<artifactId>junit</artifactId>
	<version>4.11</version>
	<scope>test</scope>
</dependency>
<dependency>
	<groupId>org.assertj</groupId>
	<artifactId>assertj-core</artifactId>
	<version>3.2.0</version>
	<scope>test</scope>
</dependency>
<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-test</artifactId>
	<version>4.1.6.RELEASE</version>
	<scope>test</scope>
</dependency>
<dependency>
	<groupId>org.dbunit</groupId>
	<artifactId>dbunit</artifactId>
	<version>2.5.1</version>
	<scope>test</scope>
	<exclusions>
		<exclusion>
			<artifactId>junit</artifactId>
			<groupId>junit</groupId>
		</exclusion>
	</exclusions>
</dependency>
<dependency>
	<groupId>com.github.springtestdbunit</groupId>
	<artifactId>spring-test-dbunit</artifactId>
	<version>1.2.1</version>
	<scope>test</scope>
</dependency>

After we have configured the required dependencies in our pom.xml file, we can configure our integration tests.

Configuring Our Integration Tests

We can configure our integration tests by following these steps:

  1. Run integration tests by using the SpringJUnit4ClassRunner class. It is a custom JUnit runner that integrates the Spring Test framework with JUnit. We can configure the used JUnit runner by annotating our test class with the @RunWith annotation.
  2. Configure the application context configuration class (or XML configuration file) that configures the application context used by our integration tests. We can configure the used application context configuration class (or XML configuration file) by annotating our test class with the @ContextConfiguration annotation.
  3. Configure the test execution listeners which react to the test execution events that are published by the Spring Test framework. We have to configure the following test execution listeners:
    • The DependencyInjectionTestExecutionListener provides dependency injection for the test object.
    • The TransactionalTestExecutionListener adds transaction support (with default rollback semantics) into our integration tests.
    • The DbUnitTestExecutionListener adds support for the features provided by the Spring Test DbUnit library.

After we have added this configuration into our integration test class, its source code looks as follows:

import com.github.springtestdbunit.DbUnitTestExecutionListener;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestExecutionListeners;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
import org.springframework.test.context.transaction.TransactionalTestExecutionListener;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {PersistenceContext.class})
@TestExecutionListeners({DependencyInjectionTestExecutionListener.class,
        TransactionalTestExecutionListener.class,
        DbUnitTestExecutionListener.class})
public class ITFindBySearchTermTest {
}

After we have configured our integration test class, we can start writing integration tests for our Spring Data JPA repository.

Writing Integration Tests for Our Repository

We can write integration tests for our repository by following these steps:

First, we have to inject the tested repository into our test class. Because we are writing integration tests for the TodoRepository interface, we have to inject it into our test class. The source code of our test class looks as follows:

import com.github.springtestdbunit.DbUnitTestExecutionListener;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestExecutionListeners;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
import org.springframework.test.context.transaction.TransactionalTestExecutionListener;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {PersistenceContext.class})
@TestExecutionListeners({DependencyInjectionTestExecutionListener.class,
        TransactionalTestExecutionListener.class,
        DbUnitTestExecutionListener.class})
public class ITFindBySearchTermTest {

	@Autowired
	private TodoRepository repository;
}

Second, we have to create the DbUnit dataset that initializes our database into a known state before our test cases are invoked. We will use the flat XML dataset format because it is less verbose than the original DbUnit dataset format. This means that we can create our dataset by following these rules:

  • Each XML element contains the information of a single table row.
  • The name of the XML element identifies the name of the database table in which its information is inserted.
  • The attributes of the XML element specify the values that are inserted into the columns of the database table.

The tested repository (TodoRepository) queries information from the todos table that has the following columns: id, created_by_user, creation_time, description, modified_by_user, modification_time, title, and version.

Because we are writing integration tests for a method that returns a list of Todo objects, we want to insert two rows to the todos table. We can do this by creating a DbUnit dataset file (todo-entries.xml) that looks as follows:

<dataset>
    <todos id="1"
           created_by_user="createdByUser"
           creation_time="2014-12-24 11:13:28"
           description="description"
           modified_by_user="modifiedByUser"
           modification_time="2014-12-25 11:13:28"
           title="title"
           version="0"/>
    <todos id="2"
           created_by_user="createdByUser"
           creation_time="2014-12-24 11:13:28"
           description="tiscription"
           modified_by_user="modifiedByUser"
           modification_time="2014-12-25 11:13:28"
           title="Foo bar"
           version="0"/>
</dataset>
Additional reading:

DbUnit documentation provides more information about the different DbUnit dataset formats.

Third, we can write integration tests for the findBySearchTerm() method of the TodoRepository interface. Let's write integration tests which ensure that the findBySearchTerm() method is working correctly when the title of one todo entry contains the given search term. We can write these integration tests by following these steps:

  1. Configure the used dataset file by annotating the integration test class with the @DatabaseSetup annotation.
  2. Write an integration test which ensures that the findBySearchTerm() method returns one todo entry when the search term "iTl" is passed as a method parameter.
  3. Write an integration test which ensures that the findBySearchTerm() method returns the "first" todo entry when the search term "iTl" is passed as a method parameter.

The source code of the ITFindBySearchTerm class looks as follows:

import com.github.springtestdbunit.DbUnitTestExecutionListener;
import com.github.springtestdbunit.annotation.DatabaseSetup;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestExecutionListeners;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
import org.springframework.test.context.transaction.TransactionalTestExecutionListener;

import static org.assertj.core.api.Assertions.assertThat;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {PersistenceContext.class})
@TestExecutionListeners({DependencyInjectionTestExecutionListener.class,
        TransactionalTestExecutionListener.class,
        DbUnitTestExecutionListener.class})
@DatabaseSetup("todo-entries.xml")
public class ITFindBySearchTermTest {

	@Autowired
	private TodoRepository repository;
	
	@Test
	public void findBySearchTerm_TitleOfFirstTodoEntryContainsGivenSearchTerm_ShouldReturnOneTodoEntry() {
		List<Todo> searchResults = repository.findBySearchTerm("iTl");
		assertThat(searchResults).hasSize(1);
	}
	
	@Test
	public void findBySearchTerm_TitleOfFirstTodoEntryContainsGivenSearchTerm_ShouldReturnFirstTodoEntry() {
		List<Todo> searchResults = repository.findBySearchTerm("iTl");

		Todo found = searchResults.get(0);
		assertThat(found.getId()).isEqualTo(1L);
	}	
}
When you use the @DatabaseSetup annotation, you have to follow these rules:

  • If all test methods of your test class use the same dataset, you can configure it by annotating your test class with @DatabaseSetup annotation. However, if all test methods of your test class do not use the same dataset, you have to annotate your test methods with the @DatabaseSetup annotation.
  • If the dataset file is in the same package than the integration test class, you can configure it by using the name of the dataset file. On the other hand, if the dataset file is not in same the package than the test class, you have to configure the full path of the dataset file. For example, if your dataset file (todo-entries.xml) is in the package foo.bar, you can configure its full path by using the string: "/foo/bar/todo-entries.xml".

Additional Reading:

Let's move on and summarize what we learned from this blog post.

Summary

This blog post has taught us four things:

  • We can integrate DbUnit with the Spring Test framework by using Spring Test DbUnit.
  • We can integrate Spring Test DbUnit with the Spring Test framework by using the DbUnitTestExecutionListener class.
  • We should use the flat XML databaset format because it is less verbose than the original DbUnit dataset format.
  • We can use the @DatabaseSetup annotation on the class level or on the method level.

P.S. You can get the example applications of this blog post from Github (query methods, JPA Criteria API, Querydsl).

If you want to learn how to use Spring Data JPA, you should read my Spring Data JPA tutorial.
85 comments… add one
  • Boris Jul 23, 2013 @ 15:31

    I got java.lang.NullPointerException when I attemt to run repository method ,
    object who insance of interface is null .
    How solve it ?

  • Julio Aguilar Aug 30, 2013 @ 19:18

    This tutorial helped me and my team implement unit tests successfully. There are minor things missing in the post (like bonecp inclusion in the pom.xml and the full contents of the Spring context) but the github project helped to fill those.

    Great work and thanks for sharing!

    • Petri Aug 31, 2013 @ 23:08

      I am happy to hear that your team found this blog post useful. Also, I left the other dependencies out because I felt that they were out of the scope of this post. However, now that you mentioned this issue, I will update this blog post and add a link to the full pom.xml file. Maybe this will clarify things a bit.

  • Nikolay Jan 20, 2014 @ 15:39

    the useful article, thanks!!

    • Petri Jan 20, 2014 @ 18:01

      You are welcome. I am happy to hear that this blog post was useful to you.

  • Chris Feb 6, 2014 @ 2:52

    Hi Petri

    Im trying to use spring db unit. The versions I have are the ones recommended above in your blog. However Im encountering (could not find dataset.xml) file
    testController(my.tests.web.TermControllerIntegrationTest) Time elapsed: 4.245 sec <<< ERROR!
    java.lang.IllegalArgumentException: Unable to load dataset from "classpath:/termData.xml" using class com.github.springtestdbunit.dataset.FlatXmlDataSetLoader
    at org.springframework.util.Assert.notNull(Assert.java:112)

    I have tried many things.
    Putting the dataset in the same package as the class.
    in the classpath under resources
    and trying to reference it with'/' like classpath:/termData.xml etc with no luck.
    My unit test works well without db unit.

    @TestExecutionListeners({ DependencyInjectionTestExecutionListener.class,
    DirtiesContextTestExecutionListener.class,
    TransactionalTestExecutionListener.class,
    //TransactionDbUnitTestExecutionListener.class})
    DbUnitTestExecutionListener.class })

    any help would be very useful.

    • Petri Feb 6, 2014 @ 10:00

      Hi Chris,

      If the dataset file is in the same package than your test class, you can reference it by simply using its name (In your case, termsData.xml should work). Also, remember that you have to compile your project every time when you add a new dataset file or modify an existing file (this sucks btw).

  • CJ Feb 14, 2014 @ 4:17

    Can you give me an example to write @Query for Join operation ? Iam struggling with the error called "unexpected token:" error - @Query(select count(*) from employee emp join employeedetaildata edd on emp.id=edd.employeedataid where emp.rankId = :rankId)

  • Patrick Mar 15, 2014 @ 0:27

    Hi Petri,

    thanks for your great Spring Data JPA tutorials. I just started to use DbUnit in one of my private applications but I get this warning every time DbUnit sets up the database via @DatabaseSetup:

    WARN org.dbunit.dataset.AbstractTableMetaData - Potential problem found: The configured data type factory 'class org.dbunit.dataset.datatype.DefaultDataTypeFactory' might cause problems with the current database 'H2' (e.g. some datatypes may not be supported properly). In rare cases you might see this message because the list of supported database products is incomplete (list=[derby]). If so please request a java-class update via the forums.If you are using your own IDataTypeFactory extending DefaultDataTypeFactory, ensure that you override getValidDbProducts() to specify the supported database products.

    Relevant test dependencies:
    org.springframework:spring-test:4.0.2
    com.github.springtestdbunit:spring-test-dbunit:1.1.0
    org.dbunit:dbunit:2.4.9
    com.h2database:h2:1.3.175

    The only difference to your setup is, that I override the DataSource of my default application config in the test config via:

    @Bean
    public DataSource dataSource() {
    return new EmbeddedDatabaseBuilder().setType(EmbeddedDatabaseType.H2).build();
    }

    But that should not be the problem. I am able to suppress the warning via logging configuration but maybe you know another way to solve this "problem" (all test run fine)?

    Best regards,
    Patrick

    P.S.: Have you already recognized the new Hikari Connection Pool (HikariCP) as alternate to BoneCP? Though I haven't benchmarked the connection pool myself yet, it definitely feels faster than BoneCP to me and the benchmarks on the HikariCP web page promise an outstanding performance.

    • Petri Mar 15, 2014 @ 0:58

      Hi Patrick,

      Thank you for your kind words! It is nice to hear that my tutorials have been useful to you.

      I assume that this line is written to the log because the default DbUnit data type factory doesn't support vendor specific data types. I am not 100% sure about this but it seems like a reasonable assumption (my assumption is based on this DbUnit FAQ entry).

      If my assumption is correct, you have to configure DbUnit to use the H2DataTypeFactory instead of the DefaultDataTypeFactory. When you use Spring Test DBUnit, you can do this adding the DatabaseConfigBean (configure the data type factory by calling the setDatatypeFactory() method) and DatabaseDataSourceConnectionFactoryBean beans to the application context (more about this here. Search for Custom IDatabaseConnections text).

      I haven't done this myself because I haven't been using vendor specific data types but based on the documentation, it should do the trick. By the way, I figured this out after I read this answer to a similar Stack Overflow question.

      I haven't used HikariCP myself but I ran its data source benchmark this week, and HikariCP seems to be a lot faster than BoneCP. I am going to try it in the example applications of my new blog entries. It will be interesting to see if I notice any difference.

      • Patrick Mar 15, 2014 @ 1:08

        Hi Petri,

        thanks for your quick reply. I solved the warning by using this configuration: https://gist.github.com/PatrickGotthard/9558923

        Regards,
        Patrick

        • Petri Mar 15, 2014 @ 1:14

          Hi Patrick,

          you are welcome. Your configuration looks simple and I am going to use the same approach in my new blog posts. Thanks for sharing it.

          • Patrick Mar 15, 2014 @ 1:29

            Hi again,

            are you interested to be notified when I push the project to GitHub? Maybe we could exchange some knowledge based on the projects sources.

          • Petri Mar 15, 2014 @ 1:31

            Hi,

            sure. Let me know when you do that (push the project to Github). I think it would be a good opportunity to learn something new (for both of us).

          • Patrick Apr 2, 2014 @ 23:35

            Hi Petri,

            I have pushed my project to GitHub now: https://github.com/PatrickGotthard/newsreadr-server

            Feel free to look at the sources (especially the integration tests). I would be really happy to get some input from you.

            You would have to check out https://github.com/PatrickGotthard/newsreadr-bom and https://github.com/PatrickGotthard/newsreadr-shared as well to import the project into your IDE (currently there are no public Maven snapshots available but I would configure my Jenkins and Nexus soon).

            Regards,
            Patrick

          • Petri Apr 2, 2014 @ 23:36

            Hi Patrick,

            Cool! I will take a look at that project later this week (and give feedback of course).

          • Patrick Gotthard Apr 3, 2014 @ 23:08

            Update: the transitive dependencies are now public available so that you don't have to check out the related projects. I've also added installation and build instructions to get started.

          • Petri Apr 5, 2014 @ 11:15

            I just cloned the Github repository and started playing with the code. I will email my thoughts to you when I am finished.

  • Ameen Mar 17, 2014 @ 13:07

    nice post, would i ask for an example on how to test model data ?
    I want to test a controller request method that initiate the model data,
    also is it possible to test thymeleaf templates ?

    Thanks in advance

    • Petri Mar 17, 2014 @ 15:49

      You should check out my Spring MVC Test Tutorial. It explains how you can write both unit and integration tests for your Spring MVC controllers.

      Also, It is possible to test Thymeleaf templates by using the Spring MVC Test framework. I haven't written anything about this because I haven't had the chance to use Thymeleaf. However, this blog post has a small description about this, and it seems that writing tests for Thymeleaf templates is pretty simple. All you have to do is to write assertions for the response body.

  • Bodo Mar 27, 2014 @ 12:32

    Very great blog. Good explanation.
    Thank you!

    • Petri Mar 27, 2014 @ 13:37

      You are welcome. I am happy to hear that this blog post was useful to you.

  • Happy Apr 11, 2014 @ 5:04

    Nice post. Can I ask : How can I write integration test cases (testing from request to response) on my spring mvc app which uses spring data jpa - without changing the state of the oracle database ? Can I somehow fetch the required tables and their data into an in memory database for testing purposes ?
    I have never used it but DBUnit seems less configurable for complex data sets and I don't want to write xml files for data sets. AND it doesn't create in memory HSQLDB tables either. What other options do I have ?

    • Petri Apr 11, 2014 @ 9:48

      The example application of this blog has two profiles: dev and integration-test. Both of these profiles use H2 in-memory database but in a real life scenario, only the integration-test profile would be using it (the other profiles would use a "real" database).

      I have configured Hibernate to create the database tables by setting the value of the hibernate.hbm2ddl.auto property to create-drop.

      If you take a look at the config.properties file used when the integration-test Maven profile is activated, you will find the following line from it:

      
      hibernate.hbm2ddl.auto=create-drop
      
      

      This line ensures that the Hibernate creates the database for my integration tests. After the database has been created, I insert the test data by using DbUnit. All example applications of my Spring MVC Test tutorial use the same approach.

      I agree that the DbUnit can be a tricky beast to configure and large data sets are pain in the ass (that is why they should be avoided). However, I haven't been able to find any alternatives to it. If you happen to find one, let me know!

  • Rashidi Zin Apr 19, 2014 @ 13:52

    Hello,

    I tried following the tutorial. Unfortunately I hit into an error "Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type".

    My implementation can be found at https://gist.github.com/rashidi/34f7f0dc70da92de1a1b. I am using gradle instead of maven.

    Also, I placed the test class inside src/test instead of src/main. I tried moving it to src/main but it still failed with the same error.

    Thanks in advanced for your help! :D

    • Petri Apr 20, 2014 @ 10:41

      Hi,

      it seems that you forgot to configure Spring Data JPA, and that is why the Spring container cannot find the repository bean (and throws an exception). You can fix this by annotating your configuration class with the @EnableJpaRepositories annotation (remember to set the value of its basePackages attribute).

      By the way, if you want to use annotation driven transaction management, you have to annotate your configuration class with the @EnableTransactionManagement as well.

      I hope that this answered to your question.

  • Basu Jun 24, 2014 @ 8:16

    Thanks for the excellent posts on spring jpa. Helped me learn a lot about them.

    After going through your other Spring JPA Tutorials, I successfully implemented it in a Sprin MVC application. Everything works fine.

    However, now I'm trying to learn to write integration test cases based on the above tutorial. When I run the test, I get the exception " Injection of autowired dependencies failed;" for the Autowired Repository.

    What could possibly be wrong here when the application is successfully running when tested from the front-end.

    Thanks a lot in advance.

    PS: I use XML based configuration. and I've included the below for the jpa repositories in the XML Configuration.

    Regards,
    Basu

    • Petri Jun 24, 2014 @ 10:09

      Hi,

      It seems that Wordpress ate your XML configuration. Anyway, typically the root cause of your problem is that the Spring container couldn't either create the repository OR it just didn't find it (check the stacktrace for more details).

      The first case often happens when there is a problem in the configuration, and the second case happens the repository base package is incorrect.

      It would be helpful to see your integration test, repository interface, and the application context configuration file. Maybe you could add these files to pastebin? Also, the full stacktrace would be really helpful because you can find the root cause of the problem by reading it.

  • sanjeev Jun 24, 2014 @ 20:41

    Hello Perti,

    I am following your series and using JavaConfig approach.
    I am getting below error. Could you please give some hint why this is happening.

    Many thanks
    Sanjeev.

    Caused by: java.lang.NoClassDefFoundError: Could not initialize class org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:526)
    at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:148)
    at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:89)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:1069)
    ... 45 more

    • sanjeev Jun 24, 2014 @ 20:59

      Thanks,
      The error is gone, After I removed the following annotations from the PersistenceContext class.

      
      @Configuration
      //@ComponentScan
      //@EnableJpaRepositories 
      //@EnableTransactionManagement
      //@EnableWebMvc 
      //@EnableEnableAutoConfiguration
      //@ImportResource("classpath:applicationContext.xml")
      @PropertySource(value = { "classpath:application.properties" })
      class PersistenceContext { ...}
      
      
      • Petri Jun 24, 2014 @ 22:38

        This might be a class loading issue. I noticed that your PersistenceContext class was annotated with the @EnableAutoConfiguration annotation. I assume that you are using Spring Boot. Right?

        If this is the case, you should ensure that you don't have older Spring versions in your classpath. Check out section 9.1.1 Maven Installation of the Spring Boot reference manual for more details about this.

  • Sumit Aug 1, 2014 @ 15:04

    Hi Petri,
    When Spring data JPA check for correct database url.
    I need to catch the same in exception if database connectivity fails.

    • Petri Kainulainen Aug 4, 2014 @ 21:10

      Hi Sumit,

      • If your database url doesn't follow the correct syntax, SQLException is thrown when your application is started. For example, you might see an error message like this your log file: Caused by: java.sql.SQLException: No suitable driver found for jbc:mysql://localhost:3306/socialtwitter
      • If your database url follows the correct syntax, but your application cannot open a database connection, it throws an exception when it tries to open a database connection.

      What kind of an error handler you want to implement?

      If you want to just redirect the user to an error page when your application cannot open a database connection, you should take a look at the Javadoc of the SimpleMappingExceptionResolver class.

      If you want to learn how you can handle exceptions with Spring MVC, you should read a blog post titled: Exception Handling in Spring MVC.

  • william Aug 18, 2014 @ 5:00

    ¿why I can not use ?
    Please respond to my email

    • Petri Aug 20, 2014 @ 20:21

      I send an email to you.

  • Paul Statham Aug 26, 2014 @ 13:33

    Hi Petri,

    I have a question for you regarding development process. I have been reading through a number of your very useful posts. My process so far (using TDD) is for example to develop Controller test A (using a mock service as you have done), have it fail, flesh out the controller method A have that pass.

    Then move on to writing service test A (using a mock repository), have it fail, then flesh out the service method A and have that pass.

    Then go back through the same process with test B, test C etc.

    Once the unit tests have a good coverage for the Controller and Service layers, I can then move on to writing integration tests for the Repository layer as you have done here. Is this similar to the process you follow?

    Thanks,
    Paul

    • Petri Aug 26, 2014 @ 18:21

      Hi Paul,

      I have actually written a blog post that describes the process which I am using the moment. But I will save you from reading it by telling that I use the same process than you do. Also, after I have finished writing integration tests for my repositories, I write integration tests for my controllers (I guess you can call these end-to-end tests as well).

  • Paul Statham Jan 20, 2015 @ 13:10

    Hi Petri,

    I finally got round to trying this out and I am having some problems with the database setup not working. Instead of typing this all out again I have made a stack overflow post here

    http://stackoverflow.com/questions/28043618/integration-testing-spring-repository-layer-with-dbunit

    If you have some time would you be able to take a look and see if you can spot where I'm going wrong?

    Thanks,
    Paul

    • Petri Jan 20, 2015 @ 17:06

      Hi Paul,

      I left a comment to your StackOverflow question and asked a few additional questions that will help me to write a better answer for you.

      • Paul Statham Jan 20, 2015 @ 18:00

        Thanks, I've also replied with some progress. I now have tables, but no data, so the second test still fails as it doesn't return the expected result

      • Paul Statham Jan 21, 2015 @ 17:01

        It's perhaps something to do with the TestExecutionListener, I have this message in my log. Any ideas?

        14:47:05,670 INFO TestContextManager:242 - Could not instantiate TestExecutionListener [com.github.springtestdbunit.DbUnitTestExecutionListener]. Specify custom listener classes or make the default listener classes (and their required dependencies) available. Offending class: [org/dbunit/dataset/IDataSet]

        • Petri Jan 21, 2015 @ 17:40

          I will take a look at this later today (I have to work for few hours), but I think that you have found the smoking gun. Which Spring version are you using?

          • Paul Statham Jan 21, 2015 @ 17:44

            Thanks, I'm using 4.0.6

          • Petri Jan 21, 2015 @ 22:47

            Unfortunately I could not reproduce your problem :( Are you using the latest versions of Spring Test DbUnit (1.1.0) and DbUnit (2.5.0)?

            I assume that you are using the latest versions because Spring Framework 4.0 is not compatible with Spring Test DbUnit 1.0.0, and if you try to use it, your tests fail because an exception is thrown.

            You could try to take a look at this example application and compare its configuration with your configuration. Note that the XML configuration is not working at the moment. I will try to fix it tomorrow.

          • Paul Statham Jan 22, 2015 @ 11:13

            Gah! That was it, I was missing the DBUnit dependency! Silly mistake! Thanks for your time Petri!

          • Petri Jan 22, 2015 @ 20:27

            Great! It is good to hear that you were able to solve your problem. If you want to find out what kind of tests I write for my data access code, you should read my Writing Tests for Data Access Code tutorial.

  • arahansa Mar 24, 2015 @ 4:37

    Thanks, Petri.
    These days I'm in JPA&TDD develop study. Your post is always helpful.
    ^0^

  • Ivan Apr 10, 2015 @ 5:41

    Hi Petri, is this tutorial also work for Spring Boot?

    • Petri Apr 10, 2015 @ 9:33

      Yes, but you have to make minor changes to the configuration (use the @SpringApplicationConfiguration annotation instead of the @ContextConfiguration annotation). The section 35.3 Testing Spring Boot Applications of the Spring Boot Reference Manual provides more information about this.

      P.S. I haven't tried writing tests for Spring Boot applications, but I cannot figure out any reasons why this wouldn't work.

      • Ivan Jun 11, 2015 @ 6:41

        Also, is it possible to see the SQL statement used by dbunit when the database is populated from the XML dataset?

        • Petri Jun 11, 2015 @ 7:28

          DbUnit writes the invoked prepared statements to the log file by using the DEBUG log level. If you need to see the actual SQL statements, you can use log4jdbc.

  • sathish Oct 2, 2015 @ 13:31

    hi petri,
    how to mock test the below interface.
    ========================

    
    public abstract java.lang.Iterable save(java.lang.Iterable arg0);
    
    

    ========================
    i want to batch update an repo , which has 200 records .
    i wan to batch update it with a loop of 20 records in one save.which is the best approach to flush the repository.

    i am using this below startegy is this ok .
    ist of repository object -modelListPersist

    
    int count=0;
      
    while ( count< modelListPersist.size() ) {
    	model model = (model) modelListPersist.get(count);
    	modelRepository.save(model);
    	if ( ++count % 20 == 0 ) {
    		//flush a batch of updates and release memory:
    		modelRepository.flush();            	
    	}
    }
    
    
    • Petri Oct 2, 2015 @ 20:42

      Hi Sathish,

      Before I can help you to mock the model repository, I have to understand what kind of an test do you want to write. For example:

      • Do you want to ensure that correct Model objects are saved to the database?
      • Do you want to ensure that the changes are flushed after 20 objects have been processed?
  • johaness vix Oct 26, 2015 @ 12:58

    I cannot use it inside my real world project, you are pointing to stuff at Github! please post pure solutions! we cannot use it if it points to GitHub third party stuff!

    • Petri Oct 26, 2015 @ 19:27

      Do you have a company policy that prevents you from using 3rd party frameworks or libraries hosted on Github? If so, I feel sorry for you.

      However, I will continue using these frameworks and libraries in my examples for two reasons:

      1. I don't want to write a worse version of an existing library or framework.
      2. These libraries and frameworks help me to save time. This is extremely valuable because my time is a limited resource.

      P.S. You should read this blog post: Using a framework or not?

  • Prashant rai Feb 5, 2016 @ 10:05

    Do you have any video tutorial?

  • Steve Feb 21, 2016 @ 22:19

    Hi Petri,

    Great article, I am trying to use this information to test a sample project I have got from the internet. https://github.com/netgloo/spring-boot-samples/tree/master/spring-boot-mysql-springdatajpa-hibernate

    I have added a PersistanceContext but alot of the information in there references db.url when the properties file uses "spring.datasource.url" shall I change this in the PersistanceContext?

    • Petri Feb 21, 2016 @ 22:37

      Hi Steve,

      If you are trying to write integration tests for a repository that is configured by Spring Boot, you need to configure your integration tests by following the advice given in the Spring Boot Reference Documentation.

      Also, you don't need to add the PersistenceContext class because Spring Boot should configure the persistence layer of your application.

      If you have any additional questions, don't hesitate to ask them!

      • Steve Feb 21, 2016 @ 23:30

        Thanks for the quick reply.

        I have a user-entries.xml that contains:

        Is this still needed to represent the dummy data?
        `I have added @ContextConfiguration({"classpath:/user-entries.xml"})`
        to load it if I do.

        and then tried to run the test, I am getting an exception:
        java.lang.IllegalStateException: Failed to load ApplicationContext
        at org.springframework.test.context.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:94)
        at org.springframework.test.context.DefaultTestContext.getApplicationContext(DefaultTestContext.java:72)
        at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.injectDependencies(DependencyInjectionTestExecutionListener.java:117)
        at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.prepareTestInstance(DependencyInjectionTestExecutionListener.java:83)
        at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:212)
        ...

        Thanks

        • Petri Feb 22, 2016 @ 21:59

          Hi Steve,

          If you want to initialize your database into a known state before your test methods are invoked, you need to configure the used dataset by using the @DatabaseSetup annotation. You can use this annotation on class level or method level. Also, remember to follow the rules described in this blog post.

  • Olgerts Mar 8, 2017 @ 1:09

    Excellent post, Petri!
    I'm very happy to read your posts!
    Quick question, what would be the strategy to test for example 15 Repositories fast? Currently it takes about 8 minutes. Because I have some setup @Before, cleanup @After, and all tests are running one by one. Is it possible to launch them parallel somehow?

    • Petri Mar 8, 2017 @ 9:11

      Hi,

      The Maven Failsafe plugin has good support for parallel test execution. That being said, eight minutes is a long time. I assume that your test cases require a somewhat complex setup / teardown code?

      • Olgerts Mar 8, 2017 @ 21:42

        Exactly! Some tests are complicated. You need to create client, company, etc., before a test, and clean up everything after each test. Some things in common are in the Test setup. That's why it's takes so much time.
        Any ideas? Any strategy?
        Thanks in advance!

        • Petri Mar 9, 2017 @ 18:39

          How do you initialize your database into a known state? The reason why I ask this is that your setup doesn't sound that complex, and yet, it seems to take a lot of time.

  • Rostowich Mar 16, 2017 @ 21:07

    Thank you for this post. But when i run the example using spring boot, i get this error about the PersistenceContext.
    java.lang.IllegalStateException: Failed to load ApplicationContext.
    Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [javax.persistence.PersistenceContext]: Specified class is an interface.
    How to write a class to implement this interface?

    • Petri Mar 16, 2017 @ 21:30

      Hi,

      You have imported the wrong class. You can find the correct class from the net.petrikainulainen.springdata.jpa.config package.

      That being said, if you are using Spring Boot you probably shouldn't use the technique described in this blog post because Spring Boot has a lot of testing related improvements that make the configuration a bit different. If you want to take a look at some newer integration testing examples, you should take look at the example applications of my Test With Spring course (intermediate package).

  • Alex Mar 17, 2017 @ 11:51

    Hi Petri,

    I'm dealing with the same problem. When I try to run the test I get:
    java.lang.IllegalStateException: Failed to load ApplicationContext
    Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'persistenceContext': Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [javax.persistence.PersistenceContext]: Specified class is an interface
    Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [javax.persistence.PersistenceContext]: Specified class is an interface

    I'm not using Spring Boot, but I'm not sure I done the configuration properly. I have my persistence.xml file inside src/main/resources/META-INF directory. My data source bean looks like this:

    And the entity manager factory:

    Application properties file is:

    #Database Configuration
    db.driver=oracle.jdbc.driver.OracleDriver
    db.url=jdbc:oracle:thin:@:oracle
    db.username=
    db.password=

    #Hibernate Configuration
    hibernate.dialect=org.hibernate.dialect.Oracle12cDialect
    hibernate.format_sql=true
    hibernate.hbm2ddl.auto=update
    hibernate.show_sql=true
    hibernate.generate_statistics=true

    Can you please help me solve this problem?

    • Petri Mar 17, 2017 @ 18:40

      Hi,

      Like I mentioned in my previous answer, you have imported the wrong class. You should either use your application context configuration class (replace PersistenceContext class with your configuration class) or configure your integration tests by using your XML configuration file. Do you configure your application context by using Java configuration or XML configuration files?

      • Alex Mar 19, 2017 @ 10:53

        I'm using XML configuration file. Is there a example I can follow? I'm a newbie at this, so anything would be helpful.

        • Petri Mar 19, 2017 @ 22:10

          Hi,

          You can follow the instructions given in this blog post as long as you make one change to the configuration of your integration tests:

          You have to configure your application context by using your XML configuration file. You can specify the location of your application context configuration file by using the locations attribute of the @ContextConfiguration annotation.

          For example, if your application context configuration file is called example-context.xml and it is found from the root directory of your classpath, you can use the following annotation:

          
          @ContextConfiguration(locations = {"classpath:example-context.xml"})
          
          

          If you have any additional questions, don't hesitate to ask them.

          • Alex Mar 20, 2017 @ 9:38

            Thanks, Petry,

            Test passed when I used the .xml file. When running a test, why are even controllers being injected and instantiated, even if I'm not using them for my test? I mean, is taking a pretty long time until the test is finished... And what is that data set file being used for?

          • Petri Mar 27, 2017 @ 21:01

            Hi,

            The controllers are instantiated (most likely) because you configure the application context of your integration tests by using an XML configuration file that configures your web layer. You can solve this problem by splitting your application context configuration into multiple smaller configuration files and loading only the configuration files that are relevant for your integration tests.

  • Suchi Apr 19, 2017 @ 6:10

    I am getting this error - any pointer?

    aused by: org.springframework.boot.autoconfigure.jdbc.DataSourceProperties$DataSourceBeanCreationException: Cannot determine embedded database driver class for database type NONE. If you want an embedded database please put a supported one on the classpath. If you have database settings to be loaded from a particular profile you may need to active it (no profiles are currently active).
    at org.springframework.boot.autoconfigure.jdbc.DataSourceProperties.determineDriverClassName(DataSourceProperties.java:246)
    at org.springframework.boot.autoconfigure.jdbc.DataSourceProperties.initializeDataSourceBuilder(DataSourceProperties.java:183)
    at org.springframework.boot.autoconfigure.jdbc.DataSourceConfiguration.createDataSource(DataSourceConfiguration.java:42)
    at org.springframework.boot.autoconfigure.jdbc.DataSourceConfiguration

    • Petri Apr 19, 2017 @ 19:52

      Hi,

      It seems that you are using Spring Boot. If you want to write integration tests for a Spring Boot application, you shouldn't use the instructions given in this blog post. These instructions are valid only if you are writing integration tests for Spring applications.

      That being said, you should take a look at the example applications of my Test With Spring course. These examples demonstrate how you can write integration tests for both Spring and Spring Boot applications. For example, if you want to know how you can configure DbUnit when you are writing integration tests for your Spring powered repositories AND you are using Spring Boot, you should take a look at this example.

      If you have any additional questions, don't hesitate to ask them.

  • Yolanda Oct 10, 2017 @ 13:03

    Hi,
    I have tried your recommended test framework, but when I have two tables with foreign key relationship, it seems it has no mechanism for solving foreign key constraint, do you have some workaround?

    • Petri Oct 14, 2017 @ 10:33

      Hi,

      Let's assume that you have two tests: X and Y that use different data. To be more specific, Y inserts data only to the table that is referenced by the foreign key constraint.

      When you say that DbUnit has no mechanism for solving foreign key constraints, do you mean that Y fails when it tries to delete the data from the database table that is found from your data set?

Leave a Reply