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.
- 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:
- 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.
- 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.
- 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 { }
- Spring Framework Reference Documentation: 11.3 Integration Testing
- Spring Framework Reference Documentation: 14.5.2 TestExecutionListener configuration
- The Javadoc of the @RunWith annotation
- The Javadoc of the SpringJUnit4ClassRunner class
- The Javadoc of the @ContextConfiguration annotation
- The Javadoc of the @TestExecutionListeners annotation
- The Javadoc of the TestExecutionListener interface
- The Javadoc of the DependencyInjectionTestExecutionListener class
- The Javadoc of the TransactionalTestExecutionListener class
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>
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:
- Configure the used dataset file by annotating the integration test class with the @DatabaseSetup annotation.
- 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.
- 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); } }
- 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:
- Writing Tests for Data Access Code is a five-part tutorial that describes how you can write tests for your data access code, and ensure that your tests are clean and easy to maintain.
- Spring From the Trenches: Using Null Values in DbUnit Datasets describes why you should use null values in your DbUnit datasets and explains how you can use them.
- Spring From the Trenches: Resetting Auto Increment Columns Before Each Test Method describes why you should reset the auto increment columns before each test method and explains how you can do it.
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).
I got java.lang.NullPointerException when I attemt to run repository method ,
object who insance of interface is null .
How solve it ?
Hi Boris,
You asked the same question in another blog post. I added my answer as a comment to that blog post.
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!
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.
the useful article, thanks!!
You are welcome. I am happy to hear that this blog post was useful to you.
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.
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).
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)
This page describes how you can create joins with JPQL. I hope that it answers to your question.
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.
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 theDefaultDataTypeFactory
. When you use Spring Test DBUnit, you can do this adding theDatabaseConfigBean
(configure the data type factory by calling thesetDatatypeFactory()
method) andDatabaseDataSourceConnectionFactoryBean
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.
Hi Petri,
thanks for your quick reply. I solved the warning by using this configuration: https://gist.github.com/PatrickGotthard/9558923
Regards,
Patrick
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.
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.
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).
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
Hi Patrick,
Cool! I will take a look at that project later this week (and give feedback of course).
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.
I just cloned the Github repository and started playing with the code. I will email my thoughts to you when I am finished.
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
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.
Very great blog. Good explanation.
Thank you!
You are welcome. I am happy to hear that this blog post was useful to you.
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 ?
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 tocreate-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:
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!
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
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 itsbasePackages
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.
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
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.
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
Thanks,
The error is gone, After I removed the following annotations from the PersistenceContext class.
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.
Hi Petri,
When Spring data JPA check for correct database url.
I need to catch the same in exception if database connectivity fails.
Hi Sumit,
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
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.
¿why I can not use ?
Please respond to my email
I send an email to you.
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
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).
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
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.
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
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]
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?
Thanks, I'm using 4.0.6
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.
Gah! That was it, I was missing the DBUnit dependency! Silly mistake! Thanks for your time Petri!
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.
Thanks, Petri.
These days I'm in JPA&TDD develop study. Your post is always helpful.
^0^
You are welcome. I am happy hear that this blog post was helpful to you. You might also want to read my blog post titled: From Top to Bottom - TDD for Web Applications.
Hi Petri, is this tutorial also work for Spring Boot?
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.
Also, is it possible to see the SQL statement used by dbunit when the database is populated from the XML dataset?
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.hi petri,
how to mock test the below interface.
========================
========================
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
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:
Model
objects are saved to the database?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!
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:
P.S. You should read this blog post: Using a framework or not?
Do you have any video tutorial?
Hi,
I do have an old video tutorial that explains how you can write integration tests for Spring Data JPA repositories. However, it is rather old and some information might be out of date.
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?
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!
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
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.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?
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?
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!
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.
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?
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).
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?
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?I'm using XML configuration file. Is there a example I can follow? I'm a newbie at this, so anything would be helpful.
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:
If you have any additional questions, don't hesitate to ask them.
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?
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.
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
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.
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?
Hi,
Let's assume that you have two tests:
X
andY
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?