https://github.com/seijikohara/db-tester
Annotation-driven database testing framework for JVM. Load test data and verify database state using simple annotations, data files, and naming conventions.
https://github.com/seijikohara/db-tester
junit spock testing
Last synced: 4 months ago
JSON representation
Annotation-driven database testing framework for JVM. Load test data and verify database state using simple annotations, data files, and naming conventions.
- Host: GitHub
- URL: https://github.com/seijikohara/db-tester
- Owner: seijikohara
- License: mit
- Created: 2025-11-30T11:31:54.000Z (6 months ago)
- Default Branch: main
- Last Pushed: 2026-02-19T04:35:27.000Z (4 months ago)
- Last Synced: 2026-02-19T07:38:48.654Z (4 months ago)
- Topics: junit, spock, testing
- Language: Java
- Homepage: https://seijikohara.github.io/db-tester/
- Size: 2.37 MB
- Stars: 1
- Watchers: 0
- Forks: 1
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
- Agents: AGENTS.md
Awesome Lists containing this project
README
# DB Tester
[](https://github.com/seijikohara/db-tester/actions/workflows/test.yml)
[](https://search.maven.org/artifact/io.github.seijikohara/db-tester-bom)
[](https://opensource.org/licenses/MIT)
[](https://seijikohara.github.io/db-tester/)
A database testing framework for JUnit 6, Spock 2, and Kotest 6. Load CSV test data before tests and verify database state after tests using `@DataSet` and `@ExpectedDataSet` annotations.
**[Documentation](https://seijikohara.github.io/db-tester/)** · **[Maven Central](https://central.sonatype.com/artifact/io.github.seijikohara/db-tester-bom)** · **[Examples](examples/)**
---
## Quick Start
### JUnit
```java
@ExtendWith(DatabaseTestExtension.class)
class UserRepositoryTest {
@BeforeAll
static void setUp(ExtensionContext context) {
DataSource dataSource = createDataSource();
DatabaseTestExtension.getRegistry(context).registerDefault(dataSource);
}
@Test
@DataSet // Loads USERS.csv before test
@ExpectedDataSet // Verifies expected/USERS.csv after test
void shouldCreateUser() {
userRepository.create(new User("john", "john@example.com"));
}
}
```
### Spock
```groovy
@DatabaseTest
class UserRepositorySpec extends Specification {
@Shared
DataSourceRegistry dbTesterRegistry
def setupSpec() {
dbTesterRegistry = new DataSourceRegistry()
dbTesterRegistry.registerDefault(createDataSource())
}
@DataSet
@ExpectedDataSet
def "should create user"() {
when:
userRepository.create(new User("john", "john@example.com"))
then:
noExceptionThrown()
}
}
```
### Kotest
```kotlin
class UserRepositorySpec : AnnotationSpec() {
private val registry = DataSourceRegistry()
init {
extensions(DatabaseTestExtension(registryProvider = { registry }))
}
@BeforeAll
fun setupSpec() {
registry.registerDefault(createDataSource())
}
@Test
@DataSet
@ExpectedDataSet
fun `should create user`() {
userRepository.create(User("john", "john@example.com"))
}
}
```
### Dataset Files
```
src/test/resources/
└── com/example/UserRepositoryTest/
├── USERS.csv # Loaded before test
└── expected/
└── USERS.csv # Verified after test
```
**USERS.csv** (preparation):
```csv
ID,NAME,EMAIL
1,existing,existing@example.com
```
**expected/USERS.csv** (expectation):
```csv
ID,NAME,EMAIL
1,existing,existing@example.com
2,john,john@example.com
```
---
## Features
| Feature | Description |
|---------|-------------|
| Annotation-driven | Declarative test data management with `@DataSet` and `@ExpectedDataSet` |
| Convention-based | Automatic dataset discovery based on test class package and name |
| Scenario filtering | Share CSV files across tests using the `[Scenario]` column |
| Spring Boot integration | Automatic DataSource registration from ApplicationContext |
| Pure JDBC | No ORM or external testing framework dependencies |
---
## Requirements
| Component | Version |
|-----------|---------|
| Java | 21 or later |
| JUnit | 6 (for JUnit integration) |
| Spock | 2 with Groovy 5 (for Spock integration) |
| Kotest | 6 with Kotlin 2 (for Kotest integration) |
| Spring Boot | 4 (for Spring Boot integration) |
---
## Installation
Select a module based on your test framework:
| Use Case | Module |
|----------|--------|
| JUnit | `db-tester-junit` |
| JUnit with Spring Boot | `db-tester-junit-spring-boot-starter` |
| Spock | `db-tester-spock` |
| Spock with Spring Boot | `db-tester-spock-spring-boot-starter` |
| Kotest | `db-tester-kotest` |
| Kotest with Spring Boot | `db-tester-kotest-spring-boot-starter` |
All Modules
| Module | Description |
|--------|-------------|
| `db-tester-bom` | Bill of Materials for version management |
| `db-tester-api` | Public API (annotations, configuration, SPI) |
| `db-tester-core` | Internal implementation |
| `db-tester-junit` | JUnit extension |
| `db-tester-spock` | Spock extension |
| `db-tester-kotest` | Kotest extension |
| `db-tester-junit-spring-boot-starter` | Spring Boot auto-configuration for JUnit |
| `db-tester-spock-spring-boot-starter` | Spring Boot auto-configuration for Spock |
| `db-tester-kotest-spring-boot-starter` | Spring Boot auto-configuration for Kotest |
### Gradle
```kotlin
testImplementation(platform("io.github.seijikohara:db-tester-bom:VERSION"))
testImplementation("io.github.seijikohara:db-tester-junit")
```
### Maven
```xml
io.github.seijikohara
db-tester-bom
${db-tester.version}
pom
import
io.github.seijikohara
db-tester-junit
test
```
---
## Spring Boot Integration
Spring Boot starters automatically discover and register `DataSource` beans from the ApplicationContext. No manual registration is required.
### JUnit with Spring Boot
```java
@SpringBootTest
@ExtendWith(SpringBootDatabaseTestExtension.class)
class UserRepositoryTest {
@Autowired
private UserRepository userRepository;
@Test
@DataSet
@ExpectedDataSet
void shouldCreateUser() {
userRepository.save(new User("john", "john@example.com"));
}
}
```
### Spock with Spring Boot
```groovy
@SpringBootTest
@SpringBootDatabaseTest
class UserRepositorySpec extends Specification {
@Autowired
UserRepository userRepository
@DataSet
@ExpectedDataSet
def "should create user"() {
when:
userRepository.save(new User("john", "john@example.com"))
then:
noExceptionThrown()
}
}
```
### Kotest with Spring Boot
```kotlin
@SpringBootTest
class UserRepositorySpec : AnnotationSpec() {
@Autowired
private lateinit var userRepository: UserRepository
init {
extensions(SpringBootDatabaseTestExtension())
}
@Test
@DataSet
@ExpectedDataSet
fun `should create user`() {
userRepository.save(User("john", "john@example.com"))
}
}
```
### Configuration Properties
Configure via `application.properties`:
```properties
db-tester.enabled=true
db-tester.auto-register-data-sources=true
db-tester.convention.data-format=CSV
db-tester.convention.expectation-suffix=/expected
db-tester.operation.preparation=CLEAN_INSERT
```
See the [Configuration](https://seijikohara.github.io/db-tester/specs/04-configuration) documentation for all options.
---
## Usage Examples
### Scenario Filtering
Share CSV files across multiple tests using the `[Scenario]` column:
```csv
[Scenario],ID,NAME,EMAIL
shouldCreateUser,1,existing,existing@example.com
shouldUpdateUser,1,target,target@example.com
shouldDeleteUser,1,delete_me,delete@example.com
```
Each test method loads only rows matching its name.
### Custom Resource Location
Specify explicit resource locations instead of convention-based discovery:
```java
@DataSet(sources = @DataSetSource(resourceLocation = "custom/data"))
@ExpectedDataSet(sources = @DataSetSource(resourceLocation = "custom/expected"))
void testWithCustomLocation() { }
```
### Column Exclusion
Exclude columns (such as timestamps or auto-generated IDs) from verification:
**Per-dataset exclusion** via `@DataSetSource.excludeColumns`:
```java
@Test
@DataSet
@ExpectedDataSet(sources = @DataSetSource(
excludeColumns = {"CREATED_AT", "UPDATED_AT", "VERSION"}
))
void testWithExcludedColumns() {
userRepository.create(new User("john", "john@example.com"));
}
```
**Global exclusion** via `ConventionSettings.globalExcludeColumns`:
```java
@BeforeAll
static void setUp(ExtensionContext context) {
var config = Configuration.builder()
.conventions(ConventionSettings.builder()
.globalExcludeColumns(Set.of("CREATED_AT", "UPDATED_AT"))
.build())
.build();
DatabaseTestExtension.setConfiguration(context, config);
DatabaseTestExtension.getRegistry(context).registerDefault(dataSource);
}
```
**Spring Boot configuration**:
```properties
db-tester.convention.global-exclude-columns=CREATED_AT,UPDATED_AT,VERSION
```
Column names are case-insensitive. Per-dataset exclusions are combined with global exclusions.
---
## Configuration
### Operations
| Operation | Description |
|-----------|-------------|
| `NONE` | No database operation |
| `INSERT` | Insert rows |
| `UPDATE` | Update existing rows |
| `UPSERT` | Upsert (insert or update) |
| `DELETE` | Delete specified rows |
| `DELETE_ALL` | Delete all rows |
| `TRUNCATE_TABLE` | Truncate tables |
| `CLEAN_INSERT` | Delete all rows, then insert (default) |
| `TRUNCATE_INSERT` | Truncate, then insert |
```java
@DataSet(operation = Operation.INSERT)
```
### Data Formats
| Format | Extension |
|--------|-----------|
| CSV | `.csv` (default) |
| TSV | `.tsv` |
```java
ConventionSettings conventions = ConventionSettings.builder()
.dataFormat(DataFormat.TSV)
.build();
```
### Multiple DataSources
```java
DataSourceRegistry registry = DatabaseTestExtension.getRegistry(context);
registry.registerDefault(primaryDataSource);
registry.register("secondary", secondaryDataSource);
```
```java
@DataSet(sources = @DataSetSource(dataSourceName = "secondary"))
```
---
## Assertion Output
When verification fails, the framework reports differences in YAML format:
```yaml
summary:
status: FAILED
total_differences: 3
tables:
USERS:
differences:
- path: row_count
expected: 3
actual: 2
ORDERS:
differences:
- path: "row[0].STATUS"
expected: COMPLETED
actual: PENDING
column:
type: VARCHAR(50)
nullable: true
- path: "row[1].AMOUNT"
expected: 100.00
actual: 99.99
column:
type: "DECIMAL(10,2)"
```
### Output Structure
| Field | Description |
|-------|-------------|
| `summary.status` | `FAILED` when differences exist |
| `summary.total_differences` | Total count of differences |
| `tables..differences` | List of differences for each table |
| `path` | Location: `table_count`, `row_count`, or `row[N].COLUMN` |
| `expected` / `actual` | Expected and actual values |
| `column` | JDBC metadata when available |
---
## Troubleshooting
| Error | Cause | Solution |
|-------|-------|----------|
| `DataSetLoadException: Could not find dataset directory` | CSV path does not match test class | Verify directory structure matches package path |
| `DataSourceNotFoundException` | DataSource not registered | Register in `@BeforeAll` or use Spring Boot starter |
---
## Documentation
| Resource | Description |
|----------|-------------|
| [Technical Specifications](https://seijikohara.github.io/db-tester/) | Architecture, API, and configuration |
| [Examples](examples/) | Working test examples for all frameworks |
---
## License
MIT License. See [LICENSE](LICENSE) for details.