Skip to content
Open
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
6 changes: 6 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
.gradle/
build/
.git/
.claude/
*.md
src/test/
10 changes: 10 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
FROM gradle:8-jdk21 AS build
WORKDIR /app
COPY . .
RUN gradle bootJar --no-daemon

FROM eclipse-temurin:21-jre
WORKDIR /app
COPY --from=build /app/build/libs/*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
28 changes: 27 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,27 @@
# spring-gift-test
# spring-gift-test

인수 테스트 중심으로 기능을 검증하며, PostgreSQL + Docker Compose 기반 실행을 지원합니다.

## Acceptance Test Docs

- 테스트 전략 문서: [TEST_STRATEGY.md](./TEST_STRATEGY.md)
- 테스트 실행 가이드: [TEST_RUN_GUIDE.md](./TEST_RUN_GUIDE.md)
- AI 활용 기록: [USING_AI.md](./USING_AI.md)

## 빠른 실행

```bash
./gradlew test # Cucumber BDD 테스트 실행(H2)
./gradlew cucumberTest # Docker(App+PostgreSQL)로 Cucumber 실행
./gradlew step1Test # 기존 RestAssured 인수 테스트 실행
```

## 요구사항 3 실행(컨테이너)

```bash
./gradlew dockerBuild
./gradlew dockerUp
curl http://localhost:28080/api/categories
./gradlew cucumberTest
./gradlew dockerDown
```
91 changes: 91 additions & 0 deletions TEST_RUN_GUIDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
# Test 실행 가이드

## 개요
현재 Gradle 설정 기준으로 테스트 태스크는 아래처럼 분리되어 있습니다.

- `./gradlew test`: Cucumber BDD 테스트만 실행
- `./gradlew cucumberTest`: Docker(App + PostgreSQL) 기반 Cucumber 테스트 실행
- `./gradlew step1Test`: 기존 RestAssured 인수 테스트(1단계)만 실행
- `./gradlew dockerBuild/dockerUp/dockerDown`: 애플리케이션 컨테이너 실행/종료

## 1) Cucumber 테스트 실행
명령어:

```bash
./gradlew test
```

실행 대상:

- `gift.cucumber.CucumberTest`
- `src/test/resources/features/*.feature` 시나리오
- step definitions: `src/test/java/gift/cucumber/steps/*.java`

설정 근거:

- `build.gradle`의 `test` 태스크 필터가 `gift.cucumber.CucumberTest`로 제한되어 있음

## 2) Docker(App + PostgreSQL) 기반 Cucumber 실행
명령어:

```bash
./gradlew cucumberTest
```

실행 흐름:

- `dockerComposeUp` 실행 (`docker compose up -d --wait`)
- `gift.cucumber.CucumberTest` 실행 (프로파일: `test`)
- 완료 후 `dockerComposeDown` 실행 (`docker compose down -v`)

설정 근거:

- `build.gradle`의 `cucumberTest` 태스크 (`dependsOn dockerComposeUp`, `finalizedBy dockerComposeDown`)
- `src/test/resources/application-test.properties`의 PostgreSQL 설정

## 3) 컨테이너 실행(요구사항 3)
명령어:

```bash
./gradlew dockerBuild
./gradlew dockerUp
curl http://localhost:28080/api/categories
./gradlew cucumberTest
./gradlew dockerDown
```

검증 포인트:

- `docker compose ps`에서 `app`, `postgres`가 `healthy`
- `curl http://localhost:28080/api/categories` 응답 확인
- `curl http://localhost:28080`가 404인 것은 정상(루트 매핑 없음)

## 4) 1단계 인수 테스트 실행
명령어:

```bash
./gradlew step1Test
```

실행 대상:

- `gift.CategoryAcceptanceTest`
- `gift.ProductAcceptanceTest`
- `gift.GiftAcceptanceTest`

설정 근거:

- `build.gradle`의 `step1Test` 태스크 `includeTestsMatching` 필터

## 5) 개별 테스트 클래스만 실행
특정 클래스만 실행할 때:

```bash
./gradlew test --tests gift.cucumber.CucumberTest
./gradlew step1Test --tests gift.GiftAcceptanceTest
```

## 주의사항
- `./gradlew test`는 기본 실행이며 Docker 없이 실행됩니다.
- `./gradlew cucumberTest`는 `dockerUp` 후 테스트를 실행하고 종료 시 `dockerDown`을 수행합니다.
- Cucumber 시나리오는 `DatabaseCleanUp` 훅으로 시나리오마다 DB를 초기화합니다.
62 changes: 61 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,68 @@ dependencies {
runtimeOnly 'com.h2database:h2'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'io.rest-assured:rest-assured'

// cucumber 관련 의존성
testImplementation 'io.cucumber:cucumber-java:7.22.0'
testImplementation 'io.cucumber:cucumber-spring:7.22.0'
testImplementation 'io.cucumber:cucumber-junit-platform-engine:7.22.0'
testImplementation 'org.junit.platform:junit-platform-suite'

// postgresql 의존성
runtimeOnly 'org.postgresql:postgresql'
}

tasks.withType(Test).configureEach {
testLogging {
events 'passed', 'failed', 'skipped', 'standardOut', 'standardError'
showStandardStreams = true
exceptionFormat = 'full'
}
}

tasks.named('test') {
// 1) ./gradlew test => cucumber 실행
tasks.named('test', Test) {
useJUnitPlatform()
description = 'Runs Cucumber BDD tests'
group = 'verification'
filter {
includeTestsMatching 'gift.cucumber.CucumberTest'
}
}

// 2) ./gradlew cucumberTest => Docker (App + PostgreSQL)로 Cucumber 실행
tasks.register('cucumberTest', Test) {
description = 'Runs Cucumber tests with Docker'
group = 'verification'
useJUnitPlatform()
filter {
includeTestsMatching 'gift.cucumber.CucumberTest'
}
systemProperty 'spring.profiles.active', 'test'
dependsOn 'dockerUp'
finalizedBy 'dockerDown'
}

// 3) ./gradlew step1Test => 기존 인수테스트 실행
tasks.register('step1Test', Test) {
useJUnitPlatform()
description = 'Runs step1 acceptance tests'
group = 'verification'
filter {
includeTestsMatching 'gift.CategoryAcceptanceTest'
includeTestsMatching 'gift.ProductAcceptanceTest'
includeTestsMatching 'gift.GiftAcceptanceTest'
}
}

tasks.register('dockerBuild', Exec) {
commandLine 'docker', 'compose', 'build'
}

tasks.register('dockerUp', Exec) {
commandLine 'docker', 'compose', 'up', '-d', '--wait'
}

tasks.register('dockerDown', Exec) {
commandLine 'docker', 'compose', 'down', '-v'
}
32 changes: 32 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
services:
postgres:
image: postgres:16-alpine
environment:
POSTGRES_DB: gift_test
POSTGRES_USER: gift
POSTGRES_PASSWORD: gift
ports:
- "5432:5432"
healthcheck:
test: ["CMD-SHELL", "pg_isready -U gift -d gift_test"]
interval: 3s
timeout: 5s
retries: 10

app:
build: .
ports:
- "28080:8080"
environment:
SPRING_DATASOURCE_URL: jdbc:postgresql://postgres:5432/gift_test
SPRING_DATASOURCE_USERNAME: gift
SPRING_DATASOURCE_PASSWORD: gift
SPRING_JPA_HIBERNATE_DDL_AUTO: create
depends_on:
postgres:
condition: service_healthy
healthcheck:
test: ["CMD-SHELL", "wget -qO- http://localhost:8080/api/categories || exit 1"]
interval: 5s
timeout: 5s
retries: 15
5 changes: 4 additions & 1 deletion src/test/java/gift/CategoryAcceptanceTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,10 @@ void setUp() {
* C2: 카테고리 목록을 조회한다.
*/
@Test
@Sql(scripts = "classpath:test-data.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
@Sql(
scripts = {"classpath:cleanup.sql", "classpath:test-data.sql"},
executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD
)
void 카테고리_목록을_조회한다() {

// when
Expand Down
7 changes: 5 additions & 2 deletions src/test/java/gift/ProductAcceptanceTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,10 @@ void setUp() {
* P2: 상품 목록을 조회한다.
*/
@Test
@Sql(scripts = "classpath:test-data.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
@Sql(
scripts = {"classpath:cleanup.sql", "classpath:test-data.sql"},
executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD
)
void 상품_목록을_조회한다() {
// when
ExtractableResponse<Response> response = 상품_목록을_조회_요청한다();
Expand Down Expand Up @@ -111,4 +114,4 @@ void setUp() {
.then().log().all()
.extract();
}
}
}
20 changes: 20 additions & 0 deletions src/test/java/gift/cucumber/CucumberSpringConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package gift.cucumber;

import io.cucumber.spring.CucumberContextConfiguration;
import io.restassured.RestAssured;
import jakarta.annotation.PostConstruct;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.server.LocalServerPort;

@CucumberContextConfiguration
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class CucumberSpringConfig {

@LocalServerPort
int port;

@PostConstruct
void setUp() {
RestAssured.port = port;
}
}
17 changes: 17 additions & 0 deletions src/test/java/gift/cucumber/CucumberTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package gift.cucumber;

import org.junit.platform.suite.api.ConfigurationParameter;
import org.junit.platform.suite.api.IncludeEngines;
import org.junit.platform.suite.api.SelectClasspathResource;
import org.junit.platform.suite.api.Suite;

import static io.cucumber.junit.platform.engine.Constants.GLUE_PROPERTY_NAME;
import static io.cucumber.junit.platform.engine.Constants.PLUGIN_PROPERTY_NAME;

@Suite
@IncludeEngines("cucumber")
@SelectClasspathResource("features")
@ConfigurationParameter(key = GLUE_PROPERTY_NAME, value = "gift.cucumber")
@ConfigurationParameter(key = PLUGIN_PROPERTY_NAME, value = "pretty")
public class CucumberTest {
}
21 changes: 21 additions & 0 deletions src/test/java/gift/cucumber/DatabaseCleanUp.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package gift.cucumber;

import io.cucumber.java.Before;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;

public class DatabaseCleanUp {

@Autowired
JdbcTemplate jdbcTemplate;

@Before(order = 0)
public void cleanUp() {
jdbcTemplate.execute("TRUNCATE TABLE wish, option, product, member, category CASCADE");
jdbcTemplate.execute("ALTER SEQUENCE category_id_seq RESTART WITH 1");
jdbcTemplate.execute("ALTER SEQUENCE product_id_seq RESTART WITH 1");
jdbcTemplate.execute("ALTER SEQUENCE option_id_seq RESTART WITH 1");
jdbcTemplate.execute("ALTER SEQUENCE member_id_seq RESTART WITH 1");
jdbcTemplate.execute("ALTER SEQUENCE wish_id_seq RESTART WITH 1");
}
}
Loading