testing
Load when writing tests, modifying test infrastructure, working with E2E test base classes, or improving test coverage.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
Load when writing tests, modifying test infrastructure, working with E2E test base classes, or improving test coverage.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
SOC 직업 분류 기준
| name | testing |
| description | Load when writing tests, modifying test infrastructure, working with E2E test base classes, or improving test coverage. |
Load this context when writing or modifying tests.
| Type | When to Write | Coverage Goal |
|---|---|---|
| E2E | Required for ALL API endpoints | 100% endpoint coverage |
| Unit | Complex logic, exception-heavy code | Selective |
Philosophy: Prefer E2E tests over unit tests. Unit tests only when logic is complex enough to warrant isolated testing.
// All E2E tests extend from E2ETestBase
abstract class E2ETestBase {
@Autowired
protected lateinit var tokenProvider: AuthTokenProvider
@Autowired
protected lateinit var userRepository: UserRepository
@AfterEach
protected open fun tearDown() {
userRepository.deleteAllInBatch()
}
// Create test user and get JWT token
fun createTestUserAndToken(
email: String = "test-${System.currentTimeMillis()}@example.com",
name: String = "Test User",
providerType: ProviderType = ProviderType.TEST,
): Pair<User, String>
}
Reference: src/test/kotlin/com/neki/e2e/E2ETestBase.kt
For domain-specific setup, extend the domain base class:
// Photo domain tests
class CreateFolderE2ETest : FolderE2ETestBase() {
// FolderE2ETestBase extends E2ETestBase
// Adds folder-specific setup/teardown
}
src/test/kotlin/com/neki/
├── e2e/ # E2E tests (organized by domain)
│ ├── E2ETestBase.kt # Base class for all E2E tests
│ ├── auth/
│ │ └── AuthE2ETest.kt
│ ├── photo/
│ │ └── folder/
│ │ ├── FolderE2ETestBase.kt
│ │ ├── CreateFolderE2ETest.kt
│ │ ├── DeleteFolderE2ETest.kt
│ │ ├── GetAllFolderE2ETest.kt
│ │ └── UpdateFolderE2ETest.kt
│ └── user/
│ └── UserE2ETest.kt
├── auth/ # Unit tests (next to domain)
│ └── infra/security/filter/
│ └── AuthMdcFilterTest.kt
├── common/
│ └── filter/
│ └── RequestMdcFilterTest.kt
└── JasyptTest.kt # Utility tests
@Test
fun `should create folder successfully`() {
// 1. Create test user and get token
val (user, token) = createTestUserAndToken()
// 2. Make authenticated request
RestAssured.given()
.header("Authorization", "Bearer $token")
.contentType(ContentType.JSON)
.body(CreateFolderRequest(name = "My Folder"))
.`when`()
.post("/api/folders")
.then()
.statusCode(200)
.body("success", equalTo(true))
.body("data.folderId", notNullValue())
}
@Test
fun `should return error when folder name duplicated`() {
val (user, token) = createTestUserAndToken()
// Create first folder
createFolder(token, "Duplicate Name")
// Try to create duplicate
RestAssured.given()
.header("Authorization", "Bearer $token")
.contentType(ContentType.JSON)
.body(CreateFolderRequest(name = "Duplicate Name"))
.`when`()
.post("/api/folders")
.then()
.statusCode(400)
.body("resultCode", equalTo("D-06")) // CONFLICT_FOLDER
}
Tests run with @ActiveProfiles("test"):
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ActiveProfiles("test")
class MyE2ETest : E2ETestBase() {
// ...
}
| Tool | Purpose |
|---|---|
| Kotest | Test framework with DSL |
| MockK | Mocking library for Kotlin |
| RestAssured | HTTP testing |
| Testcontainers | Database containers |
Encrypt sensitive values for configuration:
// src/test/kotlin/com/neki/JasyptTest.kt
@Test
fun jasyptGeneratTest() {
val text = "value_to_encrypt"
val encrypted = jasyptStringEncryptor.encrypt(text)
println("ENC($encrypted)")
}
createFavoritePhotoImage in
PhotoImageE2ETestBaseExample:
// ✅ GOOD: Helper in base class
abstract class PhotoImageE2ETestBase : E2ETestBase() {
protected fun createFavoritePhotoImage(
userId: Long,
mediaId: Long,
folderId: Long? = null
): PhotoImage {
val photo = createPhotoImage(userId, mediaId, folderId)
favoritePhotoRepository.save(
FavoritePhoto(userId = userId, imageId = photo.id!!)
)
return photo
}
}
// ❌ BAD: Duplicating same helper in multiple test files
class GetFavoritePhotosE2ETest : PhotoImageE2ETestBase() {
fun createFavoritePhotoImage(...) { /* duplicate! */
}
}
Delete dependent entities FIRST to respect foreign key constraints:
@AfterEach
override fun tearDown() {
favoritePhotoRepository.deleteAllInBatch() // Dependent first
photoImageRepository.deleteAllInBatch() // Parent second
folderRepository.deleteAllInBatch()
mediaRepository.deleteAllInBatch()
super.tearDown()
}
@AfterEach (delete dependent entities first)Load when developing API endpoints, designing request/response DTOs, creating controllers, or working with BaseResponse wrapper and exception handling patterns.
Load when designing new domains or modules, refactoring architecture, working with Clean Architecture layers, ports/adapters pattern, or cross-domain communication.
Commit, push, and open a PR following project conventions. Use when creating pull requests.
GitHub PR의 코드 리뷰 코멘트를 읽어와 피드백을 반영하고, 각 코멘트에 답글을 남깁니다.
프로젝트 컨벤션에 맞는 일관된 커밋 메시지를 생성하고 커밋을 수행합니다.
Create a GitHub issue following project conventions. Use when creating issues.