Question

I have a series of integration-tests that I want to test my spring-mvc/spring-data-jpa stack with. Unfortunately the build time is ridiculous and only getting worse with each new integration tests. It looks as if each separate test is going thru the overhead of creating an embedded database, bean creation etc.

I have a base test class:

@RunWith( SpringJUnit4ClassRunner.class )
@ContextConfiguration( loader = AnnotationConfigContextLoader.class, classes = { JpaConfig.class } )
public class BaseItegration {

private static EmbeddedDatabase database;

@BeforeClass
public static void setUp() throws Exception {
    database = new EmbeddedDatabaseBuilder().setType( EmbeddedDatabaseType.H2 ).setName( "mydb" )
        .addScript( "classpath:embeddedDatabase.sql" ).build();
}


@Test
    public void testInit() {
        Assert.assertNotNull( database );
    }

Where my JpaConfig.java :

@Configuration
@EnableTransactionManagement
@ComponentScan( basePackages = { "org.myproject.service", "org.myproject.utility",
      "org.myproject.controller", "org.myproject.utility.startup",
      "org.myproject.security" } )
@ImportResource( { "classpath:applicationContext.xml", "classpath:myproject-spring-security.xml" } )
public class JpaConfig {
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean() {...}
    @Bean
    public DataSource dataSource() {...}

    <etc>
}

And then finally I try and use it such as:

@TransactionConfiguration( defaultRollback = true )
@Transactional( propagation = Propagation.NESTED )
public class TestContactTypesIT extends BaseItegration {

    @Autowired
    private ContactTypeRefRepository contactTypeRepository;
    @Test
    public void testRepositoryNotNull() {
        Assert.assertNotNull( contactTypeRepository );
    }

...}

When watching the build log, I can see the application init'ing for each test. Is there a way to get the BaseIntegrationTest to only startup one and each test to use that application context and embeddeddatabase?

========

Update

I changed my JpaConfig to this:

@Bean
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder().setType( EmbeddedDatabaseType.H2 ).setName( "mydb" )
                .addScript( "classpath:embeddedDatabase.sql" ).build();
}

and my BaseIntegration now is empty:

@RunWith( SpringJUnit4ClassRunner.class )
@ContextConfiguration( loader = AnnotationConfigContextLoader.class, classes = { JpaConfig.class } )
public abstract class BaseItegration {

}

here is one of the tests that is failing with the error:

InvalidDataAccessResourceUsageException (Table "ADDRESSTYPEREF" not found; SQL statement:

@TransactionConfiguration( defaultRollback = true )
@Transactional( propagation = Propagation.NESTED )
public class TestSeedAddressTypesIT extends BaseItegration {

    @Autowired
    private AddressTypeRefRepository addressTypeRepository;

    @Autowired
    private SeedAddressTypes seedAddressTypes;

    // hack because we can't do a BeforeClass with Autowired
    private boolean seeded = false;

    @Test
    public void testRepositoryNotNull() {
        Assert.assertNotNull( addressTypeRepository );
    }

    @Test
    public void testPopulatedDB() {
        if (!seeded) {
            seedAddressTypes.seed();
            seeded = true;
        }
        List<AddressTypeRef> addressTypes = addressTypeRepository.findAll();
        Assert.assertEquals( 5, addressTypes.size() );

    }
}

However, all integration tests seem to recreate the applicationContext and embedded database. While the build log does not show the datasource being created, I do see each integration test creates a new log4j file, and 45 tests take 15 minutes to build.

Was it helpful?

Solution

It seems @BeforeClass means any static method annotated with @BeforeClass runs before any test method in the class. So if you have a test suite with several classes, it runs as many times as the quantity of your test classes.

Why not init your embedded database and real database in seperate applicationContexts and add only the embedded database applicationContext in your test?

For example(Sorry, I'm not familiar with the java config style):

Listing-1:embedded-database.xml

<jdbc:embedded-database id="dataSource">
    <jdbc:script location="classpath:schema.sql"/>
    <jdbc:script location="classpath:test-data.sql"/>
</jdbc:embedded-database>

Listing-2:BaseItegration.java

@RunWith( SpringJUnit4ClassRunner.class )
@ContextConfiguration( locations = { "classpath:your-application-without-a-real-database.xml", "classpath:"embedded-database.xml" } )
public class BaseItegration {


}

The embedded database is then initiated with the applicationContext(therefore only once).

Another solutin is using Spring Profile and you don't have to seperate databases in seperate xmls.

Listing-1:your-application-context.xml

<bean profile="integrationTest">
    <jdbc:embedded-database id="dataSource">
        <jdbc:script location="classpath:schema.sql"/>
        <jdbc:script location="classpath:test-data.sql"/>
    </jdbc:embedded-database>
<bean>

<bean profile="production">
    //omitted 
</bean>

Listing-2:BaseItegration.java

@RunWith( SpringJUnit4ClassRunner.class )
@ContextConfiguration( locations = { "classpath:your-application-context.xml" } )
@ActiveProfiles("integrationTest")
public class BaseItegration {


}

Remember to active production in your web.xml.

Update:

Modify your BaseIntegration as abstract class to solve no @Test method problem. And is the dataSource created in your log?

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top