├── .travis.yml ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── .gitignore ├── src ├── test │ ├── resources │ │ └── spring │ │ │ └── context.xml │ └── groovy │ │ └── be │ │ └── janbols │ │ └── spock │ │ └── extension.dbunit │ │ ├── SpecUtils.groovy │ │ ├── SharedDatasourceSpecification.groovy │ │ ├── DatasourceProviderSpecification.groovy │ │ ├── SharedDatasourceProviderSpecification.groovy │ │ ├── DatasourceFromSpringSpecification.groovy │ │ ├── StaticContentSpecification.groovy │ │ ├── SchemaSpecification.groovy │ │ ├── ColumnSensingSpecification.groovy │ │ ├── QualifiedNamesSpecification.groovy │ │ ├── Issue4Specification.groovy │ │ ├── Issue10Specification.groovy │ │ ├── ContentPerFeatureSpecification.groovy │ │ ├── DatasourceFromFieldSpecification.groovy │ │ └── ConfigureTesterSpecification.groovy └── main │ └── groovy │ └── be │ └── janbols │ └── spock │ └── extension │ └── dbunit │ ├── support │ ├── DatasetProvider.groovy │ ├── DataSourceProvider.groovy │ ├── DbUnitFeatureInterceptor.groovy │ └── DbUnitInterceptor.groovy │ ├── DbUnitExtension.groovy │ └── DbUnit.groovy ├── gradlew.bat ├── gradlew └── README.md /.travis.yml: -------------------------------------------------------------------------------- 1 | language: groovy 2 | 3 | before_install: 4 | - chmod +x gradlew -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/janbols/spock-dbunit/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | 3 | .gradle 4 | build 5 | out 6 | 7 | # Package Files # 8 | *.jar 9 | !gradle-wrapper.jar 10 | *.war 11 | *.ear 12 | 13 | 14 | # idea 15 | .idea 16 | atlassian-ide-plugin.xml 17 | *.iml -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Tue Mar 28 20:05:48 CEST 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-3.4.1-bin.zip 7 | -------------------------------------------------------------------------------- /src/test/resources/spring/context.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/test/groovy/be/janbols/spock/extension.dbunit/SpecUtils.groovy: -------------------------------------------------------------------------------- 1 | package be.janbols.spock.extension.dbunit 2 | 3 | import groovy.sql.Sql 4 | import org.apache.tomcat.jdbc.pool.DataSource 5 | 6 | class SpecUtils { 7 | 8 | private SpecUtils() {} 9 | 10 | static DataSource inMemoryDataSource() { 11 | return new DataSource().with { dataSource -> 12 | dataSource.driverClassName = 'org.h2.Driver' 13 | dataSource.url = 'jdbc:h2:mem:' 14 | dataSource.username = 'sa' 15 | dataSource.password = '' 16 | dataSource 17 | } 18 | } 19 | 20 | static void createUserTable(javax.sql.DataSource dataSource){ 21 | assert dataSource 22 | new Sql(dataSource).execute("CREATE TABLE User(id INT PRIMARY KEY, name VARCHAR(255), created TIMESTAMP, expiration TIMESTAMP )") 23 | } 24 | 25 | static void dropUserTable(javax.sql.DataSource dataSource){ 26 | assert dataSource 27 | new Sql(dataSource).execute("drop table User") 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/test/groovy/be/janbols/spock/extension.dbunit/SharedDatasourceSpecification.groovy: -------------------------------------------------------------------------------- 1 | package be.janbols.spock.extension.dbunit 2 | 3 | import groovy.sql.Sql 4 | import org.apache.tomcat.jdbc.pool.DataSource 5 | import spock.lang.Shared 6 | import spock.lang.Specification 7 | 8 | import static be.janbols.spock.extension.dbunit.SpecUtils.* 9 | 10 | /** 11 | * Specification showing that DbUnit uses the datasource in a shared datasource field 12 | */ 13 | class SharedDatasourceSpecification extends Specification{ 14 | 15 | @Shared DataSource dataSource 16 | 17 | @DbUnit def content = { 18 | User(id: 1, name: 'janbols') 19 | } 20 | 21 | 22 | 23 | def setupSpec(){ 24 | dataSource = inMemoryDataSource() 25 | dataSource?.with {createUserTable(it)} 26 | } 27 | 28 | def cleanupSpec() { 29 | dataSource?.with {dropUserTable(it)} 30 | } 31 | 32 | def "selecting from the User table returns the user"() { 33 | when: 34 | def result = new Sql(dataSource).firstRow("select * from User where name = 'janbols'") 35 | then: 36 | result.id == 1 37 | } 38 | 39 | 40 | 41 | 42 | } 43 | -------------------------------------------------------------------------------- /src/test/groovy/be/janbols/spock/extension.dbunit/DatasourceProviderSpecification.groovy: -------------------------------------------------------------------------------- 1 | package be.janbols.spock.extension.dbunit 2 | 3 | import groovy.sql.Sql 4 | import org.apache.tomcat.jdbc.pool.DataSource 5 | import spock.lang.Specification 6 | 7 | import static be.janbols.spock.extension.dbunit.SpecUtils.* 8 | 9 | /** 10 | * Specification showing that DbUnit uses the datasource that is specified in the DbUnit#datasourceProvider closure 11 | */ 12 | class DatasourceProviderSpecification extends Specification{ 13 | 14 | DataSource dataSource 15 | 16 | @DbUnit(datasourceProvider = { 17 | dataSource 18 | }) 19 | def content = { 20 | User(id: 1, name: 'janbols') 21 | } 22 | 23 | def setup(){ 24 | dataSource = inMemoryDataSource() 25 | dataSource?.with {createUserTable(it)} 26 | } 27 | 28 | def cleanup() { 29 | dataSource?.with {dropUserTable(it)} 30 | } 31 | 32 | def "selecting from the User table returns the user"() { 33 | when: 34 | def result = new Sql(dataSource).firstRow("select * from User where name = 'janbols'") 35 | then: 36 | result.id == 1 37 | } 38 | 39 | 40 | 41 | 42 | } 43 | -------------------------------------------------------------------------------- /src/test/groovy/be/janbols/spock/extension.dbunit/SharedDatasourceProviderSpecification.groovy: -------------------------------------------------------------------------------- 1 | package be.janbols.spock.extension.dbunit 2 | 3 | import groovy.sql.Sql 4 | import org.apache.tomcat.jdbc.pool.DataSource 5 | import spock.lang.Shared 6 | import spock.lang.Specification 7 | 8 | import static be.janbols.spock.extension.dbunit.SpecUtils.* 9 | 10 | /** 11 | * Specification showing that DbUnit uses the datasource that is specified in the DbUnit#datasourceProvider closure referencing a shared datasource 12 | */ 13 | class SharedDatasourceProviderSpecification extends Specification{ 14 | 15 | @Shared DataSource dataSource 16 | 17 | @DbUnit(datasourceProvider = { 18 | dataSource 19 | }) 20 | def content = { 21 | User(id: 1, name: 'janbols') 22 | } 23 | 24 | def setupSpec(){ 25 | dataSource = inMemoryDataSource() 26 | dataSource?.with {createUserTable(it)} 27 | } 28 | 29 | def cleanupSpec() { 30 | dataSource?.with {dropUserTable(it)} 31 | } 32 | 33 | def "selecting from the User table returns the user"() { 34 | when: 35 | def result = new Sql(dataSource).firstRow("select * from User where name = 'janbols'") 36 | then: 37 | result.id == 1 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/test/groovy/be/janbols/spock/extension.dbunit/DatasourceFromSpringSpecification.groovy: -------------------------------------------------------------------------------- 1 | package be.janbols.spock.extension.dbunit 2 | 3 | import groovy.sql.Sql 4 | import org.apache.tomcat.jdbc.pool.DataSource 5 | import org.springframework.beans.factory.annotation.Autowired 6 | import org.springframework.test.context.ContextConfiguration 7 | import spock.lang.Specification 8 | 9 | import static SpecUtils.createUserTable 10 | import static SpecUtils.dropUserTable 11 | 12 | /** 13 | * Specification showing that DbUnit uses the datasource autowired by a spring config 14 | */ 15 | @ContextConfiguration(locations='classpath:/spring/context.xml') 16 | class DatasourceFromSpringSpecification extends Specification{ 17 | 18 | @Autowired DataSource dataSource 19 | 20 | @DbUnit 21 | def content = { 22 | User(id: 1, name: 'janbols') 23 | } 24 | 25 | 26 | def setup(){ 27 | dataSource?.with {createUserTable(it)} 28 | } 29 | 30 | def cleanup() { 31 | dataSource?.with {dropUserTable(it)} 32 | } 33 | 34 | def "selecting from the User table returns the user"() { 35 | when: 36 | def result = new Sql(dataSource).firstRow("select * from User where name = 'janbols'") 37 | then: 38 | result.id == 1 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/test/groovy/be/janbols/spock/extension.dbunit/StaticContentSpecification.groovy: -------------------------------------------------------------------------------- 1 | package be.janbols.spock.extension.dbunit 2 | 3 | import groovy.sql.Sql 4 | import org.apache.tomcat.jdbc.pool.DataSource 5 | import spock.lang.Specification 6 | 7 | import static be.janbols.spock.extension.dbunit.SpecUtils.* 8 | 9 | /** 10 | * Specification showing how you can leverage closures specified as static content for easier reuse 11 | */ 12 | class StaticContentSpecification extends Specification { 13 | 14 | DataSource dataSource 15 | 16 | //content field references a static closure specified in another class 17 | @DbUnit 18 | def content = DbData.userData 19 | 20 | def setup() { 21 | dataSource = inMemoryDataSource() 22 | dataSource?.with {createUserTable(it)} 23 | } 24 | 25 | def cleanup() { 26 | dataSource?.with {dropUserTable(it)} 27 | } 28 | 29 | def "selecting from the User table returns the user"() { 30 | when: 31 | def result = new Sql(dataSource).firstRow("select * from User where name = 'janbols'") 32 | then: 33 | result.id == 1 34 | } 35 | 36 | 37 | } 38 | 39 | /** 40 | * Class containing reusable closures of db content 41 | */ 42 | class DbData { 43 | static def userData = { User(id: 1, name: 'janbols') } 44 | } -------------------------------------------------------------------------------- /src/test/groovy/be/janbols/spock/extension.dbunit/SchemaSpecification.groovy: -------------------------------------------------------------------------------- 1 | package be.janbols.spock.extension.dbunit 2 | 3 | import groovy.sql.Sql 4 | import org.apache.tomcat.jdbc.pool.DataSource 5 | import spock.lang.Specification 6 | 7 | import static SpecUtils.inMemoryDataSource 8 | 9 | /** 10 | * Specification showing how to specify the schema to use using the DbUnit#schema field 11 | */ 12 | class SchemaSpecification extends Specification { 13 | 14 | DataSource dataSource 15 | 16 | @DbUnit(schema = "otherSchema") 17 | def content = { 18 | User(id: 1, name: 'janbols') 19 | } 20 | 21 | def setup() { 22 | dataSource = inMemoryDataSource() 23 | def sql = new Sql(dataSource) 24 | sql.execute("CREATE SCHEMA if not exists otherSchema") 25 | sql.execute("CREATE TABLE otherSchema.User(id INT PRIMARY KEY, name VARCHAR(255))") 26 | } 27 | 28 | def cleanup() { 29 | new Sql(dataSource).execute("drop table otherSchema.User") 30 | new Sql(dataSource).execute("drop schema otherSchema") 31 | } 32 | 33 | def "dbUnit fills the table in the correct schema when specifying it in the annotation"() { 34 | when: 35 | def result = new Sql(dataSource).firstRow("select * from otherSchema.User where name = 'janbols'") 36 | 37 | then: 38 | result.id == 1 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/test/groovy/be/janbols/spock/extension.dbunit/ColumnSensingSpecification.groovy: -------------------------------------------------------------------------------- 1 | package be.janbols.spock.extension.dbunit 2 | 3 | import groovy.sql.Sql 4 | import org.apache.tomcat.jdbc.pool.DataSource 5 | import spock.lang.Shared 6 | import spock.lang.Specification 7 | 8 | import static be.janbols.spock.extension.dbunit.SpecUtils.createUserTable 9 | import static be.janbols.spock.extension.dbunit.SpecUtils.dropUserTable 10 | import static be.janbols.spock.extension.dbunit.SpecUtils.inMemoryDataSource 11 | 12 | /** 13 | * When {@link DbUnit#columnSensing()} is true, 14 | * extra columns in rows after the first one are not ignored. 15 | */ 16 | class ColumnSensingSpecification extends Specification { 17 | @Shared 18 | DataSource dataSource 19 | 20 | @DbUnit(columnSensing = true) 21 | def content = { 22 | User(id: 1, /* The entry for name is intentionally missing */) 23 | User(id: 2, name: 'joe') 24 | } 25 | 26 | def setupSpec() { 27 | dataSource = inMemoryDataSource() 28 | dataSource?.with { createUserTable(it) } 29 | } 30 | 31 | def cleanupSpec() { 32 | dataSource?.with { dropUserTable(it) } 33 | } 34 | 35 | def "value for name column is is inserted"() { 36 | when: 37 | def result = new Sql(dataSource).firstRow("select * from User where name = 'joe'") 38 | then: 39 | result.id == 2 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/test/groovy/be/janbols/spock/extension.dbunit/QualifiedNamesSpecification.groovy: -------------------------------------------------------------------------------- 1 | package be.janbols.spock.extension.dbunit 2 | 3 | import groovy.sql.Sql 4 | import org.apache.tomcat.jdbc.pool.DataSource 5 | import org.dbunit.IDatabaseTester 6 | import org.dbunit.database.DatabaseConfig 7 | import spock.lang.Issue 8 | import spock.lang.Specification 9 | 10 | import static SpecUtils.inMemoryDataSource 11 | 12 | /** 13 | * Specification showing how to use qualified table names 14 | */ 15 | @Issue("12") 16 | class QualifiedNamesSpecification extends Specification { 17 | 18 | DataSource dataSource 19 | 20 | @DbUnit( configure = { IDatabaseTester it -> 21 | it.connection.config.setProperty(DatabaseConfig.FEATURE_QUALIFIED_TABLE_NAMES, true) 22 | }) 23 | 24 | def content = { 25 | 'Foo.Bar'(id: 42) 26 | } 27 | 28 | def setup() { 29 | dataSource = inMemoryDataSource() 30 | def sql = new Sql(dataSource) 31 | sql.execute("CREATE SCHEMA if not exists Foo") 32 | sql.execute("CREATE TABLE Foo.Bar(id INT PRIMARY KEY)") 33 | } 34 | 35 | def cleanup() { 36 | new Sql(dataSource).execute("drop table Foo.Bar") 37 | new Sql(dataSource).execute("drop schema Foo") 38 | } 39 | 40 | def "dbUnit fills the table in the correct schema when using qualified names"() { 41 | when: 42 | def foobar = new Sql(dataSource).firstRow("select * from Foo.Bar") 43 | 44 | then: 45 | foobar.id == 42 46 | } 47 | } -------------------------------------------------------------------------------- /src/test/groovy/be/janbols/spock/extension.dbunit/Issue4Specification.groovy: -------------------------------------------------------------------------------- 1 | package be.janbols.spock.extension.dbunit 2 | 3 | import groovy.sql.Sql 4 | import org.apache.tomcat.jdbc.pool.DataSource 5 | import org.dbunit.IDatabaseTester 6 | import org.dbunit.operation.DatabaseOperation 7 | import org.springframework.beans.factory.annotation.Autowired 8 | import org.springframework.test.context.ContextConfiguration 9 | import spock.lang.Issue 10 | import spock.lang.Specification 11 | 12 | /** 13 | * 14 | */ 15 | @ContextConfiguration(locations='classpath:/spring/context.xml') 16 | class Issue4Specification extends Specification{ 17 | 18 | @Autowired DataSource dataSource 19 | 20 | @DbUnit(configure = { IDatabaseTester it -> 21 | it.setUpOperation = DatabaseOperation.CLEAN_INSERT 22 | it.tearDownOperation = DatabaseOperation.TRUNCATE_TABLE 23 | }) 24 | def content = { 25 | User(name: 'test1', ip: '1.2.3.4') 26 | User(name: 'test2', ip: '127.0.0.1') 27 | } 28 | 29 | 30 | def setup(){ 31 | new Sql(dataSource).execute("CREATE TABLE User(name VARCHAR(255), ip VARCHAR(255))") 32 | } 33 | 34 | def cleanup() { 35 | new Sql(dataSource).execute("drop table User") 36 | } 37 | 38 | 39 | 40 | @Issue("4") 41 | def "when selecting all User rows that where inserted, the results are returned"() { 42 | when: 43 | def result = new Sql(dataSource).rows("select * from User order by name") 44 | then: 45 | result == [ 46 | [NAME: 'test1', IP: '1.2.3.4'] 47 | , [NAME: 'test2', IP: '127.0.0.1'] 48 | ] 49 | } 50 | 51 | 52 | 53 | 54 | } 55 | -------------------------------------------------------------------------------- /src/test/groovy/be/janbols/spock/extension.dbunit/Issue10Specification.groovy: -------------------------------------------------------------------------------- 1 | package be.janbols.spock.extension.dbunit 2 | 3 | import groovy.sql.Sql 4 | import org.apache.tomcat.jdbc.pool.DataSource 5 | import org.dbunit.IDatabaseTester 6 | import org.dbunit.database.DatabaseConfig 7 | import org.dbunit.dataset.Column 8 | import org.dbunit.dataset.filter.IColumnFilter 9 | import org.dbunit.operation.DatabaseOperation 10 | import spock.lang.Issue 11 | import spock.lang.Specification 12 | 13 | import static be.janbols.spock.extension.dbunit.SpecUtils.inMemoryDataSource 14 | 15 | 16 | @Issue("10") 17 | class Issue10Specification extends Specification { 18 | 19 | DataSource dataSource 20 | 21 | def setup() { 22 | dataSource = inMemoryDataSource() 23 | new Sql(dataSource).execute("CREATE TABLE tableWithoutPK(no_key1 VARCHAR(255), no_key2 VARCHAR(255))") 24 | } 25 | 26 | def cleanup() { 27 | new Sql(dataSource).execute("drop table tableWithoutPK") 28 | } 29 | 30 | 31 | @DbUnit(configure = { IDatabaseTester it -> 32 | it.connection.config.setProperty(DatabaseConfig.PROPERTY_PRIMARY_KEY_FILTER, new MyPrimaryKeyFilter()) 33 | it.setUpOperation = DatabaseOperation.REFRESH 34 | it.tearDownOperation = DatabaseOperation.NONE 35 | }, content = { 36 | tableWithoutPK(no_key1: 'value1', no_key2: 'value2') 37 | }) 38 | def "add value to table without pk"() { 39 | when: 40 | def result = new Sql(dataSource).rows("select * from tableWithoutPK") 41 | then: 42 | result[0].no_key1 == 'value1' 43 | result[0].no_key2 == 'value2' 44 | } 45 | 46 | static class MyPrimaryKeyFilter implements IColumnFilter { 47 | @Override 48 | boolean accept(String tableName, Column column) { 49 | return true 50 | } 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /src/test/groovy/be/janbols/spock/extension.dbunit/ContentPerFeatureSpecification.groovy: -------------------------------------------------------------------------------- 1 | package be.janbols.spock.extension.dbunit 2 | 3 | import groovy.sql.Sql 4 | import org.apache.tomcat.jdbc.pool.DataSource 5 | import spock.lang.Specification 6 | 7 | import static be.janbols.spock.extension.dbunit.SpecUtils.* 8 | 9 | /** 10 | * Specification showing how to override the DbUnit content in specific features. 11 | */ 12 | class ContentPerFeatureSpecification extends Specification { 13 | 14 | DataSource dataSource 15 | 16 | //default db content for all features that don't override this one 17 | @DbUnit 18 | def content = { 19 | User(id: 3, name: 'dinatersago') 20 | } 21 | 22 | def setup() { 23 | dataSource = inMemoryDataSource() 24 | dataSource?.with {createUserTable(it)} 25 | } 26 | 27 | def cleanup() { 28 | dataSource?.with {dropUserTable(it)} 29 | } 30 | 31 | 32 | //DbUnit on a feature overrides the DbUnit annotation in the content field 33 | @DbUnit(content = { 34 | User(id: 1, name: 'janbols') 35 | }) 36 | def "Specifying the content as a method level annotation inserts the data in the User table"() { 37 | when: 38 | def result = new Sql(dataSource).firstRow("select * from User where name = 'janbols'") 39 | then: 40 | result.id == 1 41 | } 42 | 43 | @DbUnit(content = { 44 | User(id: 2, name: 'jefkevermeulen') 45 | }) 46 | def "Specifying other content as a method level annotation inserts the data in the User table"() { 47 | when: 48 | def result = new Sql(dataSource).firstRow("select * from User where name = 'jefkevermeulen'") 49 | then: 50 | result.id == 2 51 | } 52 | 53 | def "Not specifying content as a method level annotation inserts the data from the field in the User table"() { 54 | when: 55 | def result = new Sql(dataSource).firstRow("select * from User where name = 'dinatersago'") 56 | then: 57 | result.id == 3 58 | } 59 | 60 | 61 | 62 | } 63 | -------------------------------------------------------------------------------- /src/test/groovy/be/janbols/spock/extension.dbunit/DatasourceFromFieldSpecification.groovy: -------------------------------------------------------------------------------- 1 | package be.janbols.spock.extension.dbunit 2 | 3 | import groovy.sql.Sql 4 | import org.apache.tomcat.jdbc.pool.DataSource 5 | import spock.lang.Specification 6 | 7 | import static SpecUtils.inMemoryDataSource 8 | 9 | /** 10 | * Specification showing that DbUnit uses the datasource that is specified as a field 11 | */ 12 | class DatasourceFromFieldSpecification extends Specification { 13 | 14 | DataSource dataSource 15 | 16 | @DbUnit 17 | def content = { 18 | User(id: 1, name: 'janbols') 19 | User(id: 2, name: 'bluepoet', ip: '127.0.0.1') 20 | 21 | Other_User(id: 1, name: 'bluepoet', ip: '127.0.0.1') 22 | Other_User(id: 2, name: 'janbols') 23 | Other_User(id: 3, name: 'tester', ip: '1.2.3.4') 24 | } 25 | 26 | def setup() { 27 | dataSource = inMemoryDataSource() 28 | new Sql(dataSource).execute("CREATE TABLE User(id INT PRIMARY KEY, name VARCHAR(255), ip VARCHAR(50))") 29 | new Sql(dataSource).execute("CREATE TABLE Other_User(id INT PRIMARY KEY, name VARCHAR(255), ip VARCHAR(50))") 30 | } 31 | 32 | def cleanup() { 33 | new Sql(dataSource).execute("drop table User") 34 | new Sql(dataSource).execute("drop table Other_User") 35 | } 36 | 37 | def "selecting from the User table returns the user"() { 38 | when: 39 | def result = new Sql(dataSource).firstRow("select * from User where name = 'janbols'") 40 | then: 41 | result.id == 1 42 | } 43 | 44 | def "Check the data without putting the data of the ip field first."() { 45 | when: 46 | def result = new Sql(dataSource).firstRow("select * from User where name = 'bluepoet'") 47 | 48 | then: 49 | result.id == 2 50 | result.ip == null 51 | } 52 | 53 | def "Put the data of the ip field first and check the data."() { 54 | when: 55 | def result = new Sql(dataSource).rows("select * from Other_User") 56 | 57 | then: 58 | result == [ 59 | [ID: 1, NAME: 'bluepoet', IP: '127.0.0.1'], 60 | [ID: 2, NAME: 'janbols', IP: null], 61 | [ID: 3, NAME: 'tester', IP: '1.2.3.4'] 62 | ] 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/main/groovy/be/janbols/spock/extension/dbunit/support/DatasetProvider.groovy: -------------------------------------------------------------------------------- 1 | package be.janbols.spock.extension.dbunit.support 2 | 3 | import be.janbols.spock.extension.dbunit.DbUnit 4 | import groovy.xml.MarkupBuilder 5 | import org.dbunit.dataset.DataSetException 6 | import org.dbunit.dataset.ReplacementDataSet 7 | import org.dbunit.dataset.xml.FlatXmlDataSetBuilder 8 | import org.spockframework.runtime.extension.ExtensionException 9 | import org.spockframework.runtime.model.FieldInfo 10 | import org.spockframework.util.Nullable 11 | 12 | /** 13 | * Provides the xml data set from the {@link DbUnit} annotation or from an annotated field 14 | */ 15 | class DataSetProvider { 16 | 17 | private final DbUnit dbUnitAnnotation 18 | private final FieldInfo dataFieldInfo 19 | 20 | DataSetProvider(DbUnit dbUnitAnnotation, @Nullable FieldInfo dataFieldInfo) { 21 | this.dbUnitAnnotation = dbUnitAnnotation 22 | this.dataFieldInfo = dataFieldInfo 23 | } 24 | 25 | ReplacementDataSet findDataSet(Object target) { 26 | def dataSetClosure = dataFieldInfo?.readValue(target) 27 | if (!dataSetClosure && dbUnitAnnotation.content() && Closure.isAssignableFrom(dbUnitAnnotation.content())) { 28 | dataSetClosure = dbUnitAnnotation.content().newInstance(target, target) 29 | } 30 | 31 | String dataSetAsString = writeXmlDataSet(dataSetClosure as Closure) 32 | if (!dataSetAsString) { 33 | throw new ExtensionException("Failed to find a the data set. Specify one as a DbUnit-annotated field or provide one using @DbUnit.content") 34 | } 35 | 36 | return replacementDataSet(new StringReader(dataSetAsString)) 37 | } 38 | 39 | private static String writeXmlDataSet(Closure dataSetClosure) { 40 | def xmlWriter = new StringWriter() 41 | def builder = new MarkupBuilder(xmlWriter) 42 | builder.dataset(dataSetClosure) 43 | return xmlWriter as String 44 | } 45 | 46 | private ReplacementDataSet replacementDataSet(Reader input) throws DataSetException { 47 | def flatXmlDataSet = new FlatXmlDataSetBuilder().setColumnSensing(dbUnitAnnotation.columnSensing()).build(input) 48 | 49 | def dataSet = new ReplacementDataSet(flatXmlDataSet) 50 | dataSet.addReplacementObject("[NULL]", null); 51 | dataSet.addReplacementObject("[NOW]", new Date()); 52 | return dataSet; 53 | } 54 | 55 | 56 | } 57 | -------------------------------------------------------------------------------- /src/main/groovy/be/janbols/spock/extension/dbunit/DbUnitExtension.groovy: -------------------------------------------------------------------------------- 1 | package be.janbols.spock.extension.dbunit 2 | 3 | import be.janbols.spock.extension.dbunit.support.DbUnitInterceptor 4 | import org.spockframework.runtime.extension.AbstractAnnotationDrivenExtension 5 | import org.spockframework.runtime.extension.ExtensionException 6 | import org.spockframework.runtime.model.FeatureInfo 7 | import org.spockframework.runtime.model.FieldInfo 8 | import org.spockframework.runtime.model.SpecInfo 9 | 10 | /** 11 | * Extension for DbUnit annotation. 12 | */ 13 | class DbUnitExtension extends AbstractAnnotationDrivenExtension { 14 | 15 | private DbUnitInterceptor fieldInterceptor 16 | 17 | @Override 18 | void visitFieldAnnotation(DbUnit annotation, FieldInfo field) { 19 | if (fieldInterceptor) throw new ExtensionException("Expected maximum one field annotated with @DbUnit") 20 | if (annotation.content() != Object) throw new ExtensionException("Specifying the content of the database is only supported for annotations on a feature") 21 | fieldInterceptor = new DbUnitInterceptor(field, annotation) 22 | } 23 | 24 | @Override 25 | void visitFeatureAnnotation(DbUnit annotation, FeatureInfo feature) { 26 | def interceptor = new DbUnitInterceptor(feature, annotation) 27 | feature.spec.addSetupSpecInterceptor(interceptor) 28 | feature.spec.addSetupInterceptor(interceptor) 29 | feature.featureMethod.addInterceptor(interceptor) 30 | feature.spec.addCleanupInterceptor(interceptor) 31 | 32 | } 33 | 34 | @Override 35 | void visitSpec(SpecInfo spec) { 36 | //Note: spring integration works because the SpringExtension is a global extension and is executed before this one. 37 | if (fieldInterceptor) { 38 | spec.addSetupSpecInterceptor(fieldInterceptor) 39 | spec.addSetupInterceptor(fieldInterceptor) 40 | //add field interceptor only to those features that aren't annotated yet by any DbUnit annotation 41 | spec.features 42 | .findAll { f -> !f.featureMethod.reflection.annotations*.annotationType().contains(DbUnit) } 43 | .each { f -> f.featureMethod.addInterceptor(fieldInterceptor) 44 | } 45 | spec.addCleanupInterceptor(fieldInterceptor) 46 | } 47 | } 48 | 49 | 50 | } 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /src/test/groovy/be/janbols/spock/extension.dbunit/ConfigureTesterSpecification.groovy: -------------------------------------------------------------------------------- 1 | package be.janbols.spock.extension.dbunit 2 | 3 | import groovy.sql.Sql 4 | 5 | // 6 | import org.apache.tomcat.jdbc.pool.DataSource 7 | import org.dbunit.IDatabaseTester 8 | import org.dbunit.database.DatabaseConfig 9 | import org.dbunit.dataset.ReplacementDataSet 10 | import org.dbunit.ext.h2.H2DataTypeFactory 11 | import org.dbunit.operation.DatabaseOperation 12 | import org.joda.time.LocalDate 13 | import org.joda.time.LocalDateTime 14 | import org.springframework.beans.factory.annotation.Autowired 15 | import org.springframework.test.context.ContextConfiguration 16 | import spock.lang.Specification 17 | 18 | import static SpecUtils.createUserTable 19 | import static SpecUtils.dropUserTable 20 | 21 | /** 22 | * Specification showing how to configure the database by accessing the IDatabaseTester in the DbUnit#configure closure 23 | */ 24 | @ContextConfiguration(locations = 'classpath:/spring/context.xml') 25 | class ConfigureTesterSpecification extends Specification { 26 | 27 | @Autowired 28 | DataSource dataSource 29 | 30 | @DbUnit(configure = { IDatabaseTester it -> 31 | //tell db unit how to setup and teardown the database 32 | it.setUpOperation = DatabaseOperation.CLEAN_INSERT 33 | it.tearDownOperation = DatabaseOperation.TRUNCATE_TABLE 34 | 35 | //tell db unit to replace all occurrences of [TOMORROW] with the real value 36 | (it.dataSet as ReplacementDataSet).addReplacementObject('[TOMORROW]', LocalDateTime.now().plusDays(1).toDate()) 37 | //specify custom configurations if needed 38 | it.connection.config.setProperty(DatabaseConfig.PROPERTY_DATATYPE_FACTORY, new H2DataTypeFactory()) 39 | }) 40 | def content = { 41 | User(id: 1, name: 'janbols', created: '[NOW]', expiration: '[TOMORROW]') 42 | } 43 | 44 | 45 | def setup() { 46 | dataSource?.with { createUserTable(it) } 47 | } 48 | 49 | def cleanup() { 50 | dataSource?.with { dropUserTable(it) } 51 | } 52 | 53 | 54 | def "selecting from the User table returns the user"() { 55 | when: 56 | def result = new Sql(dataSource).firstRow("select * from User where name = 'janbols'") 57 | then: 58 | result.id == 1 59 | } 60 | 61 | def "the row in the User table returns has a created date of today and an expiration date of tomorrow"() { 62 | when: 63 | def result = new Sql(dataSource).firstRow("select * from User where name = 'janbols'") 64 | then: 65 | new LocalDate(result.created) == LocalDate.now() 66 | new LocalDate(result.expiration) == LocalDate.now().plusDays(1) 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/main/groovy/be/janbols/spock/extension/dbunit/support/DataSourceProvider.groovy: -------------------------------------------------------------------------------- 1 | package be.janbols.spock.extension.dbunit.support 2 | 3 | import be.janbols.spock.extension.dbunit.DbUnit 4 | import org.spockframework.runtime.extension.ExtensionException 5 | import org.spockframework.runtime.extension.IMethodInvocation 6 | import org.spockframework.util.ReflectionUtil 7 | 8 | import javax.sql.DataSource 9 | 10 | /** 11 | * Provides the datasource by calling the DbUnit.datasourceProvider closure or by looking for a shared or unshared DataSource field 12 | */ 13 | class DataSourceProvider { 14 | private static final Class transactionAwareClass = ReflectionUtil.loadClassIfAvailable("org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy") 15 | 16 | private final DbUnit dbUnitAnnotation 17 | private IMethodInvocation setupSpecInvocation 18 | private IMethodInvocation setupInvocation 19 | 20 | DataSourceProvider(DbUnit dbUnitAnnotation) { 21 | this.dbUnitAnnotation = dbUnitAnnotation 22 | } 23 | 24 | def withSetupSpecInvocation(IMethodInvocation invocation) { 25 | this.setupSpecInvocation = invocation 26 | } 27 | 28 | def withSetupInvocation(IMethodInvocation invocation) { 29 | this.setupInvocation = invocation 30 | } 31 | 32 | DataSource findDataSource() { 33 | DataSource result = doFindDataSource(dbUnitAnnotation.datasourceProvider(), setupSpecInvocation) 34 | if (!result) { 35 | result = doFindDataSource(dbUnitAnnotation.datasourceProvider(), setupInvocation) 36 | } 37 | 38 | if (result) { 39 | result = makeTransactionalAware(result) 40 | } 41 | 42 | return result 43 | } 44 | 45 | 46 | 47 | private static DataSource doFindDataSource(Class dataSourceProviderClosureClass, IMethodInvocation invocation) { 48 | DataSource result 49 | 50 | 51 | if (dataSourceProviderClosureClass && Closure.isAssignableFrom(dataSourceProviderClosureClass)) { 52 | result = findDataSourceByProvider(dataSourceProviderClosureClass, invocation.instance) 53 | } 54 | 55 | if (!result) { 56 | result = findDataSourceByField(invocation) 57 | } 58 | return result 59 | } 60 | 61 | private static DataSource findDataSourceByField(IMethodInvocation iMethodInvocation) { 62 | def datasourceFieldInfo = iMethodInvocation.spec.allFields.find { 63 | return DataSource.isAssignableFrom(it.reflection.type) 64 | } 65 | return datasourceFieldInfo?.readValue(iMethodInvocation.instance) 66 | } 67 | 68 | private static DataSource findDataSourceByProvider(Class dataSourceProviderClass, Object target) { 69 | try { 70 | def dataSourceClosure = dataSourceProviderClass.newInstance(target, target) 71 | return dataSourceClosure(); 72 | } catch (Exception e) { 73 | throw new ExtensionException("Failed to instantiate datasourceProvider in @DbUnit", e); 74 | } 75 | } 76 | 77 | /** 78 | * use transaction aware data source so changes are visible during the same @Transactional annotated feature 79 | */ 80 | private static DataSource makeTransactionalAware(DataSource dataSource) { 81 | if (transactionAwareClass) { 82 | if(transactionAwareClass.isInstance(dataSource)) { 83 | return dataSource 84 | } else { 85 | return transactionAwareClass.newInstance(dataSource) as DataSource 86 | } 87 | } else { 88 | return dataSource 89 | } 90 | } 91 | 92 | 93 | } 94 | -------------------------------------------------------------------------------- /src/main/groovy/be/janbols/spock/extension/dbunit/DbUnit.groovy: -------------------------------------------------------------------------------- 1 | package be.janbols.spock.extension.dbunit 2 | 3 | import org.spockframework.runtime.extension.ExtensionAnnotation 4 | 5 | import javax.sql.DataSource 6 | import java.lang.annotation.ElementType 7 | import java.lang.annotation.Retention 8 | import java.lang.annotation.RetentionPolicy 9 | import java.lang.annotation.Target 10 | 11 | /** 12 | * Allows specifying the xml data as a closure field. This avoids the need to link to a separate xml file containing the xml data. 13 | * For this DbUnit needs to get hold of a {@link javax.sql.DataSource} either by returning it in the {@link DbUnit#datasourceProvider} method or by adding it as a field in the specification. 14 | * f.e. 15 | *
16 |  class MyDbUnitTest extends Specification{
17 | 
18 |     DataSource dataSource
19 | 
20 |     {@literal @}DbUnit
21 |     def content =  {
22 |         User(id: 1, name: 'jackdaniels', createdOn: '[NOW]')
23 |     }
24 |     ...
25 |  }
26 |   
27 | * The values for the collumns in the sql data are replaced using the {@link org.dbunit.dataset.ReplacementDataSet}. By default this is done for the keyword [NULL] and [NOW] 28 | */ 29 | @Retention(RetentionPolicy.RUNTIME) 30 | @Target([ElementType.FIELD, ElementType.METHOD]) 31 | @ExtensionAnnotation(DbUnitExtension.class) 32 | public @interface DbUnit { 33 | /** 34 | * Allows a {@link javax.sql.DataSource} to be provided inside the given closure. A dataSource can also be provided as a field of the test class. In that case you don't need to specify one in here. 35 | * @return A closure returning a {@link javax.sql.DataSource} 36 | */ 37 | Class> datasourceProvider() default Object.class; 38 | 39 | /** 40 | * Allows you to configure the {@link org.dbunit.IDatabaseTester} passed as the closure's argument. 41 | * This can be used f.e. to specify other {@link org.dbunit.operation.DatabaseOperation} for setup and teardown than the defaults. 42 | * This also allows you to specify the schema used or add a an operation listener. 43 | * Finally it allows you to specify replacements for the sql values you specified as the database tester is initially set up with a {@link org.dbunit.dataset.ReplacementDataSet}. 44 | * @return A closure with a IDatabaseTester as input argument configured with the data specified on the accompanying field. 45 | * @see org.dbunit.IDatabaseTester 46 | * @see org.dbunit.operation.DatabaseOperation 47 | * @see org.dbunit.AbstractDatabaseTester#setUpOperation 48 | * @see org.dbunit.AbstractDatabaseTester#tearDownOperation 49 | * @see org.dbunit.dataset.ReplacementDataSet 50 | */ 51 | Class configure() default Object.class; 52 | 53 | /** 54 | * Name of the schema to use. 55 | * @return 56 | */ 57 | String schema() default ""; 58 | 59 | /** 60 | * Optional Closure containing the content of the database. This can only be used in a DbUnit annotation on a feature. 61 | * For DbUnit annotation on a field, the field itself is expected to contain the content. 62 | * f.e. 63 | *
64 |      class MyDbUnitTest extends Specification{
65 | 
66 |          {@literal @}DbUnit(content = {
67 |             User(id: 1, name: 'janbols')
68 |          })
69 |          def "feature with specific databasse content"() {
70 |             ...
71 |          }
72 | 
73 |      }
74 |      
75 | * @return 76 | */ 77 | Class content() default Object.class; 78 | 79 | /** 80 | * Enables "column sensing" feature of DBUnit, 81 | * where the list of columns for a table can vary - it is no longer deducted from the first row. 82 | */ 83 | boolean columnSensing() default false; 84 | } 85 | -------------------------------------------------------------------------------- /src/main/groovy/be/janbols/spock/extension/dbunit/support/DbUnitFeatureInterceptor.groovy: -------------------------------------------------------------------------------- 1 | package be.janbols.spock.extension.dbunit.support 2 | 3 | import be.janbols.spock.extension.dbunit.DbUnit 4 | import org.dbunit.DataSourceDatabaseTester 5 | import org.dbunit.IDatabaseTester 6 | import org.spockframework.runtime.extension.AbstractMethodInterceptor 7 | import org.spockframework.runtime.extension.ExtensionException 8 | import org.spockframework.runtime.extension.IMethodInvocation 9 | import org.spockframework.runtime.model.FeatureInfo 10 | import org.spockframework.runtime.model.SpecInfo 11 | 12 | /** 13 | * Interceptor for setup and cleanup methods for DbUnit 14 | */ 15 | class DbUnitFeatureInterceptor extends AbstractMethodInterceptor { 16 | 17 | private DataSourceDatabaseTester tester 18 | private final FeatureInfo featureInfo 19 | private final DbUnit dbUnitAnnotation 20 | 21 | private final DataSourceProvider dataSourceProvider 22 | private final DataSetProvider dataSetProvider 23 | 24 | 25 | DbUnitFeatureInterceptor(FeatureInfo featureInfo, DbUnit dbUnitAnnotation) { 26 | this.dbUnitAnnotation = dbUnitAnnotation 27 | this.featureInfo = featureInfo 28 | this.dataSetProvider = new DataSetProvider(dbUnitAnnotation, null) 29 | this.dataSourceProvider = new DataSourceProvider(dbUnitAnnotation) 30 | } 31 | 32 | @Override 33 | void interceptSetupSpecMethod(IMethodInvocation invocation) { 34 | invocation.proceed() 35 | dataSourceProvider.withSetupSpecInvocation(invocation) 36 | } 37 | 38 | @Override 39 | void interceptSetupMethod(IMethodInvocation invocation) { 40 | invocation.proceed() 41 | 42 | if (!featureInfo || featureInfo.featureMethod == invocation.feature.featureMethod) { 43 | dataSourceProvider.withSetupInvocation(invocation) 44 | //after setup to allow datasource setup 45 | def dataSource = dataSourceProvider.findDataSource() 46 | if (!dataSource) { 47 | throw new ExtensionException("Failed to find a javax.sql.DataSource. Specify one as a field or provide one using @DbUnit.datasourceProvider") 48 | } 49 | 50 | def dataSet = dataSetProvider.findDataSet(invocation.instance) 51 | if (!dataSet) { 52 | throw new ExtensionException("Failed to find a the data set. Specify one as a DbUnit-annotated field or provide one using @DbUnit.content") 53 | } 54 | 55 | tester = new DataSourceDatabaseTester(dataSource, dbUnitAnnotation.schema()) 56 | tester.dataSet = dataSet 57 | configureTester(tester, invocation) 58 | tester.onSetup() 59 | } 60 | } 61 | 62 | @Override 63 | void interceptFeatureMethod(IMethodInvocation invocation) throws Throwable { 64 | invocation.proceed() 65 | 66 | } 67 | 68 | @Override 69 | void interceptFeatureExecution(IMethodInvocation invocation) throws Throwable { 70 | super.interceptFeatureExecution(invocation) 71 | } 72 | 73 | private void configureTester(IDatabaseTester tester, IMethodInvocation invocation) { 74 | def configureClosureClass = dbUnitAnnotation.configure() 75 | if (configureClosureClass && Closure.isAssignableFrom(configureClosureClass)) { 76 | try { 77 | def configureClosure = configureClosureClass.newInstance(invocation.instance, tester) 78 | configureClosure(tester); 79 | } catch (Exception e) { 80 | throw new ExtensionException("Failed to instantiate tester configurer in @DbUnit", e); 81 | } 82 | } 83 | } 84 | 85 | 86 | @Override 87 | void interceptCleanupMethod(IMethodInvocation invocation) { 88 | tester?.onTearDown() 89 | invocation.proceed() 90 | } 91 | 92 | 93 | void install(SpecInfo spec) { 94 | spec.addSetupInterceptor this 95 | spec.addSetupSpecInterceptor this 96 | spec.addCleanupInterceptor this 97 | } 98 | 99 | 100 | } 101 | -------------------------------------------------------------------------------- /src/main/groovy/be/janbols/spock/extension/dbunit/support/DbUnitInterceptor.groovy: -------------------------------------------------------------------------------- 1 | package be.janbols.spock.extension.dbunit.support 2 | 3 | import be.janbols.spock.extension.dbunit.DbUnit 4 | import org.dbunit.DataSourceDatabaseTester 5 | import org.dbunit.IDatabaseTester 6 | import org.dbunit.database.IDatabaseConnection 7 | import org.spockframework.runtime.extension.AbstractMethodInterceptor 8 | import org.spockframework.runtime.extension.ExtensionException 9 | import org.spockframework.runtime.extension.IMethodInvocation 10 | import org.spockframework.runtime.model.FeatureInfo 11 | import org.spockframework.runtime.model.FieldInfo 12 | 13 | /** 14 | * Interceptor for setup, feature and cleanup methods for DbUnit 15 | */ 16 | class DbUnitInterceptor extends AbstractMethodInterceptor { 17 | 18 | private IDatabaseTester tester 19 | private final DbUnit dbUnitAnnotation 20 | 21 | private final DataSourceProvider dataSourceProvider 22 | private final DataSetProvider dataSetProvider 23 | 24 | DbUnitInterceptor(FieldInfo dataFieldInfo, DbUnit dbUnitAnnotation) { 25 | assert dataFieldInfo 26 | assert dbUnitAnnotation 27 | this.dbUnitAnnotation = dbUnitAnnotation 28 | this.dataSetProvider = new DataSetProvider(dbUnitAnnotation, dataFieldInfo) 29 | this.dataSourceProvider = new DataSourceProvider(dbUnitAnnotation) 30 | } 31 | 32 | DbUnitInterceptor(FeatureInfo featureInfo, DbUnit dbUnitAnnotation) { 33 | assert featureInfo 34 | assert dbUnitAnnotation 35 | this.dbUnitAnnotation = dbUnitAnnotation 36 | this.dataSetProvider = new DataSetProvider(dbUnitAnnotation, null) 37 | this.dataSourceProvider = new DataSourceProvider(dbUnitAnnotation) 38 | } 39 | 40 | @Override 41 | void interceptSetupSpecMethod(IMethodInvocation invocation) { 42 | invocation.proceed() 43 | dataSourceProvider.withSetupSpecInvocation(invocation) 44 | } 45 | 46 | @Override 47 | void interceptSetupMethod(IMethodInvocation invocation) { 48 | invocation.proceed() 49 | dataSourceProvider.withSetupInvocation(invocation) 50 | } 51 | 52 | volatile IDatabaseConnection currentConnection = null 53 | 54 | @Override 55 | void interceptFeatureMethod(IMethodInvocation invocation) throws Throwable { 56 | //after setup to allow datasource setup 57 | def dataSource = dataSourceProvider.findDataSource() 58 | if (!dataSource) { 59 | throw new ExtensionException("Failed to find a javax.sql.DataSource. Specify one as a field or provide one using @DbUnit.datasourceProvider") 60 | } 61 | 62 | def dataSet = dataSetProvider.findDataSet(invocation.instance) 63 | if (!dataSet) { 64 | throw new ExtensionException("Failed to find a the data set. Specify one as a DbUnit-annotated field or provide one using @DbUnit.content") 65 | } 66 | 67 | //override default behaviour of DataSourceDatabaseTester to always create new connections. 68 | 69 | //make sure to initialize schema to null when it's not specified. Otherwise a schema "" will be created instead of the default one 70 | //see https://github.com/janbols/spock-dbunit/issues/12 71 | tester = new DataSourceDatabaseTester(dataSource, dbUnitAnnotation.schema() ?: null ) { 72 | @Override 73 | IDatabaseConnection getConnection() throws Exception { 74 | if (!currentConnection || currentConnection.connection.isClosed()) { 75 | currentConnection = super.connection 76 | } 77 | return currentConnection 78 | } 79 | 80 | @Override 81 | void closeConnection(IDatabaseConnection connection) throws Exception { 82 | super.closeConnection(connection) 83 | currentConnection = null 84 | } 85 | } 86 | tester.dataSet = dataSet 87 | configureTester(tester, invocation) 88 | tester.onSetup() 89 | 90 | invocation.proceed() 91 | } 92 | 93 | private void configureTester(IDatabaseTester tester, IMethodInvocation invocation) { 94 | def configureClosureClass = dbUnitAnnotation.configure() 95 | if (configureClosureClass && Closure.isAssignableFrom(configureClosureClass)) { 96 | try { 97 | def configureClosure = configureClosureClass.newInstance(invocation.instance, tester) 98 | configureClosure(tester); 99 | } catch (Exception e) { 100 | throw new ExtensionException("Failed to instantiate tester configurer in @DbUnit", e); 101 | } 102 | } 103 | } 104 | 105 | @Override 106 | void interceptCleanupMethod(IMethodInvocation invocation) { 107 | tester?.onTearDown() 108 | invocation.proceed() 109 | } 110 | 111 | } 112 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # For Cygwin, ensure paths are in UNIX format before anything is touched. 46 | if $cygwin ; then 47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 48 | fi 49 | 50 | # Attempt to set APP_HOME 51 | # Resolve links: $0 may be a link 52 | PRG="$0" 53 | # Need this for relative symlinks. 54 | while [ -h "$PRG" ] ; do 55 | ls=`ls -ld "$PRG"` 56 | link=`expr "$ls" : '.*-> \(.*\)$'` 57 | if expr "$link" : '/.*' > /dev/null; then 58 | PRG="$link" 59 | else 60 | PRG=`dirname "$PRG"`"/$link" 61 | fi 62 | done 63 | SAVED="`pwd`" 64 | cd "`dirname \"$PRG\"`/" >&- 65 | APP_HOME="`pwd -P`" 66 | cd "$SAVED" >&- 67 | 68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 69 | 70 | # Determine the Java command to use to start the JVM. 71 | if [ -n "$JAVA_HOME" ] ; then 72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 73 | # IBM's JDK on AIX uses strange locations for the executables 74 | JAVACMD="$JAVA_HOME/jre/sh/java" 75 | else 76 | JAVACMD="$JAVA_HOME/bin/java" 77 | fi 78 | if [ ! -x "$JAVACMD" ] ; then 79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 80 | 81 | Please set the JAVA_HOME variable in your environment to match the 82 | location of your Java installation." 83 | fi 84 | else 85 | JAVACMD="java" 86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 87 | 88 | Please set the JAVA_HOME variable in your environment to match the 89 | location of your Java installation." 90 | fi 91 | 92 | # Increase the maximum file descriptors if we can. 93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 94 | MAX_FD_LIMIT=`ulimit -H -n` 95 | if [ $? -eq 0 ] ; then 96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 97 | MAX_FD="$MAX_FD_LIMIT" 98 | fi 99 | ulimit -n $MAX_FD 100 | if [ $? -ne 0 ] ; then 101 | warn "Could not set maximum file descriptor limit: $MAX_FD" 102 | fi 103 | else 104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 105 | fi 106 | fi 107 | 108 | # For Darwin, add options to specify how the application appears in the dock 109 | if $darwin; then 110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 111 | fi 112 | 113 | # For Cygwin, switch paths to Windows format before running java 114 | if $cygwin ; then 115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 158 | function splitJvmOpts() { 159 | JVM_OPTS=("$@") 160 | } 161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 163 | 164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 165 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/janbols/spock-dbunit.svg?branch=master)](https://travis-ci.org/janbols/spock-dbunit) 2 | [![Download](https://api.bintray.com/packages/janbols/maven/spock-dbunit/images/download.svg?version=0.4) ](https://bintray.com/janbols/maven/spock-dbunit/0.4/link) 3 | 4 | spock-dbunit 5 | ============ 6 | 7 | [Dbunit](http://dbunit.sourceforge.net/) extension for [spock](http://spockframework.org/) avoiding writing separate xml files 8 | when doing db related tests. 9 | 10 | Normally when using dbUnit, you specify your sql data in a separate xml file. This can become cumbersome 11 | and makes it more difficult to understand how your test is set up. 12 | You can avoid this using this spock extension. 13 | 14 | Using groovy's [MarkupBuilder](http://groovy-lang.org/processing-xml.html#_markupbuilder) you just specify your sql as a field 15 | like in the example below: 16 | 17 | ```groovy 18 | class MyDbUnitTest extends Specification{ 19 | 20 | DataSource dataSource 21 | 22 | @DbUnit 23 | def content = { 24 | User(id: 1, name: 'jackdaniels', createdOn: '[NOW]') 25 | } 26 | 27 | ... 28 | ``` 29 | 30 | The above code will setup dbUnit to insert the specified row in the User table. 31 | It will take the data source specified in the datasource field to get the connection to the database. 32 | 33 | Configuration 34 | ------------- 35 | #### Finding the DataSource 36 | dbUnit needs a [DataSource](https://docs.oracle.com/javase/7/docs/api/javax/sql/DataSource.html) to connect to the database. There are several ways to do this: 37 | * by specifying a datasourceProvider as an extra closure parameter in the `@DbUnit` annotation. The closure returns a configured DataSource 38 | ```groovy 39 | class MyDbUnitTest extends Specification{ 40 | 41 | @DbUnit(datasourceProvider = { 42 | inMemoryDataSource() 43 | }) 44 | def content = { 45 | User(id: 1, name: 'janbols') 46 | } 47 | 48 | ... 49 | ``` 50 | * by specifying a DataSource field in your specification 51 | ```groovy 52 | class MyDbUnitTest extends Specification{ 53 | 54 | DataSource dataSource 55 | 56 | @DbUnit 57 | def content = { 58 | User(id: 1, name: 'janbols') 59 | } 60 | 61 | def setup() { 62 | dataSource = inMemoryDataSource() 63 | } 64 | ... 65 | ``` 66 | * by specifying a `@Shared` DataSource field in your specification 67 | ```groovy 68 | class MyDbUnitTest extends Specification{ 69 | 70 | @Shared DataSource dataSource 71 | 72 | @DbUnit def content = { 73 | User(id: 1, name: 'janbols') 74 | } 75 | 76 | def setupSpec(){ 77 | dataSource = inMemoryDataSource() 78 | } 79 | ... 80 | ``` 81 | 82 | #### Configuring the DatabaseTester 83 | The dbUnit [DatabaseTester](http://dbunit.sourceforge.net/apidocs/org/dbunit/IDatabaseTester.html) 84 | can also be configured as an extra closure in the `@DbUnit` annotation. An example can be seen below: 85 | 86 | ```groovy 87 | class MyDbUnitTest extends Specification{ 88 | 89 | @DbUnit(configure={IDatabaseTester it -> 90 | it.setUpOperation = DatabaseOperation.CLEAN_INSERT 91 | it.tearDownOperation = DatabaseOperation.TRUNCATE_TABLE 92 | 93 | (it.dataSet as ReplacementDataSet) 94 | .addReplacementObject('[TOMORROW]', LocalDateTime.now().plusDays(1).toDate()) 95 | }) 96 | def content = { 97 | User(id: 1, name: 'jackdaniels', created: '[NOW]', expiration: '[TOMORROW]') 98 | } 99 | 100 | ... 101 | ``` 102 | 103 | In the example above, the DatabaseTester is being configured to do a clean insert during setup and a table truncate during cleanup. 104 | In addition all `[TOMORROW]` fields are being replaced with the date of tomorrow. 105 | 106 | #### Specifying the schema 107 | You can specify the default schema using the `schema` field in the `DbUnit` annotation. 108 | The example below shows an example: 109 | 110 | ```groovy 111 | class MyDbUnitTest extends Specification{ 112 | 113 | DataSource dataSource 114 | 115 | @DbUnit(schema = "otherSchema") 116 | def content = { 117 | User(id: 1, name: 'janbols') 118 | } 119 | 120 | ... 121 | ``` 122 | 123 | #### Use different content per feature 124 | You can specify different database content per feature. This can be done by adding a `DbUnit` annotation 125 | on the feature method. 126 | The content can be specified in the `content` field of the `DbUnit` annotation. 127 | It accepts a closure that specifies the database content. 128 | When there's also a `DbUnit` annotation on a field containing database content, 129 | the one on the feature takes precedence and the one on the field is ignored. 130 | An example is shown below: 131 | ```groovy 132 | class MyDbUnitTest extends Specification{ 133 | 134 | //default db content for all features that don't override this one 135 | @DbUnit 136 | def content = { 137 | User(id: 3, name: 'dinatersago') 138 | } 139 | 140 | ... 141 | 142 | //DbUnit on a feature overrides the one in the content field 143 | @DbUnit(content = { 144 | User(id: 1, name: 'janbols') 145 | }) 146 | def "feature with own database content"() { 147 | ... 148 | } 149 | 150 | def "feature without own database content taking the content of the field"() { 151 | ... 152 | } 153 | 154 | ... 155 | 156 | ``` 157 | 158 | Getting started 159 | --- 160 | To enable this Spock extension, you need to add a dependency to this project and a dependency to dbUnit 161 | 162 | using Maven: 163 | 164 | Enable the JCenter repository: 165 | 166 | 167 | jcenter 168 | JCenter Repo 169 | http://jcenter.bintray.com 170 | 171 | Add spock-reports to your : 172 | 173 | 174 | com.github.janbols 175 | spock-dbunit 176 | 0.4 177 | test 178 | 179 | 180 | org.dbunit 181 | dbunit 182 | 2.5.1 183 | test 184 | 185 | 186 | 187 | using Gradle: 188 | 189 | repositories { 190 | jcenter() 191 | } 192 | 193 | dependencies { 194 | testCompile 'com.github.janbols:spock-dbunit:0.4' 195 | testCompile 'org.dbunit:dbunit:2.5.1' 196 | } 197 | 198 | If you prefer, you can just download the jar directly 199 | from [JCenter](http://jcenter.bintray.com/com/github/janbols/spock-dbunit/0.4/:spock-dbunit-0.4.jar). 200 | 201 | Changes 202 | --- 203 | #### Version 0.4 204 | * Solve a bug that allows you to specify connection configuration properties (https://github.com/janbols/spock-dbunit/issues/9) 205 | * Solve a bug when no `DbUnit` annotation exists on a field but only on features (https://github.com/janbols/spock-dbunit/issues/10) 206 | 207 | #### Version 0.3 208 | * Be able to override the `DbUnit` content per feature (https://github.com/janbols/spock-dbunit/issues/7) 209 | * Be able to specify the schema (https://github.com/janbols/spock-dbunit/issues/6) 210 | 211 | #### Version 0.2 212 | * Easier way to see if spring-jdbc is on the classpath 213 | * Publish in bintray 214 | 215 | #### Version 0.1 216 | * Initial version 217 | 218 | --------------------------------------------------------------------------------