Spock方法测试执行后数据库死锁

时间:2018-12-28 10:00:33

标签: postgresql spring-boot docker spock testcontainers

我尝试使用Spring Boot和testcontainers库在spock中编写集成测试。

我为所有集成测试创建了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信息27346 --- [main] ostctransaction.TransactionContext:开始测试上下文的事务(1)[DefaultTestContext @ f27ea3 testClass = BrygadaControllerSpec,testInstance = pl.com.kartgis。 zlecenia.api.web.brygada.BrygadaControllerSpec@44189797,testMethod = $ spock_feature_1_1 @ BrygadaControllerSpec,testException = [null],mergedContextConfiguration = [WebMergedContextConfiguration @ 1ce61929 testClass = BrygadaControllerSpec,location ='{}',classes ='{class pl.com .kartgis.zlecenia.Application,类pl.com.kartgis.zlecenia.Application}',contextInitializerClasses ='[]',activeProfiles ='{integration}',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.properties.PropertyMappingContextCustomizer@0,组织蛋白gframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory$Customizer@534df152,org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@2145433b,org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory $ DuplicateJsonObjectContext7,105fe org.springframework.boot.test.mock.mockito.MockitoContextCustomizer @ 0,org.springframework.boot.test.web.client.TestRestTemplateContextCustomizer @ 17c1bced],resourceBasePath ='src / main / webapp',contextLoader ='org.springframework。 boot.test.context.SpringBootContextLoader',父级= [null]],属性= map ['org.springframework.test.context.web.ServletTestExecutionListener.activateListener'-> false,'org.spockframework.spring.SpringMockTestExecutionListener.MOCKED_BEANS_LIST' ->列表[[空]]]];交易经理[org.springframework.orm.jpa.JpaTransactionManager@b67b359];回滚[true] 2018-12-28 10:52:13.412 INFO 27346 --- [main] o.e.j.s.h.ContextHandler.application:初始化Spring TestDispatcherServlet'' 2018-12-28 10:52:13.412 INFO 27346 --- [main] o.s.t.web.servlet.TestDispatcherServlet:初始化Servlet'' 2018-12-28 10:52:13.418 INFO 27346 --- [main] o.s.t.web.servlet.TestDispatcherServlet:在6毫秒内完成初始化 休眠状态:选择brygada0_.id作为id1_2_0_,brygada0_.uuid作为uuid2_2_0_,brygada0_.data_do作为data_do3_2_0_,brygada0_.data_od作为data_od4_2_0_,brygada0_.nazwa作为nazwa5_ada.0y从zle? 休眠:更新zlecenia.brygada设置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作为nazwa5_g_em_g_em_0_。从zlecenia.bry >

问题是在执行第一个测试方法后,postgres数据库上出现了一些死锁,并且测试从未完成。 我有几个问题:

  1. 我的测试配置是否正确?我的意思是声明一个容器(应该是静态var和@Shared吗?)
  2. 我正在使用liquibase,我看到在第一个测试类之前执行了脚本-扩展了行为吗?当在每个测试类(删除架构/创建等)之前执行脚本时,Meybe更好。
  3. 每次执行测试方法都会引起容器凝视...

1 个答案:

答案 0 :(得分:0)

我正在使用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')")

我认为问题已经解决!