Блокировка базы данных после выполнения теста метода спока

Я пытаюсь написать интеграционный тест в спок с библиотекой Spring Boot и testcontainers.

Я создал базовый класс IntegrationSpec для всех моих интеграционных испытаний, как показано ниже:

@TypeChecked
@CompileStatic
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = [Application.class])
@Transactional
@Rollback
@ActiveProfiles("integration")
@Testcontainers
class IntegrationSpec extends Specification {

    @Autowired
    protected WebApplicationContext webApplicationContext

    @Autowired
    ObjectMapper jsonObjectMapper

    @Shared
    groovy.sql.Sql sql

    @Autowired
    void setSqlDataSource(DataSource dataSource) {
        this.sql = new Sql(dataSource)
    }

    MockMvc mockMvc

    @Shared
    PostgreSQLContainer postgreSQLContainer = container
    static final String DB_USERNAME = "username"
    static final String DB_PASSWORD = "password"
    static final String DB_NAME = "zlecenia"

    static PostgreSQLContainer container = new PostgreSQLContainer("postgres:9.6.8")
            .withDatabaseName(DB_NAME)
            .withUsername(DB_USERNAME)
            .withPassword(DB_PASSWORD)


    @Before
    void setupMockMvc() {
        mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext)
        //.apply(springSecurity())
                .build()
    }

    def cleanupSpec() {
        if (sql != null)
            sql.close()
        if (postgreSQLContainer.isRunning()) {
            println("[BASE-INTEGRATION-TEST] - Stopping Postgres...")
            postgreSQLContainer.stop()
        }
    }
  //code omitted
}

Когда я запускаю тест с 4 методами:

class BrygadaControllerSpec extends IntegrationSpec implements BrygadaSampleData {

    @Shared
    Clock fixedClock = Clock.fixed(Instant.parse("2019-01-01T00:00:00.00Z"), ZoneId.systemDefault())

    def setupSpec() {
        //to ensure  our tests depends on specified time
        ValidationUtils.setClock(fixedClock)
    }

    def setup() {
        deleteFromTable("brygada")
    }

    def cleanup() {
        deleteFromTable("brygada")
    }

И ниже пример одного из методов:

def "should add new brigade"() {

        given: "brigade which we want to add"
        String dataOd = "01-01-2019", dataDo = "01-02-2019"
        BrygadaDto brygadaDtoRequest = createBrygadaDtoForInsert(Fields.BRYGADA_1_NAME, dataOd, dataDo)

        when: "I go to /brygada (POST)"
        def brygadaResultDto = performPostAndMapResponseToDto("/brygada", brygadaDtoRequest, BrygadaResultDto.class)
        def grantedIdByDB = brygadaResultDto.id

        then: "status is ok"
        brygadaResultDto.responseStatus == ResponseStatus.OK

        and: 'body contains proper values'
        with(brygadaResultDto) {
            it.data.id != null
            it.data.nazwa == Fields.BRYGADA_1_NAME
            it.data.dataOd == toDate(dataOd)
            it.data.dataDo == toDate(dataDo)
        }

        when: "I go to /brygada/$brygadaResultDto.id (GET)"
        brygadaResultDto = performGetAndMapResponseToDto("/brygada/$brygadaResultDto.id", BrygadaResultDto.class)

        then: "status is ok"
        brygadaResultDto.responseStatus == ResponseStatus.OK

        and: "body contains proper value"
        with(brygadaResultDto) {
            it.data.id == grantedIdByDB
            it.data.nazwa == Fields.BRYGADA_1_NAME
            it.data.dataOd == toDate(dataOd)
            it.data.dataDo == toDate(dataDo)
        }
    }

И второй способ:

def "should update existing brigade"() {

        given: "brigade which we want to update exists"
        sql.execute("insert into brygada(id, nazwa, data_od, data_do) values (1, 'Brygada 3', '2019-01-01 00:00', '2019-12-31 00:00')")

        and: "we has defined what we are going to update"
        String nazwa = Fields.BRYGADA_1_NAME, dataOd = "01-01-2019", dataDo = "01-02-2019"
        BrygadaDto brygadaDtoRequest = createBrygadaDtoForUpdate(Fields.BRYGADA_1_ID, nazwa, dataOd, dataDo)

        when: "I go to /brygada/$Fields.BRYGADA_1_ID (PUT)"
        def brygadaResultDto = performPutAndMapResponseToDto("/brygada/$Fields.BRYGADA_1_ID", brygadaDtoRequest, BrygadaResultDto.class)

        then: "status is ok"
        brygadaResultDto.responseStatus == ResponseStatus.OK

        and: "body contains proper value"
        with(brygadaResultDto) {
            it.data.id == Fields.BRYGADA_1_ID
            it.data.nazwa == nazwa
            it.data.dataOd == toDate(dataOd)
            it.data.dataDo == toDate(dataDo)
        }

        when: "I go to /brygada/$brygadaResultDto.id (GET)"
        brygadaResultDto = performGetAndMapResponseToDto("/brygada/$brygadaResultDto.id", BrygadaResultDto.class)

        then: "status is ok"
        brygadaResultDto.responseStatus == ResponseStatus.OK

        and: "body contains proper value"
        with(brygadaResultDto) {
            it.data.id == Fields.BRYGADA_1_ID
            it.data.nazwa == nazwa
            it.data.dataOd == toDate(dataOd)
            it.data.dataDo == toDate(dataDo)
        }
    }

Я вижу в журналах: 2018-12-28 10: 52: 13.410 INFO 27346 --- [main] ostctransaction.TransactionContext: начата транзакция (1) для тестового контекста [DefaultTestContext@f27ea3 testClass = BrygadaControllerSpec, testInstance = pl.com.kartgis.zlecenia. com.kartgis.zlecenia.Application, класс pl.com.kartgis.zlecenia.Application}', contextInitializerClasses = '[]', activeProfiles = '{интеграция}', propertySourceLocations = '{}', propertySourceProperties = '{org.springframework.boot.test.context.SpringBootTestContextBootstrapper=true, server.port=0}', contextCustomizers = set[org.spockframework.spring.mock.SpockContextCustomizer@0, org.springframework.boot.test.autoconfigure.Proa.console, org.springframe work.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory$Customizer@534df152, org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@2145433b, org.springfratestxtOject_jectupOjectBjectExFjects_Directory_Exject_File.dll org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, org.springframework.boot.test.web.client.TestRestTemplateContextCustomizer@17c1bced], resourceBasePath = 'src/main/webappf.s.s или для работы, contextLo. boot.test.context. -> список [[пусто]]]]; менеджер транзакций [org.springframework.orm.jpa.JpaTransactionManager@b67b359]; откат [true] 2018-12-28 10:52:13.412 INFO 27346 --- [ main] oejshContextHandler.application: инициализация Spring TestDispatcherServlet '' 2018-12-28 10:52:13.412 INFO 27346 --- [ main] ostweb.servlet.TestDispatcherServlet: Инициализация сервлета '' 2018-12-28 10:52:13.418 INFO 27346 --- [ main] ostweb.servlet.TestDispatcherServlet: Завершить инициализацию за 6 мс Hibernate: выберите brygada0_.id в качестве id1_2_0_, bryug0 как uuid2_2_0_, brygada0_.data_do как data_do3_2_0_, brygada0_.data_od как data_od4_2_0_, brygada0_.nazwa как nazwa5_2_0_ из zlecenia.brygada brygada0_, где brygada = brygada? Спящий режим: обновление zlecenia.brygada set uuid=?, data_do =?, data_od =?, nazwa =? где id=? Гибернация: выберите brygada0_.id в качестве id1_2_, brygada0_.uuid в качестве uuid2_2_, brygada0_.data_do в качестве data_do3_2_, brygada0_.data_od в качестве data_od4_2_, brygada0_.nazwa в качестве нового юриста

Проблема заключается в том, что после выполнения первого метода тестирования в базе данных postgres возникает некоторая тупиковая ситуация, и проверка никогда не завершается. У меня есть несколько вопросов:

  1. Правильна ли моя тестовая конфигурация? Я имею в виду выражение контейнера (это должен быть статический var и @Shared?)
  2. Я использую liquibase, и я вижу, что сценарии выполняются перед первым тестовым классом - это поведение expexted? Meybe лучше, когда скрипты выполняются перед каждым тестовым классом (сбросить схему / создать и т. Д.)
  3. Каждое выполнение тестового метода вызывает запуск контейнера...

1 ответ

Я использую объект groovy.sql.Sql и аннотацию @Transactional, объект sql использует не одно и то же соединение.

Мы можем создать объект на соединение в текущем тестовом методе (метод транзакционный)

 protected Sql getSqlObject() {
        Connection connection = DataSourceUtils.getConnection(dataSource)
        return new Sql(connection)
    }

И теперь я могу использовать в моем методе тестирования, как показано ниже:

def "should get brigade"() {

        given: "brigade exists"
        getSqlObject().execute("insert into brygada(id, nazwa, data_od, data_do) values (1, 'Brygada 3', '2018-01-01 00:00', '2018-12-31 00:00')")

Я думаю, что проблема решена!

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