{"id":35140897,"url":"https://github.com/seijikohara/db-tester","last_synced_at":"2026-02-22T02:19:46.770Z","repository":{"id":328325112,"uuid":"1106994667","full_name":"seijikohara/db-tester","owner":"seijikohara","description":"Annotation-driven database testing framework for JVM. Load test data and verify database state using simple annotations, data files, and naming conventions.","archived":false,"fork":false,"pushed_at":"2026-02-19T04:35:27.000Z","size":2488,"stargazers_count":1,"open_issues_count":0,"forks_count":1,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-02-19T07:38:48.654Z","etag":null,"topics":["junit","spock","testing"],"latest_commit_sha":null,"homepage":"https://seijikohara.github.io/db-tester/","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/seijikohara.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2025-11-30T11:31:54.000Z","updated_at":"2026-02-19T04:35:29.000Z","dependencies_parsed_at":"2026-02-19T04:05:13.790Z","dependency_job_id":null,"html_url":"https://github.com/seijikohara/db-tester","commit_stats":null,"previous_names":["seijikohara/db-tester"],"tags_count":7,"template":false,"template_full_name":null,"purl":"pkg:github/seijikohara/db-tester","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/seijikohara%2Fdb-tester","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/seijikohara%2Fdb-tester/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/seijikohara%2Fdb-tester/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/seijikohara%2Fdb-tester/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/seijikohara","download_url":"https://codeload.github.com/seijikohara/db-tester/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/seijikohara%2Fdb-tester/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29703453,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-21T23:35:04.139Z","status":"online","status_checked_at":"2026-02-22T02:00:08.193Z","response_time":110,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["junit","spock","testing"],"created_at":"2025-12-28T11:15:45.226Z","updated_at":"2026-02-22T02:19:46.746Z","avatar_url":"https://github.com/seijikohara.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# DB Tester\n\n[![Test](https://github.com/seijikohara/db-tester/actions/workflows/test.yml/badge.svg)](https://github.com/seijikohara/db-tester/actions/workflows/test.yml)\n[![Maven Central](https://img.shields.io/maven-central/v/io.github.seijikohara/db-tester-bom.svg)](https://search.maven.org/artifact/io.github.seijikohara/db-tester-bom)\n[![License](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)\n[![Docs](https://img.shields.io/badge/Docs-VitePress-646cff.svg)](https://seijikohara.github.io/db-tester/)\n\n\u003cdiv align=\"center\"\u003e\n  \u003cimg src=\"docs/public/favicon.svg\" width=\"200\" alt=\"DB Tester Logo\"\u003e\n\u003c/div\u003e\n\nA 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.\n\n**[Documentation](https://seijikohara.github.io/db-tester/)** · **[Maven Central](https://central.sonatype.com/artifact/io.github.seijikohara/db-tester-bom)** · **[Examples](examples/)**\n\n---\n\n## Quick Start\n\n### JUnit\n\n```java\n@ExtendWith(DatabaseTestExtension.class)\nclass UserRepositoryTest {\n\n    @BeforeAll\n    static void setUp(ExtensionContext context) {\n        DataSource dataSource = createDataSource();\n        DatabaseTestExtension.getRegistry(context).registerDefault(dataSource);\n    }\n\n    @Test\n    @DataSet  // Loads USERS.csv before test\n    @ExpectedDataSet  // Verifies expected/USERS.csv after test\n    void shouldCreateUser() {\n        userRepository.create(new User(\"john\", \"john@example.com\"));\n    }\n}\n```\n\n### Spock\n\n```groovy\n@DatabaseTest\nclass UserRepositorySpec extends Specification {\n\n    @Shared\n    DataSourceRegistry dbTesterRegistry\n\n    def setupSpec() {\n        dbTesterRegistry = new DataSourceRegistry()\n        dbTesterRegistry.registerDefault(createDataSource())\n    }\n\n    @DataSet\n    @ExpectedDataSet\n    def \"should create user\"() {\n        when:\n        userRepository.create(new User(\"john\", \"john@example.com\"))\n\n        then:\n        noExceptionThrown()\n    }\n}\n```\n\n### Kotest\n\n```kotlin\nclass UserRepositorySpec : AnnotationSpec() {\n\n    private val registry = DataSourceRegistry()\n\n    init {\n        extensions(DatabaseTestExtension(registryProvider = { registry }))\n    }\n\n    @BeforeAll\n    fun setupSpec() {\n        registry.registerDefault(createDataSource())\n    }\n\n    @Test\n    @DataSet\n    @ExpectedDataSet\n    fun `should create user`() {\n        userRepository.create(User(\"john\", \"john@example.com\"))\n    }\n}\n```\n\n### Dataset Files\n\n```\nsrc/test/resources/\n└── com/example/UserRepositoryTest/\n    ├── USERS.csv              # Loaded before test\n    └── expected/\n        └── USERS.csv          # Verified after test\n```\n\n**USERS.csv** (preparation):\n\n```csv\nID,NAME,EMAIL\n1,existing,existing@example.com\n```\n\n**expected/USERS.csv** (expectation):\n\n```csv\nID,NAME,EMAIL\n1,existing,existing@example.com\n2,john,john@example.com\n```\n\n---\n\n## Features\n\n| Feature | Description |\n|---------|-------------|\n| Annotation-driven | Declarative test data management with `@DataSet` and `@ExpectedDataSet` |\n| Convention-based | Automatic dataset discovery based on test class package and name |\n| Scenario filtering | Share CSV files across tests using the `[Scenario]` column |\n| Spring Boot integration | Automatic DataSource registration from ApplicationContext |\n| Pure JDBC | No ORM or external testing framework dependencies |\n\n---\n\n## Requirements\n\n| Component | Version |\n|-----------|---------|\n| Java | 21 or later |\n| JUnit | 6 (for JUnit integration) |\n| Spock | 2 with Groovy 5 (for Spock integration) |\n| Kotest | 6 with Kotlin 2 (for Kotest integration) |\n| Spring Boot | 4 (for Spring Boot integration) |\n\n---\n\n## Installation\n\nSelect a module based on your test framework:\n\n| Use Case | Module |\n|----------|--------|\n| JUnit | `db-tester-junit` |\n| JUnit with Spring Boot | `db-tester-junit-spring-boot-starter` |\n| Spock | `db-tester-spock` |\n| Spock with Spring Boot | `db-tester-spock-spring-boot-starter` |\n| Kotest | `db-tester-kotest` |\n| Kotest with Spring Boot | `db-tester-kotest-spring-boot-starter` |\n\n\u003cdetails\u003e\n\u003csummary\u003eAll Modules\u003c/summary\u003e\n\n| Module | Description |\n|--------|-------------|\n| `db-tester-bom` | Bill of Materials for version management |\n| `db-tester-api` | Public API (annotations, configuration, SPI) |\n| `db-tester-core` | Internal implementation |\n| `db-tester-junit` | JUnit extension |\n| `db-tester-spock` | Spock extension |\n| `db-tester-kotest` | Kotest extension |\n| `db-tester-junit-spring-boot-starter` | Spring Boot auto-configuration for JUnit |\n| `db-tester-spock-spring-boot-starter` | Spring Boot auto-configuration for Spock |\n| `db-tester-kotest-spring-boot-starter` | Spring Boot auto-configuration for Kotest |\n\n\u003c/details\u003e\n\n### Gradle\n\n```kotlin\ntestImplementation(platform(\"io.github.seijikohara:db-tester-bom:VERSION\"))\ntestImplementation(\"io.github.seijikohara:db-tester-junit\")\n```\n\n### Maven\n\n```xml\n\u003cdependencyManagement\u003e\n    \u003cdependencies\u003e\n        \u003cdependency\u003e\n            \u003cgroupId\u003eio.github.seijikohara\u003c/groupId\u003e\n            \u003cartifactId\u003edb-tester-bom\u003c/artifactId\u003e\n            \u003cversion\u003e${db-tester.version}\u003c/version\u003e\n            \u003ctype\u003epom\u003c/type\u003e\n            \u003cscope\u003eimport\u003c/scope\u003e\n        \u003c/dependency\u003e\n    \u003c/dependencies\u003e\n\u003c/dependencyManagement\u003e\n\n\u003cdependency\u003e\n    \u003cgroupId\u003eio.github.seijikohara\u003c/groupId\u003e\n    \u003cartifactId\u003edb-tester-junit\u003c/artifactId\u003e\n    \u003cscope\u003etest\u003c/scope\u003e\n\u003c/dependency\u003e\n```\n\n---\n\n## Spring Boot Integration\n\nSpring Boot starters automatically discover and register `DataSource` beans from the ApplicationContext. No manual registration is required.\n\n### JUnit with Spring Boot\n\n```java\n@SpringBootTest\n@ExtendWith(SpringBootDatabaseTestExtension.class)\nclass UserRepositoryTest {\n\n    @Autowired\n    private UserRepository userRepository;\n\n    @Test\n    @DataSet\n    @ExpectedDataSet\n    void shouldCreateUser() {\n        userRepository.save(new User(\"john\", \"john@example.com\"));\n    }\n}\n```\n\n### Spock with Spring Boot\n\n```groovy\n@SpringBootTest\n@SpringBootDatabaseTest\nclass UserRepositorySpec extends Specification {\n\n    @Autowired\n    UserRepository userRepository\n\n    @DataSet\n    @ExpectedDataSet\n    def \"should create user\"() {\n        when:\n        userRepository.save(new User(\"john\", \"john@example.com\"))\n\n        then:\n        noExceptionThrown()\n    }\n}\n```\n\n### Kotest with Spring Boot\n\n```kotlin\n@SpringBootTest\nclass UserRepositorySpec : AnnotationSpec() {\n\n    @Autowired\n    private lateinit var userRepository: UserRepository\n\n    init {\n        extensions(SpringBootDatabaseTestExtension())\n    }\n\n    @Test\n    @DataSet\n    @ExpectedDataSet\n    fun `should create user`() {\n        userRepository.save(User(\"john\", \"john@example.com\"))\n    }\n}\n```\n\n### Configuration Properties\n\nConfigure via `application.properties`:\n\n```properties\ndb-tester.enabled=true\ndb-tester.auto-register-data-sources=true\ndb-tester.convention.data-format=CSV\ndb-tester.convention.expectation-suffix=/expected\ndb-tester.operation.preparation=CLEAN_INSERT\n```\n\nSee the [Configuration](https://seijikohara.github.io/db-tester/specs/04-configuration) documentation for all options.\n\n---\n\n## Usage Examples\n\n### Scenario Filtering\n\nShare CSV files across multiple tests using the `[Scenario]` column:\n\n```csv\n[Scenario],ID,NAME,EMAIL\nshouldCreateUser,1,existing,existing@example.com\nshouldUpdateUser,1,target,target@example.com\nshouldDeleteUser,1,delete_me,delete@example.com\n```\n\nEach test method loads only rows matching its name.\n\n### Custom Resource Location\n\nSpecify explicit resource locations instead of convention-based discovery:\n\n```java\n@DataSet(sources = @DataSetSource(resourceLocation = \"custom/data\"))\n@ExpectedDataSet(sources = @DataSetSource(resourceLocation = \"custom/expected\"))\nvoid testWithCustomLocation() { }\n```\n\n### Column Exclusion\n\nExclude columns (such as timestamps or auto-generated IDs) from verification:\n\n**Per-dataset exclusion** via `@DataSetSource.excludeColumns`:\n\n```java\n@Test\n@DataSet\n@ExpectedDataSet(sources = @DataSetSource(\n    excludeColumns = {\"CREATED_AT\", \"UPDATED_AT\", \"VERSION\"}\n))\nvoid testWithExcludedColumns() {\n    userRepository.create(new User(\"john\", \"john@example.com\"));\n}\n```\n\n**Global exclusion** via `ConventionSettings.globalExcludeColumns`:\n\n```java\n@BeforeAll\nstatic void setUp(ExtensionContext context) {\n    var config = Configuration.builder()\n        .conventions(ConventionSettings.builder()\n            .globalExcludeColumns(Set.of(\"CREATED_AT\", \"UPDATED_AT\"))\n            .build())\n        .build();\n    DatabaseTestExtension.setConfiguration(context, config);\n    DatabaseTestExtension.getRegistry(context).registerDefault(dataSource);\n}\n```\n\n**Spring Boot configuration**:\n\n```properties\ndb-tester.convention.global-exclude-columns=CREATED_AT,UPDATED_AT,VERSION\n```\n\nColumn names are case-insensitive. Per-dataset exclusions are combined with global exclusions.\n\n---\n\n## Configuration\n\n### Operations\n\n| Operation | Description |\n|-----------|-------------|\n| `NONE` | No database operation |\n| `INSERT` | Insert rows |\n| `UPDATE` | Update existing rows |\n| `UPSERT` | Upsert (insert or update) |\n| `DELETE` | Delete specified rows |\n| `DELETE_ALL` | Delete all rows |\n| `TRUNCATE_TABLE` | Truncate tables |\n| `CLEAN_INSERT` | Delete all rows, then insert (default) |\n| `TRUNCATE_INSERT` | Truncate, then insert |\n\n```java\n@DataSet(operation = Operation.INSERT)\n```\n\n### Data Formats\n\n| Format | Extension |\n|--------|-----------|\n| CSV | `.csv` (default) |\n| TSV | `.tsv` |\n\n```java\nConventionSettings conventions = ConventionSettings.builder()\n    .dataFormat(DataFormat.TSV)\n    .build();\n```\n\n### Multiple DataSources\n\n```java\nDataSourceRegistry registry = DatabaseTestExtension.getRegistry(context);\nregistry.registerDefault(primaryDataSource);\nregistry.register(\"secondary\", secondaryDataSource);\n```\n\n```java\n@DataSet(sources = @DataSetSource(dataSourceName = \"secondary\"))\n```\n\n---\n\n## Assertion Output\n\nWhen verification fails, the framework reports differences in YAML format:\n\n```yaml\nsummary:\n  status: FAILED\n  total_differences: 3\ntables:\n  USERS:\n    differences:\n      - path: row_count\n        expected: 3\n        actual: 2\n  ORDERS:\n    differences:\n      - path: \"row[0].STATUS\"\n        expected: COMPLETED\n        actual: PENDING\n        column:\n          type: VARCHAR(50)\n          nullable: true\n      - path: \"row[1].AMOUNT\"\n        expected: 100.00\n        actual: 99.99\n        column:\n          type: \"DECIMAL(10,2)\"\n```\n\n### Output Structure\n\n| Field | Description |\n|-------|-------------|\n| `summary.status` | `FAILED` when differences exist |\n| `summary.total_differences` | Total count of differences |\n| `tables.\u003cname\u003e.differences` | List of differences for each table |\n| `path` | Location: `table_count`, `row_count`, or `row[N].COLUMN` |\n| `expected` / `actual` | Expected and actual values |\n| `column` | JDBC metadata when available |\n\n---\n\n## Troubleshooting\n\n| Error | Cause | Solution |\n|-------|-------|----------|\n| `DataSetLoadException: Could not find dataset directory` | CSV path does not match test class | Verify directory structure matches package path |\n| `DataSourceNotFoundException` | DataSource not registered | Register in `@BeforeAll` or use Spring Boot starter |\n\n---\n\n## Documentation\n\n| Resource | Description |\n|----------|-------------|\n| [Technical Specifications](https://seijikohara.github.io/db-tester/) | Architecture, API, and configuration |\n| [Examples](examples/) | Working test examples for all frameworks |\n\n---\n\n## License\n\nMIT License. See [LICENSE](LICENSE) for details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fseijikohara%2Fdb-tester","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fseijikohara%2Fdb-tester","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fseijikohara%2Fdb-tester/lists"}