我正在使用 and 進行集成測驗,testcontainers 并且spring-boot在初始化腳本時遇到問題。我有 2 個腳本:schema.sql和data.sql.
當我使用DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD它時作業正常,但在每次測驗后重新運行一個新容器并不是一個好主意。當然,這會使測驗變得非常緩慢。
另一方面,當我使用時,DirtiesContext.ClassMode.AFTER_CLASS我有這個例外:
org.springframework.jdbc.datasource.init.ScriptStatementFailedException:無法執行類路徑資源 [sql/mariadb/schema.sql] 的 SQL 腳本陳述句 #1:如果存在則洗掉表
client;嵌套例外是 java.sql.SQLIntegrityConstraintViolationException: (conn=4) 無法洗掉或更新父行:外鍵約束在 org.springframework.jdbc.datasource.init.ScriptUtils.executeSqlScript(ScriptUtils.java:282) 處失敗 ~[ spring-jdbc-5.3.13.jar:5.3.13] at ... 引起:java.sql.SQLIntegrityConstraintViolationException: (conn=4) 無法洗掉或更新父行:外鍵約束失敗 ... 引起:org.mariadb.jdbc.internal.util.exceptions.MariaDbSqlException:無法洗掉或更新父行:外鍵約束失敗
基類:
@Testcontainers
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ActiveProfiles("it")
@Sql({"/sql/mariadb/schema.sql", "/sql/mariadb/data.sql"})
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)
public abstract class BaseIntegrationTest implements WithAssertions {
@Container
protected static MariaDBContainer<?> CONTAINER = new MariaDBContainer<>("mariadb:10.6.5");
@Autowired
protected ObjectMapper mapper;
@Autowired
protected WebTestClient webTestClient;
}
集成測驗:
class ClientControllerITest extends BaseIntegrationTest {
@Test
void integrationTest_For_FindAll() {
webTestClient.get()
.uri(ApplicationDataFactory.API_V1 "/clients")
.exchange()
.expectStatus().isOk()
.expectBody(Success.class)
.consumeWith(result -> {
assertThat(Objects.requireNonNull(result.getResponseBody()).getData()).isNotEmpty();
});
}
@Test
void integrationTest_For_FindById() {
webTestClient.get()
.uri(ApplicationDataFactory.API_V1 "/clients/{ID}", CLIENT_ID)
.exchange()
.expectStatus().isOk()
.expectBody(Success.class)
.consumeWith(result -> {
var clients = mapper.convertValue(Objects.requireNonNull(result.getResponseBody()).getData(),
new TypeReference<List<ClientDto>>() {
});
var foundClient = clients.get(0);
assertAll(
() -> assertThat(foundClient.getId()).isEqualTo(CLIENT_ID),
() -> assertThat(foundClient.getFirstName()).isEqualTo(CLIENT_FIRST_NAME),
() -> assertThat(foundClient.getLastName()).isEqualTo(CLIENT_LAST_NAME),
() -> assertThat(foundClient.getTelephone()).isEqualTo(CLIENT_TELEPHONE),
() -> assertThat(foundClient.getGender()).isEqualTo(CLIENT_GENDER_MALE.name())
);
});
}
@Test
void integrationTest_For_Create() {
var newClient = createNewClientDto();
webTestClient.post()
.uri(ApplicationDataFactory.API_V1 "/clients")
.accept(MediaType.APPLICATION_JSON)
.bodyValue(newClient)
.exchange()
.expectStatus().isOk()
.expectBody(Success.class)
.consumeWith(result -> {
var clients = mapper.convertValue(Objects.requireNonNull(result.getResponseBody()).getData(),
new TypeReference<List<ClientDto>>() {
});
var createdClient = clients.get(0);
assertAll(
() -> assertThat(createdClient.getId()).isEqualTo(newClient.getId()),
() -> assertThat(createdClient.getFirstName()).isEqualTo(newClient.getFirstName()),
() -> assertThat(createdClient.getLastName()).isEqualTo(newClient.getLastName()),
() -> assertThat(createdClient.getTelephone()).isEqualTo(newClient.getTelephone()),
() -> assertThat(createdClient.getGender()).isEqualTo(newClient.getGender())
);
});
}
@Test
void integrationTest_For_Update() {
var clientToUpdate = createNewClientDto();
clientToUpdate.setFirstName(CLIENT_EDITED_FIRST_NAME);
webTestClient.put()
.uri(ApplicationDataFactory.API_V1 "/clients")
.accept(MediaType.APPLICATION_JSON)
.bodyValue(clientToUpdate)
.exchange()
.expectStatus().isOk()
.expectBody(Success.class)
.consumeWith(result -> {
var clients = mapper.convertValue(Objects.requireNonNull(result.getResponseBody()).getData(),
new TypeReference<List<ClientDto>>() {
});
var updatedClient = clients.get(0);
assertAll(
() -> assertThat(updatedClient.getId()).isEqualTo(clientToUpdate.getId()),
() -> assertThat(updatedClient.getFirstName()).isEqualTo(clientToUpdate.getFirstName()),
() -> assertThat(updatedClient.getLastName()).isEqualTo(clientToUpdate.getLastName()),
() -> assertThat(updatedClient.getTelephone()).isEqualTo(clientToUpdate.getTelephone()),
() -> assertThat(updatedClient.getGender()).isEqualTo(clientToUpdate.getGender())
);
});
}
@Test
void integrationTest_For_Delete() {
webTestClient.delete()
.uri(ApplicationDataFactory.API_V1 "/clients/{ID}", CLIENT_ID)
.exchange()
.expectStatus().isOk();
}
}
架構.sql:
DROP TABLE IF EXISTS `client`;
CREATE TABLE `client` (
`id` bigint(20) NOT NULL,
`first_name` varchar(255) DEFAULT NULL,
`last_name` varchar(255) DEFAULT NULL,
`gender` varchar(255) DEFAULT NULL,
`telephone` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
資料.sql
INSERT INTO client (id, first_name, last_name, gender, telephone) VALUES(1, 'XXX', 'XXX', 'MALE', 'XXX-XXX-XXXX');
INSERT INTO client (id, first_name, last_name, gender, telephone) VALUES(2, 'XXX', 'XXX', 'MALE', 'XXX-XXX-XXXX');
我錯過了什么?一個建議將非常受歡迎。
uj5u.com熱心網友回復:
@Sql默認情況下,將執行BEFORE_TEST_METHOD. 所以這是在每個測驗方法之前運行的。換句話說,在運行任何測驗之前,都會執行該 SQL。但是當然,通過重復使用同一個資料庫進行多次測驗,如果 sql 腳本不是“冪等的”,換句話說,如果 sql 腳本不能安全地兩次應用于同一個資料庫,這可能會出錯.
為了使其發揮作用,有多種可能性,例如:
添加一個進行
AFTER_TEST_METHOD清理。這將基本上洗掉此測驗添加的所有內容(包括@Sql之前添加的內容)。這樣,您的資料庫在每次測驗運行后都會被“清理”,并且每次運行都可以再次安全地應用相同的 sql 腳本。或者讓它安全地執行多次。這取決于腳本,但如果您可以撰寫 SQL 以允許您運行它兩次而不會出錯,那么這也可以作業。
如果不使用
@Sql,您還可以DatabasePopulator在測驗配置中配置 bean。這樣,您的 SQL 代碼只會在創建整個應用程式背景關系時運行一次。
所有這些方法都可能解決您的問題。
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/450496.html
上一篇:為什么發生RuntimeException時事務不回滾?
下一篇:無法決議值“${spring.datasource.dataSourceClassName}”中的占位符“spring.datasource.dataSourceClassName”
