Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,14 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'com.mysql:mysql-connector-j'
compileOnly 'org.projectlombok:lombok'
runtimeOnly 'com.h2database:h2'
runtimeOnly 'com.mysql:mysql-connector-j'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.testcontainers:testcontainers:1.19.3'
testImplementation 'org.testcontainers:junit-jupiter:1.19.3'
testImplementation 'org.testcontainers:mysql'
testImplementation 'io.rest-assured:rest-assured:5.3.2'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
Expand Down
4 changes: 4 additions & 0 deletions src/main/resources/application-test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
spring:
jpa:
hibernate:
ddl-auto: create
58 changes: 58 additions & 0 deletions src/test/java/in/koreatech/koin/AcceptanceTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package in.koreatech.koin;

import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;

import in.koreatech.koin.support.DBInitializer;
import io.restassured.RestAssured;
import org.junit.jupiter.api.BeforeEach;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.server.LocalServerPort;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.DynamicPropertyRegistry;
import org.springframework.test.context.DynamicPropertySource;
import org.testcontainers.containers.MySQLContainer;
import org.testcontainers.junit.jupiter.Container;

@SpringBootTest(webEnvironment = RANDOM_PORT)
@Import(DBInitializer.class)
@ActiveProfiles("test")
public abstract class AcceptanceTest {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

설정을 클래스로 따로 분리해서, 인수 테스트를 생성할 때는 해당 클래스를 상속만 받으면 되니까 편리하고 보기 좋은 것 같습니다!

이름은 왜 Acceptance인가요?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

인수테스트에 해당 클래스를 상속받게 만들고자했습니다~!
용어는 인수 검사(acceptance testing)에서 따왔습니다

참고: https://ko.wikipedia.org/wiki/%EC%9D%B8%EC%88%98_%EA%B2%80%EC%82%AC


private static final String ROOT = "test";
private static final String ROOT_PASSWORD = "1234";
Comment on lines +23 to +24
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 부분은 application-test.yml과 같은 설정 파일에 분리할 수 있을 것 같은데, 따로 분리하지 않은 이유가 있을까요??

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

위에서 한수에게 답변한 내용과 동일합니다~

charset 수정을 해당 부분에서 하는 게 맞는지 (schema.sql 에서도 될 것 같고 application.yml에서도 가능할 것 같아요)

해당 MySQL 컨테이너를 띄우는 부분은 오로지 testContainer에서 static하게 단 한번만 사용하므로 전역적으로 yml 파일로 선언하기보다는 작성해주신 대로 해당 코드에서만 사용하는게 어떨까 생각이 듭니다.

별도의 config 파일로 분리할 정도로 중요한 내용이 아니라고 생각되고, 반복되지 않는 작업을 불필요하게 분리하여 관리 요소가 늘어나는 것 같다 라는 의견입니다.

Comment on lines +23 to +24
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

설정이 test, 1234로 되어 있는데 아무거나 넣어도 되는 건가요?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

넹 아무거나 넣어도 됩니다~!
테스트용이라 아무거나 넣었어요


@LocalServerPort
protected int port;

@Autowired
private DBInitializer dataInitializer;

@Container
protected static MySQLContainer container;

@DynamicPropertySource
private static void configureProperties(final DynamicPropertyRegistry registry) {
registry.add("spring.datasource.url", container::getJdbcUrl);
registry.add("spring.datasource.username", () -> ROOT);
registry.add("spring.datasource.password", () -> ROOT_PASSWORD);
}

static {
container = (MySQLContainer) new MySQLContainer("mysql:5.7.34")
.withDatabaseName("test")
.withUsername(ROOT)
.withPassword(ROOT_PASSWORD)
.withCommand("--character-set-server=utf8mb4", "--collation-server=utf8mb4_unicode_ci");
container.start();
}

@BeforeEach
void delete() {
if (RestAssured.port == RestAssured.UNDEFINED_PORT) {
RestAssured.port = port;
}
dataInitializer.clear();
}
}
23 changes: 4 additions & 19 deletions src/test/java/in/koreatech/koin/acceptance/TrackApiTest.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package in.koreatech.koin.acceptance;

import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
import static org.springframework.test.annotation.DirtiesContext.ClassMode.BEFORE_EACH_TEST_METHOD;

import in.koreatech.koin.AcceptanceTest;
import in.koreatech.koin.domain.Member;
import in.koreatech.koin.domain.TechStack;
import in.koreatech.koin.domain.Track;
Expand All @@ -14,21 +12,12 @@
import io.restassured.response.Response;
import java.time.format.DateTimeFormatter;
import org.assertj.core.api.SoftAssertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.server.LocalServerPort;
import org.springframework.http.HttpStatus;
import org.springframework.test.annotation.DirtiesContext;

@SpringBootTest(webEnvironment = RANDOM_PORT)
@DirtiesContext(classMode = BEFORE_EACH_TEST_METHOD)
class TrackApiTest {

@LocalServerPort
int port;
class TrackApiTest extends AcceptanceTest {

@Autowired
private TrackRepository trackRepository;
Expand All @@ -39,11 +28,6 @@ class TrackApiTest {
@Autowired
private MemberRepository memberRepository;

@BeforeEach
void setUp() {
RestAssured.port = port;
}

@Test
@DisplayName("BCSDLab 트랙 정보를 조회한다")
void findTracks() {
Expand Down Expand Up @@ -119,7 +103,8 @@ void findTrack() {
softly.assertThat(response.body().jsonPath().getString("TrackName")).isEqualTo(track.getName());

softly.assertThat(response.body().jsonPath().getList("Members")).hasSize(1);
softly.assertThat(response.body().jsonPath().getInt("Members[0].id")).isEqualTo(member.getId());
softly.assertThat(response.body().jsonPath().getInt("Members[0].id"))
.isEqualTo(member.getId().longValue());
softly.assertThat(response.body().jsonPath().getString("Members[0].name")).isEqualTo(member.getName());
softly.assertThat(response.body().jsonPath().getString("Members[0].student_number"))
.isEqualTo(member.getStudentNumber());
Expand Down
61 changes: 61 additions & 0 deletions src/test/java/in/koreatech/koin/support/DBInitializer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package in.koreatech.koin.support;

import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.TestComponent;
import org.springframework.transaction.annotation.Transactional;

@TestComponent
public class DBInitializer {

private static final int OFF = 0;
private static final int ON = 1;
private static final int COLUMN_INDEX = 1;

private final List<String> tableNames = new ArrayList<>();

@Autowired
private DataSource dataSource;

@PersistenceContext
private EntityManager entityManager;

private void findDatabaseTableNames() {
try (final Statement statement = dataSource.getConnection().createStatement()) {
ResultSet resultSet = statement.executeQuery("SHOW TABLES");
while (resultSet.next()) {
final String tableName = resultSet.getString(COLUMN_INDEX);
tableNames.add(tableName);
}
} catch (Exception e) {
e.printStackTrace();
}
}

private void truncate() {
setForeignKeyCheck(OFF);
for (String tableName : tableNames) {
entityManager.createNativeQuery(String.format("TRUNCATE TABLE %s", tableName)).executeUpdate();
}
setForeignKeyCheck(ON);
}

private void setForeignKeyCheck(int mode) {
entityManager.createNativeQuery(String.format("SET FOREIGN_KEY_CHECKS = %d", mode)).executeUpdate();
}

@Transactional
public void clear() {
if (tableNames.isEmpty()) {
findDatabaseTableNames();
}
entityManager.clear();
truncate();
}
}