Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring Boot DataJpaTest unit test reverting to H2 instead of mySql

I've got a simple little "hello world" Spring Boot app. It has a single entity ("IssueReport") and it's configured to run mySQL (instead of the default H2 embedded database).

The app itself runs fine. I created a mySql database and user, Spring Boot/Hibernate created the table and successfully populates and reads the mySQL data when I run the app. Life is Good - there are no problems with mySQL and my Spring Boot app itself.

Q: Now how do I use mySQL (instead of the embedded H2) in unit tests?

  1. I created a second, separate mySQL database: test2_test_db.

  2. I'm using Spring Boot 2.0.6; Eclipse Photon on STS 3.9.6; Ubuntu Linux.

  3. I created application-test.properties in src/test/resources/:

    spring.datasource.url=jdbc:mysql://localhost:3306/test2_test_db
    spring.datasource.username=springuser
    spring.datasource.password=springuser
    spring.jpa.hibernate.ddl-auto=create
    
  4. Here's the entire unit test:

    package com.hellospring.example;
    
    import static org.assertj.core.api.Assertions.assertThat;
    import java.util.List;
    import javax.transaction.Transactional;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
    import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager;
    import org.springframework.test.context.ActiveProfiles;
    import org.springframework.test.context.junit4.SpringRunner;
    import com.hellospring.example.entity.IssueReport;
    import com.hellospring.example.repositories.IssueRepository;
    
    @RunWith(SpringRunner.class)
    @ActiveProfiles("test")
    @Transactional
    @DataJpaTest
    public class IssueRepositoryIntegrationTests {
    
         @Autowired
         private TestEntityManager entityManager;
    
         @Autowired
         private IssueRepository issueRepository;
    
         @Test
         public void addNewIssue() {
             System.out.println("addNewIssue()...");  // <-- This prints in the console
             final String email = "[email protected]";
             List<IssueReport> resultSet = issueRepository.findAll();  // <-- We get an exception in here...
         }  
    }
    
  5. Here's the console error:

    2018-10-25 22:20:16.381  INFO 13637 --- [           main] c.v.e.IssueRepositoryIntegrationTests    : The following profiles are active: test
    2018-10-25 22:20:16.405  INFO 13637 --- [           main] s.c.a.AnnotationConfigApplicationContext : Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@d554c5f: startup date [Thu Oct 25 22:20:16 PDT 2018]; root of context hierarchy
    2018-10-25 22:20:17.059  INFO 13637 --- [           main] beddedDataSourceBeanFactoryPostProcessor : Replacing 'dataSource' DataSource bean with embedded version
    2018-10-25 22:20:17.060  INFO 13637 --- [           main] o.s.b.f.s.DefaultListableBeanFactory     : Overriding bean definition for bean 'dataSource' with a different definition: replacing [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=org.springframework.boot.autoconfigure.jdbc.DataSourceConfiguration$Hikari; factoryMethodName=dataSource; initMethodName=null; destroyMethodName=(inferred); defined in class path resource [org/springframework/boot/autoconfigure/jdbc/DataSourceConfiguration$Hikari.class]] with [Root bean: class [org.springframework.boot.test.autoconfigure.jdbc.TestDatabaseAutoConfiguration$EmbeddedDataSourceFactoryBean]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null]
    2018-10-25 22:20:17.308  INFO 13637 --- [           main] o.s.j.d.e.EmbeddedDatabaseFactory        : Starting embedded database: url='jdbc:h2:mem:979b3ce9-604e-4efd-a6d4-79576c3d67e9;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=false', username='sa'
    2018-10-25 22:20:17.685  INFO 13637 --- [           main] j.LocalContainerEntityManagerFactoryBean : Building JPA container EntityManagerFactory for persistence unit 'default'
      ...  <= I do *NOT* want H2!  I want mySQL!
    
    2018-10-25 22:20:19.315  WARN 13637 --- [           main] o.h.engine.jdbc.spi.SqlExceptionHelper   : SQL Error: 42102, SQLState: 42S02
    2018-10-25 22:20:19.316 ERROR 13637 --- [           main] o.h.engine.jdbc.spi.SqlExceptionHelper   : Table "ISSUE_REPORT" not found; SQL statement:
      ...  <= Here's the exception from running the test...
    

Q: What's the EASIEST change so I can run my unit tests with mySQL, just as I'm able to run my Spring Boot app with mySQL?

Q: Is "@DataJpaTest" the best choice here, or should I try a different annotation?

Q: Must I create a separate "Bean" class? If so, can you point to an example?

================================================================

Thank you for all your replies. Including Simon Martinelli's (now deleted) response.

RESOLUTION:

  1. My original application-test.properties was OK as-is.

  2. I put it in the wrong place: all application.properties files for any profile should typically go in the same project folder: src/main/resources

    <= EXAMPLE: src/main/resources/application-test.properties

  3. @Transactional wasn't relevant here - I removed it. I kept it @ActiveProfiles("test").

  4. Per Karthik R's suggestion, I added @AutoConfigureTestDatabase(replace=Replace.NONE).

  5. At that point, the test successfully read application-test.properties and used MySQL instead of H2.

  6. Final annotations:

    @RunWith(SpringRunner.class)
    @ActiveProfiles("test")
    @DataJpaTest
    @AutoConfigureTestDatabase(replace=Replace.NONE)
    public class IssueRepositoryIntegrationTests {
    

I found this link particularly helpful: Spring Boot – Profile based properties and yaml example

<= I've always found all the material on http://www.mkyong.com extremely good!

like image 919
paulsm4 Avatar asked Oct 26 '18 05:10

paulsm4


2 Answers

By default, the @DataJpaTest uses in memory H2 database for repo tests. Should you need to use the actual DB, you can consider either to disable the auto Configurations or use @SpringBootTest where the whole application web mvc is enabled.

To disable auto config:

@RunWith(SpringRunner.class)
@ActiveProfiles("test")
@Transactional
@DataJpaTest
@AutoConfigureTestDatabase(replace=Replace.NONE)
public class IssueRepositoryIntegrationTests 

@AutoConfigureTestDatabase configures the test H2 DB for you. You can specifically mention not to by above or you can exclude this auto configuration as :

@EnableAutoConfiguration(exclude=AutoConfigureTestDatabase.class)

P.S: : I have not tried the above exclusion myself yet.

For more info on that go thru javadoc: https://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/test/autoconfigure/jdbc/AutoConfigureTestDatabase.html

like image 176
Karthik R Avatar answered Nov 10 '22 16:11

Karthik R


From @DataJpaTest documentation:

By default, tests annotated with @DataJpaTest will use an embedded in-memory database (replacing any explicit or usually auto-configured DataSource).

If you go to documentation you can see this annotation aggregates a lot of other annotations.

@Transactional annotation behaves in different way in test context than in application context:

From spring documentation:

Annotating a test method with @Transactional causes the test to be run within a transaction that is, by default, automatically rolled back after completion of the test.

I believe I provided enough information which answering your question, additionaly you can take a look at following articles:

Configuring Separate Spring DataSource for Tests
Testing with @Configuration Classes and Profiles

like image 28
Kamil W Avatar answered Nov 10 '22 17:11

Kamil W