我尝试使用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数据库上出现了一些死锁,并且测试从未完成。
我有几个问题:
答案 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')")
我认为问题已经解决!