|
| 1 | +--- |
| 2 | +name: test |
| 3 | +description: Write, update, or fix tests. Use when asked to test code, create test cases, or debug failing tests. |
| 4 | +--- |
| 5 | + |
| 6 | +# Test Skill — S3Mock |
| 7 | + |
| 8 | +Create and maintain tests for S3Mock (JUnit 5, Mockito, AssertJ, AWS SDK v2). |
| 9 | + |
| 10 | +## When to Use |
| 11 | + |
| 12 | +- Writing new unit or integration tests |
| 13 | +- Fixing failing tests |
| 14 | +- Adding test coverage for S3 operations |
| 15 | +- Creating test fixtures or helpers |
| 16 | + |
| 17 | +## Pre-Flight Checklist |
| 18 | + |
| 19 | +- [ ] Read `AGENTS.md` — especially DO/DON'T and Testing sections |
| 20 | +- [ ] Identify test type needed (unit vs. integration — see below) |
| 21 | +- [ ] Review existing test patterns in the target module |
| 22 | +- [ ] For integration tests, review `S3TestBase` for available helpers |
| 23 | + |
| 24 | +## Test Types in S3Mock |
| 25 | + |
| 26 | +### Unit Tests (`*Test.kt`) |
| 27 | +- **Location**: `server/src/test/kotlin/com/adobe/testing/s3mock/` |
| 28 | +- **Framework**: JUnit 5 + `@SpringBootTest` with `@MockitoBean` for mocking |
| 29 | +- **Assertions**: AssertJ (`assertThat(...)`) |
| 30 | +- **Purpose**: Test services and stores in isolation with Spring context and mocked dependencies |
| 31 | +- **Pattern**: |
| 32 | +```kotlin |
| 33 | +@SpringBootTest(classes = [ServiceConfiguration::class], webEnvironment = SpringBootTest.WebEnvironment.NONE) |
| 34 | +@MockitoBean(types = [BucketService::class, MultipartService::class, MultipartStore::class]) |
| 35 | +internal class ObjectServiceTest : ServiceTestBase() { |
| 36 | + @Autowired |
| 37 | + private lateinit var iut: ObjectService |
| 38 | + |
| 39 | + @Test |
| 40 | + fun `should delete object`() { |
| 41 | + val bucketName = "bucket" |
| 42 | + val key = "key" |
| 43 | + givenBucketWithContents(bucketName, "", listOf(givenS3Object(key))) |
| 44 | + |
| 45 | + iut.deleteObject(bucketName, key) |
| 46 | + |
| 47 | + assertThat(iut.getObject(bucketName, key)).isNull() |
| 48 | + } |
| 49 | +} |
| 50 | +``` |
| 51 | + |
| 52 | +### Integration Tests (`*IT.kt`) |
| 53 | +- **Location**: `integration-tests/src/test/kotlin/com/adobe/testing/s3mock/its/` |
| 54 | +- **Base class**: Extend `S3TestBase` for access to pre-configured `s3Client` (AWS SDK v2) |
| 55 | +- **Assertions**: AssertJ |
| 56 | +- **Purpose**: Test S3Mock end-to-end with real AWS SDK clients against the Docker container |
| 57 | +- **Pattern**: |
| 58 | +```kotlin |
| 59 | +internal class MyFeatureIT : S3TestBase() { |
| 60 | + @Test |
| 61 | + fun `should perform operation`(testInfo: TestInfo) { |
| 62 | + // Arrange — always use unique bucket names |
| 63 | + val bucketName = givenBucket(testInfo) |
| 64 | + |
| 65 | + // Act |
| 66 | + s3Client.putObject( |
| 67 | + PutObjectRequest.builder().bucket(bucketName).key("key").build(), |
| 68 | + RequestBody.fromString("content") |
| 69 | + ) |
| 70 | + |
| 71 | + // Assert |
| 72 | + val response = s3Client.getObject( |
| 73 | + GetObjectRequest.builder().bucket(bucketName).key("key").build() |
| 74 | + ) |
| 75 | + assertThat(response.readAllBytes().decodeToString()).isEqualTo("content") |
| 76 | + } |
| 77 | +} |
| 78 | +``` |
| 79 | + |
| 80 | +## Test Standards |
| 81 | + |
| 82 | +- **Naming**: Use backtick names with descriptive sentences: `` fun `should create bucket successfully`() `` |
| 83 | + - Legacy `testSomething` camelCase naming exists in older tests — refactor to backtick style when touching those tests |
| 84 | +- **Independence**: Each test creates its own resources — never share state between tests |
| 85 | +- **Bucket names**: Use `givenBucket(testInfo)` from `S3TestBase` in integration tests for unique names |
| 86 | +- **Arrange-Act-Assert**: Follow this pattern consistently |
| 87 | +- **Both paths**: Test success cases AND error/exception cases |
| 88 | +- **Error assertions**: Use `assertThatThrownBy { ... }.isInstanceOf(AwsServiceException::class.java)` (AssertJ), not `assertThrows` |
| 89 | +- **SDK version**: Use AWS SDK v2 (`s3Client`) only — SDK v1 has been removed |
| 90 | +- **No JUnit 4**: Use JUnit 5 exclusively (`@Test` from `org.junit.jupiter.api`) |
| 91 | +- **Visibility**: Mark test classes as `internal` |
| 92 | +- **No MockitoExtension**: Use `@SpringBootTest` with `@MockitoBean` for mocking — never `@ExtendWith(MockitoExtension::class)` |
| 93 | +- **Mocking**: Use `@MockitoBean` (class-level `types` or field-level) instead of `@Mock` / `@InjectMocks` |
| 94 | +- **Injection**: Use `@Autowired` for the class under test in Spring Boot tests |
| 95 | + |
| 96 | +## Running Tests |
| 97 | + |
| 98 | +```bash |
| 99 | +# Unit tests (server module) |
| 100 | +./mvnw test -pl server |
| 101 | + |
| 102 | +# All integration tests |
| 103 | +./mvnw verify -pl integration-tests |
| 104 | + |
| 105 | +# Specific integration test class |
| 106 | +./mvnw verify -pl integration-tests -Dit.test=BucketIT |
| 107 | + |
| 108 | +# Specific test method |
| 109 | +./mvnw verify -pl integration-tests -Dit.test=BucketIT#shouldCreateBucket |
| 110 | + |
| 111 | +# Skip Docker (for unit tests only) |
| 112 | +./mvnw test -pl server -DskipDocker |
| 113 | +``` |
| 114 | + |
| 115 | +## Post-Flight Checklist |
| 116 | + |
| 117 | +- [ ] Tests pass locally (`./mvnw test` or `./mvnw verify`) |
| 118 | +- [ ] Tests are independent (can run in any order) |
| 119 | +- [ ] Both success and failure cases covered |
| 120 | +- [ ] Assertions are specific (not just `isNotNull()`) |
| 121 | +- [ ] No hardcoded bucket names (use UUID) |
| 122 | +- [ ] Code style passes (`./mvnw ktlint:format`) |
| 123 | + |
| 124 | +## Troubleshooting Failing Tests |
| 125 | + |
| 126 | +- **Docker not running**: Integration tests require Docker — start Docker Desktop |
| 127 | +- **Port conflict**: Ports 9090/9191 may be in use — check with `lsof -i :9090` |
| 128 | +- **Flaky test**: Ensure test independence — check for shared state or ordering dependencies |
| 129 | +- **Compilation error**: Run `./mvnw clean install -DskipDocker -DskipTests` first |
| 130 | + |
| 131 | +## Output |
| 132 | + |
| 133 | +Provide complete, runnable Kotlin tests following S3Mock conventions and the Arrange-Act-Assert pattern. |
0 commit comments