Как эффективно создать базовый тестовый класс для интеграционных тестов Spring mvc

У меня есть ряд интеграционных тестов, с которыми я хочу протестировать свой стек spring-mvc/spring-data-jpa. К сожалению, время сборки нелепо и только ухудшается с каждым новым интеграционным тестом. Похоже, что каждый отдельный тест проходит через накладные расходы на создание встроенной базы данных, создание бина и т. Д.

У меня есть базовый тестовый класс:

@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 );
    }

Где мой 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>
}

И, наконец, я пытаюсь использовать его, например:

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

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

...}

При просмотре журнала сборки я вижу, как приложение запускается для каждого теста. Есть ли способ заставить BaseIntegrationTest запускать только один и каждый тест, чтобы использовать этот контекст приложения и встроенную базу данных?

========

Обновить

Я изменил свой JpaConfig на это:

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

и моя BaseIntegration теперь пуста:

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

}

Вот один из тестов, который завершается с ошибкой:

InvalidDataAccessResourceUsageException (таблица "ADDRESSTYPEREF" не найдена; оператор SQL:

@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() );

    }
}

Однако все интеграционные тесты воссоздают applicationContext и встроенную базу данных. Хотя журнал сборки не показывает создаваемый источник данных, я вижу, что каждый интеграционный тест создает новый файл log4j, а 45 тестов занимают 15 минут для сборки.

1 ответ

Решение

Кажется, @BeforeClass означает, что любой статический метод, аннотированный @BeforeClass, запускается перед любым тестовым методом в классе. Поэтому, если у вас есть набор тестов с несколькими классами, он запускается столько же, сколько и количество ваших тестовых классов.

Почему бы не инициализировать вашу встроенную базу данных и реальную базу данных в отдельном applicationContexts и добавить в тест только встроенную базу данных applicationContext?

Например (извините, я не знаком со стилем конфигурации Java):

Листинг-1: встроенная-database.xml

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

Листинг-2:BaseItegration.java

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


}

Затем встроенная база данных инициируется с помощью applicationContext(следовательно, только один раз).

Другой подход заключается в использовании Spring Profile, и вам не нужно разделять базы данных в отдельных xmls.

Листинг-1: ваш-приложения 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>

Листинг-2:BaseItegration.java

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


}

Не забывайте об активном производстве в вашем web.xml.

Обновить:

Измените BaseIntegration как абстрактный класс, чтобы решить проблему с методом @Test. И источник данных создан в вашем журнале?

Другие вопросы по тегам