Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/database-rider/database-rider
Database testing made easy!
https://github.com/database-rider/database-rider
cdi db-testing dbrider dbunit deltaspike jpa junit5 micronautfw quarkus spring springboot
Last synced: 27 days ago
JSON representation
Database testing made easy!
- Host: GitHub
- URL: https://github.com/database-rider/database-rider
- Owner: database-rider
- License: apache-2.0
- Created: 2016-09-16T12:47:36.000Z (about 8 years ago)
- Default Branch: master
- Last Pushed: 2024-07-25T19:05:32.000Z (4 months ago)
- Last Synced: 2024-10-01T05:22:32.700Z (about 1 month ago)
- Topics: cdi, db-testing, dbrider, dbunit, deltaspike, jpa, junit5, micronautfw, quarkus, spring, springboot
- Language: Java
- Homepage: https://database-rider.github.io/database-rider
- Size: 17.4 MB
- Stars: 645
- Watchers: 22
- Forks: 132
- Open Issues: 69
-
Metadata Files:
- Readme: README.adoc
- Changelog: CHANGELOG.adoc
- License: LICENSE
Awesome Lists containing this project
README
= Database Rider
:page-layout: base
:source-language: java
:icons: font
:linkattrs:
:sectanchors:
:sectlink:
:numbered:
:doctype: book
:toc: preamble
:tip-caption: :bulb:
:note-caption: :information_source:
:important-caption: :heavy_exclamation_mark:
:caution-caption: :fire:
:warning-caption: :warning:[quote]
____
So you can ride the database in your JUnit tests!
____image:https://github.com/database-rider/database-rider/actions/workflows/ci.yml/badge.svg[Build Status, link=https://github.com/database-rider/database-rider/actions/workflows/ci.yml]
image:https://coveralls.io/repos/database-rider/database-rider/badge.png[Coverage, link=https://coveralls.io/r/database-rider/database-rider]
image:https://img.shields.io/maven-central/v/com.github.database-rider/rider-core.svg?label=Maven%20Central["Maven Central",link="https://search.maven.org/search?q=g:com.github.database-rider"]
image:https://sonarcloud.io/api/project_badges/measure?project=com.github.database-rider:rider-parent&metric=alert_status["Sonar", link="https://sonarcloud.io/dashboard?id=com.github.database-rider%3Arider-parent"]
image:https://badges.gitter.im/database-rider/community.svg["Gitter", link="https://gitter.im/database-rider/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge"]This project aims for bringing http://dbunit.sourceforge.net/[DBUnit] closer to your JUnit tests *so database testing will feel like a breeze!*
Watch https://www.youtube.com/watch?v=A5ryED3a8FY[1.0 promo video^] to get an idea.
A lot of this work is based on https://github.com/arquillian/arquillian-extension-persistence/[Arquillian persistence extension] and focus on simplicity (one dependency - dbunit).
== Introduction
Consider the following (jpa) entities:
[source, java]
----
@Entity
public class User {@Id
@GeneratedValue
private long id;private String name;
@OneToMany(mappedBy = "user")
private List tweets;@OneToMany(mappedBy = "followedUser")
private List followers;//getters/setters
}
@Entity
public class Tweet {@Id
@GeneratedValue
private String id;@Size(min = 1, max = 140)
private String content;private Integer likes;
@Temporal(TemporalType.TIMESTAMP)
private Date date;@ManyToOne
private User user;
}@Entity
public class Follower {@Id
@GeneratedValue
private long id;@JoinColumn(name = "follower_id")
private User followerUser;@ManyToOne
@JoinColumn(name = "user_id")
private User followedUser;}
----
and the following dbunit yaml dataset:
.src/test/resources/datasets/users.yml
----
user:
- id: 1
name: "@realpestano"
- id: 2
name: "@dbunit"
tweet:
- id: abcdef12345
content: "dbunit rules!"
user_id: 1
follower:
- id: 1
user_id: 1
follower_id: 2
----You should be able to prepare your database before test execution, like below:
[source,java]
----
@RunWith(JUnit4.class)
public class UserIt {@Rule
public EntityManagerProvider emProvider = EntityManagerProvider.instance("rules-it");@Rule
public DBUnitRule dbUnitRule = DBUnitRule.instance(emProvider.connection());@Test
@DataSet(value = "datasets/yml/users.yml")
public void shouldLoadUserFollowers() {
User user = (User) emProvider.em().createQuery("select u from User u left join fetch u.followers where u.id = 1").getSingleResult();
assertThat(user).isNotNull();
assertThat(user.getId()).isEqualTo(1);
assertThat(user.getTweets()).hasSize(1);
assertEquals(user.getTweets().get(0).getContent(), "dbunit rules!");
assertThat(user.getFollowers()).isNotNull().hasSize(1);
Follower expectedFollower = new Follower(2,1);
assertThat(user.getFollowers()).contains(expectedFollower);
}
----NOTE: <> is a simple JUnit rule that creates a JPA entityManager (and caches it) for each test. DBunit rule don't depend on EntityManagerProvider, it only needs a *JDBC connection*;
== Documentation
A getting started guide can be found here http://database-rider.github.io/getting-started/.
For main features overview see http://database-rider.github.io/database-rider/latest/documentation.html[project living documentation^].
Older documentation versions can be found here: https://database-rider.github.io/database-rider/#documentation.
== Rider Core
This module is the basis for subsequent modules. It contains a JUnit rule (shown above), the api for dataset, DBunit configuration and *DataSetExecutor* which is responsible for dataset creation.
=== Adding Database Rider core to your project
[source, xml]
----com.github.database-rider
rider-core
1.44.0
test----
TIP: Use `jakarta` maven classifier if you use jakarta-cdi or jakarta-persistence
[NOTE]
====
It will bring the following (transitive) dependencies to your test classpath:[source,xml]
----org.dbunit
dbunitorg.yaml
snakeyamlorg.codehaus.jackson
jackson-mapper-lgpl----
======= DataSet executor
A DataSet executor is a component which creates DBUnit datasets. Datasets are "sets" of data (tables and rows) that represent the *state of the database*. DataSets are defined as textual files in *YAML*, *XML*, *JSON*, *CSV*, *XLS* or *XLSX* format, https://github.com/database-rider/database-rider/blob/master/rider-core/src/test/resources/datasets/[see examples here^].As in DBUnit Rule, `dataset executor` just needs a JDBC connection to be instantiated:
[source,java]
----
import static com.github.database.rider.util.EntityManagerProvider.em;
import static com.github.database.rider.util.EntityManagerProvider.instance;@RunWith(JUnit4.class)
public class DataSetExecutorIt {public EntityManagerProvider emProvider = instance("executor-it");
private static DataSetExecutorImpl executor;
@BeforeClass
public static void setup() {
executor = DataSetExecutorImpl.instance(new ConnectionHolderImpl(emProvider.getConnection()));
}@Test
public void shouldSeedUserDataSetUsingExecutor() {
DataSetConfig dataSetConfig = new DataSetConfig("datasets/yml/users.yml");<1>
executor.createDataSet(dataSetConfig);<2>
User user = (User) em().createQuery("select u from User u where u.id = 1").getSingleResult();
assertThat(user).isNotNull();
assertThat(user.getId()).isEqualTo(1);
}
}
----
<1> As we are not using @Rule, which is responsible for reading @DataSet annotation, we have to provide *DataSetConfig* so executor can create the dataset.
<2> this is done implicitly by *@Rule DBUnitRule*.DataSet executor setup and logic is `hidden` by DBUnit @Rule and @DataSet annotation.
[IMPORTANT]
====
Since `v1.13.0` you can use <> which provides a fluent api as an alternative to DataSetExecutor (and `@DataSet`):[source,java]
----
import static com.github.database.rider.util.EntityManagerProvider.em;
import static com.github.database.rider.util.EntityManagerProvider.instance;@RunWith(JUnit4.class)
public class DataSetExecutorIt {public EntityManagerProvider emProvider = instance("executor-it");
@Test
public void shouldSeedUserDataSetUsingRiderDSL() {
RiderDSL.withConnection(emProvider.getConnection())
.withDataSetConfig(new DataSetConfig("datasets/yml/users.yml")
.cleanBefore(true))
.withDBUnitConfig(new DBUnitConfig()
.addDBUnitProperty("caseSensitiveTableNames", false))
.createDataSet();
User user = (User) em().createQuery("select u from User u where u.id = 1").getSingleResult();
assertThat(user).isNotNull();
assertThat(user.getId()).isEqualTo(1);
}
}
----====
=== Configuration
There are two types of configuration in Database Rider: `DataSet` and `DBUnit`.
==== DataSet Configuration
This basically setup the `dataset` which will be used. The only way to configure a dataset is using *@DataSet* annotation.
It can be used at *class* or *method* level:
[source,java]
----
@Test
@DataSet(value ="users.yml", strategy = SeedStrategy.UPDATE,
disableConstraints = true,cleanAfter = true,transactional = true)
public void shouldLoadDataSetConfigFromAnnotation(){}
----Here are possible values:
[cols="3*", options="header"]
|===
|Name | Description | Default
|value
a|Dataset file name using* test resources folder as root directory.
** eg. 'yml/users.yml'
* URL-Notation for registered stream-handlers
** eg. 'file:///C:/dir/users.xml', 'http://...')
** except: loading csv datasets is only supported by the file-protocolMultiple, comma separated, dataset file names can be provided.| ""
|executorId| Name of dataset executor for the given dataset.| DataSetExecutorImpl.DEFAULT_EXECUTOR_ID
|strategy| DataSet seed strategy. Possible values are: CLEAN_INSERT, INSERT, REFRESH and UPDATE.| CLEAN_INSERT, meaning that DBUnit will clean and then insert data in tables present on provided dataset.
|useSequenceFiltering| If true dbunit will look at constraints and dataset to try to determine the correct ordering for the SQL statements.| true
|tableOrdering| A list of table names used to reorder DELETE operations to prevent failures due to circular dependencies.| ""
|disableConstraints| Disable database constraints.| false
|cleanBefore| If true Database Rider will try to delete database before test in a smart way by using table ordering and brute force.| false
|cleanAfter| If true Database Rider will try to delete database after test in a smart way by using table ordering and brute force.| false
|transactional| If true a transaction will be started before test and committed after test execution. | false
|executeStatementsBefore| A list of jdbc statements to execute before test.| {}
|executeStatementsAfter| A list of jdbc statements to execute after test.| {}
|executeScriptsBefore| A list of sql script files to execute before test. Note that commands inside sql file must be separated by `;`.| {}
|executeScriptsAfter| A list of sql script files to execute after test. Note that commands inside sql file must be separated by `;`.| {}
|skipCleaningFor| Allows user to provide tables which will NOT be cleaned in `cleanBefore` and `cleanAfter`.| {}
|replacers| Implementations of `com.github.database.rider.core.replacers.Replacer` to be used as dataset replacement during seeding database.| {}
|======= DBUnit Configuration
This basically setup `DBUnit` itself. It can be configured by *@DBUnit* annotation (class or method level) and *dbunit.yml* file present in test resources folder.
[source,java]
----
@Test
@DBUnit(cacheConnection = true, cacheTableNames = false, allowEmptyFields = true,batchSize = 50)
public void shouldLoadDBUnitConfigViaAnnotation() {}
----Here is a dbunit.yml example, also the default values:
.src/test/resources/dbunit.yml
----
cacheConnection: true
cacheTableNames: true
leakHunter: false
mergeDataSets: false
mergingStrategy: METHOD <1>
caseInsensitiveStrategy: UPPERCASE <2>
raiseExceptionOnCleanUp: false <3>
expectedDbType: UNKNOWN <4>
disableSequenceFiltering: false <5>
disablePKCheckFor: [""] <6>
alwaysCleanBefore: false <7>
alwaysCleanAfter: false <8>
properties:
batchedStatements: false
qualifiedTableNames: false
schema: null
caseSensitiveTableNames: false
batchSize: 100
fetchSize: 100
allowEmptyFields: false
escapePattern: <9>
schema:
datatypeFactory: !!com.github.database.rider.core.configuration.DBUnitConfigTest$MockDataTypeFactory {} <10>
tableType: ["TABLE"]
connectionConfig:
driver: ""
url: ""
user: ""
password: ""
----
<1> Strategy for merging datasets, if `METHOD` then the method level dataset will be loaded first but if `CLASS` then the class level dataset will be loaded first.
<2> Only applied when `caseSensitiveTableNames` is `false`. Valid values are `UPPERCASE` and `LOWERCASE`.
<3> If enabled an exception will be raised when `cleanBefore` or `cleanAfter` fails. If disabled then only a warn message is logged. Default is false.
<4> In the process of initialization, if the actual database type is different from the expected database type, exception will be thrown unless the expected database type is UNKNOWN. Default is UNKNOWN.
<5> Disables sequenceFiltering at dbunit level, overriding the default behavior of `@DataSet(useSequenceFiltering)` which is true.
<6> List of table names to disable primary key checking while seeding the database.
<7> Always enables cleanBefore at dbunit level, overriding the default behavior of `@DataSet(cleanBefore)` which is
false.
<8> Always enables cleanAfter at dbunit level, overriding the default behavior of `@DataSet(cleanAfter)` which is false.
<9> Make it possible to define a datatype factory, https://github.com/database-rider/database-rider/issues/30[see
issue #30^] for details.
<10> Allows to configure DBUnit escapePattern property. Note that since v`1.28.0` DBRider will discover the
escapePattern for each DB without the need to use the escapePattern property in @DBUnit or via dbunit.yml config file. If an espapePattern is provided then it will be used.IMPORTANT: `@DBUnit` annotation takes precedence over `dbunit.yml` global configuration which will be used only if the annotation is not present.
TIP: `System properties` and `Environment variables` can be used to resolve properties of type *String*. The value of the property must follow the format: `${system property or env var name}`. Check both annotation and global file https://github.com/database-rider/database-rider/blob/be436283070f95f3487f9f41e7b773da23222eb4/rider-core/src/test/java/com/github/database/rider/core/configuration/DBUnitConfigTest.java#L175-L225[example here^].
=== JDBC Connection
As seen in examples above `DBUnit` needs a JDBC connection to be instantiated. To avoid creating connection for each test you can define it in *dbunit.yml* for all tests or define in *@DBUnit* on each test.
NOTE: `@DBUnit` annotation takes precedence over dbunit.yml global configuration.
==== Example
[source, java, linenums]
----
@RunWith(JUnit4.class)
@DBUnit(url = "jdbc:hsqldb:mem:test;DB_CLOSE_DELAY=-1", driver = "org.hsqldb.jdbcDriver", user = "sa") <1>
public class ConnectionConfigIt {@Rule
public DBUnitRule dbUnitRule = DBUnitRule.instance(); <2>@BeforeClass
public static void initDB(){
//trigger db creation
EntityManagerProvider.instance("rules-it");
}@Test
@DataSet(value = "datasets/yml/user.yml")
public void shouldSeedFromDeclaredConnection() {
User user = (User) em().createQuery("select u from User u where u.id = 1").getSingleResult();
assertThat(user).isNotNull();
assertThat(user.getId()).isEqualTo(1);
}
}
----
<1> driver class can be ommited in new JDBC drivers since version 4.
<2> Note that the rule instantiation doesn't need a connection anymore.IMPORTANT: As CDI module depends on a produced entity manager, connection configuration will be ignored.
=== Rule chaining
DBUnit Rule can be https://github.com/junit-team/junit4/wiki/rules#rulechain[chained with other rules^] so you can define execution order among rules.
In example below <> executes *before* `DBUnit rule`:
[source,java,linenums]
----
EntityManagerProvider emProvider = EntityManagerProvider.instance("rules-it");@Rule
public TestRule theRule = RuleChain.outerRule(emProvider).
around(DBUnitRule.instance(emProvider.connection()));
----=== Multiple Databases
Each executor has a JDBC connection so multiple databases can be handled by using multiple dataset executors:[source, java]
----
import static com.github.database.rider.util.EntityManagerProvider.instance;@RunWith(JUnit4.class)
public class MultipleExecutorsIt {private static List executors = new ArrayList<>;
@BeforeClass
public static void setup() { <1>
executors.add(DataSetExecutorImpl.instance("executor1", new ConnectionHolderImpl(instance("executor1-pu").getConnection())));
executors.add(DataSetExecutorImpl.instance("executor2", new ConnectionHolderImpl(instance("executor2-pu").getConnection())));
}@Test
public void shouldSeedUserDataSet() {
for (DataSetExecutorImpl executor : executors) {
DataSetConfig dataSetConfig = new DataSetConfig("datasets/yml/users.yml");
executor.createDataSet(dataSetConfig);
User user = (User) EntityManagerProvider.instance(executor.getId() + "-pu").em().createQuery("select u from User u where u.id = 1").getSingleResult();
assertThat(user).isNotNull();
assertThat(user.getId()).isEqualTo(1);
}
}}
----
<1> As you can see each executor is responsible for a database, in case a JPA persistence unitAlso note that the same can be done using @Rule but pay attention that you must provide executor id in *@DataSet annotation*.
[source, java]
----
@Rule
public EntityManagerProvider emProvider1 = EntityManagerProvider.instance("dataset1-pu");@Rule
public EntityManagerProvider emProvider2 = EntityManagerProvider.instance("dataset2-pu");@Rule
public DBUnitRule exec1Rule = DBUnitRule.instance("exec1",emProvider1.getConnection());<1>@Rule
public DBUnitRule exec2Rule = DBUnitRule.instance("exec2",emProvider2.getConnection());@Test
@DataSet(value = "datasets/yml/users.yml",disableConstraints = true, executorId = "exec1") <2>
public void shouldSeedDataSetDisablingContraints() {
User user = (User) emProvider1.em().createQuery("select u from User u where u.id = 1").getSingleResult();
assertThat(user).isNotNull();
assertThat(user.getId()).isEqualTo(1);
}@Test
@DataSet(value = "datasets/yml/users.yml",disableConstraints = true, executorId = "exec2")
public void shouldSeedDataSetDisablingContraints2() {
User user = (User) emProvider2.em().createQuery("select u from User u where u.id = 1").getSingleResult();
assertThat(user).isNotNull();
assertThat(user.getId()).isEqualTo(1);
}
----<1> *exec1* is the id of executor responsible for dataset1-pu
<2> executorId must match id provided in @Rule annotation==== Multiple databases in Spring tests
Since `v1.9.0` both `Rider Spring` and `Rider JUnit 5` with `SpringBoot` support multiple datasources. You just need to specify the *dataSourceBeanName* property in `@DBRider` annotation:
[source, java]
----
@DBRider //default datasource is used
@SpringBootTest
public class MultipleDataSourcesTest {@Autowired
private UserRepository userRepository; //from user datasource@Autowired
private CompanyRepository companyRepository; //from company datasource@Test
@DataSet("users.yml")
public void shouldListUsers() {
assertThat(userRepository.count()).isEqualTo(3);
assertThat(userRepository.findByEmail("[email protected]")).isEqualTo(new User(3));
}@Test
@DBRider(dataSourceBeanName = "companyDataSource") //secondary (company) datasource will be used
@DataSet("companies.yml")
public void shouldListCompanies() {
assertThat(companyRepository.count()).isEqualTo(2);
assertThat(companyRepository.findByNameLike("Umbrella%")).isEqualTo(new Company(2));}
----TIP: Full `rider-junit5` example can be found https://github.com/database-rider/database-rider/blob/master/rider-examples/spring-boot-dbunit-sample/src/test/java/com/github/database/rider/springboot/MultipleDataSourcesTest.java#L17[here^] and https://github.com/database-rider/database-rider/blob/master/rider-junit5/src/test/java/com/github/database/rider/junit5/DBRiderSpringDataSourceIt.java#L28[here^].
TIP: Full `rider-spring` example can be found https://github.com/database-rider/database-rider/blob/master/rider-spring/src/test/java/com/github/database/rider/spring/dataset/MultipleDataSourcesIt.java#L31[here^].
==== Multiple databases in CDI tests
For `rider-cdi` you must use `entityManagerName` property of `@DBRider` from CDI module:
[source, java]
----
@RunWith(CdiTestRunner.class)
@DBRider
public class MultipleEntityManagerIt {@Inject
EntityManager em;@Inject
@RiderPU("cdipu2")
EntityManager em2;@Test
@DataSet("yml/users.yml")
public void shouldListUsersFromDefaultEntityManager() {
List users = em.createQuery("select u from User u").getResultList();
assertThat(users).isNotNull().isNotEmpty().hasSize(2);
}@Test
@DBRider(entityManagerName = "cdipu2")
@DataSet("yml/users.yml")
public void shouldListUsersFromEntityManager2() {
List users = em2.createQuery("select u from User u").getResultList();
assertThat(users).isNotNull().isNotEmpty().hasSize(2);
}
----TIP: Full CDI example can be found https://github.com/database-rider/database-rider/blob/master/rider-cdi/src/test/java/com/github/database/rider/cdi/MultipleEntityManagerIt.java#L27[here^].
=== Expected DataSet
Using `@ExpectedDataSet` annotation you can specify the database state you expect after test execution, example:.expectedUsers.yml
----
user:
- id: 1
name: "expected user1"
- id: 2
name: "expected user2"
----[source, java]
----
@Test
@ExpectedDataSet(value = "yml/expectedUsers.yml",ignoreCols = "id")
public void shouldMatchExpectedDataSet() {
User u = new User();
u.setName("expected user1");
User u2 = new User();
u2.setName("expected user2");
emProvider.tx().begin();
emProvider.em().persist(u);
emProvider.em().persist(u2);
emProvider.tx().commit();
}
----NOTE: As you probably noticed, there is no need for assertions in the test itself.
Now with an assertion error:
[source, java]
----
@Test
@ExpectedDataSet(value = "yml/expectedUsers.yml",ignoreCols = "id")
public void shouldMatchExpectedDataSet() {
User u = new User();
u.setName("non expected user1");
User u2 = new User();
u2.setName("non expected user2");
emProvider.tx().begin();
emProvider.em().persist(u);
emProvider.em().persist(u2);
emProvider.tx().commit();
}
----
And here is how the error is shown in JUnit console:----
Expected :expected user1
Actual :non expected user1
at org.dbunit.assertion.JUnitFailureFactory.createFailure(JUnitFailureFactory.java:39)
at org.dbunit.assertion.DefaultFailureHandler.createFailure(DefaultFailureHandler.java:97)
at org.dbunit.assertion.DefaultFailureHandler.handle(DefaultFailureHandler.java:223)
at com.github.database.rider.assertion.DataSetAssert.compareData(DataSetAssert.java:94)
----NOTE: Since `v1.4.0` you can use <> in expected dataset.
==== Expected DataSet with regular expressions
You can also use `regular expressions` in expected DataSet, for that just prepend column value with `regex:`:
----
user:
- id: "regex:\\d+" #any number
name: regex:^expected user.* #'starts with' regex
- id: "regex:\\d+"
name: regex:.*user2$ #'ends with' regex
----Now we don't need to `ignore id column` in the above example:
[source,java]
----
@Test
@ExpectedDataSet(value = "yml/expectedUsers.yml")
public void shouldMatchExpectedDataSetUsingRegex() {
User u = new User();
u.setName("expected user1");
User u2 = new User();
u2.setName("expected user2");
emProvider.tx().begin();
emProvider.em().persist(u);
emProvider.em().persist(u2);
emProvider.tx().commit();
}
----==== Expected Dataset with order by column
To ignore row ordering in expected dataset one can use **orderBy** attribute in expected dataset.
This way both actual and expected datasets will be ordered by same column:
.expectedUsersIgnoreOrder.yml
----
USER:
- NAME: "@realpestano"
- NAME: "@arhohuttunen"
- NAME: "@dbunit"----
[source, java]
----
@Test
@DataSet(value = "yml/empty.yml", disableConstraints = true)
@ExpectedDataSet(value = "yml/expectedUsersIgnoreOrder.yml", orderBy = "name")
public void shouldMatchExpectedDataSetIgnoringRowOrder() {
User u1 = new User();
u1.setName("@arhohuttunen");
User u2 = new User();
u2.setName("@realpestano");
User u3 = new User();
u3.setName("@dbunit");
tx().begin();
em().persist(u1);
em().persist(u2);
em().persist(u3);
tx().commit();
}
----Note on the example above that the order of insertion is different from declared on the dataset but the test passes because of `orderBy` will make both expected dataset and database table ordered by the same column.
==== Using `CONTAINS` in expected datatset
By default the dataset you use in `@ExpectedDataSet` must be *EQUAL* to the state of the database after test execution which means same rows.
Since `v1.5.2` expected dataset can be configured to use a *CONTAINS* operation and in this case its rows must be present in actual database after test.
Consider following datasets:
.users.yml
----
USER:
- ID: 1
NAME: "@realpestano"
- ID: 2
NAME: "@dbunit"
----.expectedUsersContains.yml
----
USER:
- ID: 3
NAME: "@dbrider"
----And the integration test below:
[source,java]
----
@Test
@DataSet(value = "user.yml", transactional = true)
@ExpectedDataSet(value = "expectedUsersContains.yml", compareOperation = CompareOperation.CONTAINS)
public void shouldMatchExpectedDataSetContains() {
User u = new User();
u.setId(3);
u.setName("@dbrider");
em().persist(u);
}
----NOTE: This test will pass if database state after test contains a row in `user table` with `id=3` and `name = @dbrider`. Other rows doesn't matter.
==== Using `script assertions` in expected datatset
Since `v1.31.0` it's possible to use javascript or groovy assertions (based on <>) in `@ExpectedDataSet`, for example consider the following dataset:
```
USER:
- ID: "js:(value > 0)"
NAME: "js:(value != null && value.contains('@realpestano'))"
- ID: "groovy:(value > 0)"
NAME: "groovy:(value != null && value.contains('@dbunit'))"
```To enable the scripting you mainly need the `js: or groovy:` prefix *followed by an expression which evaluates to boolean* inside a `parentheses`. The `value` in the expression refers to the actual value that should be in database after the test runs.
```
@Test
@DataSet(value = "yml/empty.yml", transactional = true)
@ExpectedDataSet(value = "yml/expectedUserWithScripting.yml")
public void shouldEvaluateScriptsInExpectedDataSet() {
User u = new User(1);
u.setName("@realpestano");
User u2 = new User(2);
u2.setName("@dbunit");
em().persist(u);
em().persist(u2);
}
```NOTE: To enable groovy scripts you need `groovy-all` in your test classpath
==== Using `Prolog` assertions in expected datatset
Prolog assertions enables linking column values with variables, ex:
expectedUserWithProlog.yaml
```
USER:
- ID: $$x$$
NAME: "@realpestano"
TWEET:
- ID: abcdef12345
CONTENT: "dbunit rules!"
USER_ID: $$x$$
```In the dataset above we're linking `ID` and `USER_ID` values using the `$$x$$` variable.
To enable this kind of assertion you need to use `compareOperation = CompareOperation.PROLOG` in `@ExpectedDataset`:----
@Test
@DataSet(value = "yml/users.yml", transactional = true)
@ExpectedDataSet(value = "yml/expectedUserWithProlog.yml", compareOperation = CompareOperation.PROLOG)
public void shouldSupportPrologCompareOperation() {
}
----[TIP]
====
Multiple variables can be used:----
table1:
- id: $$x$$
name: "name2"
- id: $$y$$
name: "name1"
table2:
- table1_id: $$x$$
- table1_id: $$y$$
----
====In case the variable values don't match an assertion error will be raised, for example:
```
org.dbunit.dataset.DataSetException: Could not find a solution to theory: IndexedTheory{ user('1', '@realpestano') :- true. user('2', '@dbunit') :- true. tweet(abcdef12345, 'dbunit rules!', '2023-02-25 12:57:13.0', '[null]', '[null]', '2') :- true } given query: (user(X_2, '@realpestano'), tweet(abcdef12345, 'dbunit rules!', __2, __3, __4, X_2))
```=== Meta DataSets
With meta datasets you can create annotations which holds `@DataSet` configuration and (re)use this custom annotation in any test:
.Custom annotation holding dataset configuration
----
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
@DataSet(value = "yml/users.yml", disableConstraints = true)
public @interface MetaDataSet {}
----.Test using metadataset, @DataSet config is extracted from custom annotation
----
@RunWith(JUnit4.class)
@MetaDataSet
public class MetaDataSetIt {@Rule
public EntityManagerProvider emProvider = EntityManagerProvider.instance("rules-it");@Rule
public DBUnitRule dbUnitRule = DBUnitRule.instance(emProvider.connection());@Test
public void testMetaAnnotationOnClass() {
List users = em().createQuery("select u from User u").getResultList();
assertThat(users).isNotNull().isNotEmpty().hasSize(2);
}}
----
You can use another metadataset at method level which will take precedence:
----
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
@DataSet(value = "yml/expectedUser.yml", disableConstraints = true)
public @interface AnotherMetaDataSet {}
----
----
@RunWith(JUnit4.class)
@MetaDataSet
public class MetaDataSetIt {//rules omitted for brevity
@Test
@AnotherMetaDataSet
public void testMetaAnnotationOnMethod() {
List users = em().createQuery("select u from User u").getResultList();
assertThat(users).isNotNull().isNotEmpty().hasSize(1);
}
----NOTE: This works with all Database Rider modules like `Spring`, `CDI` and `JUnit5`.
TIP: See https://github.com/database-rider/database-rider/blob/master/rider-core/src/test/java/com/github/database/rider/core/MetaDataSetIt.java[MetaDatasetIt code^] for details.
=== Merge DataSets
Since `v1.3.0` it is possible to merge @DataSet configuration by declaring the annotation at `class` and `method` level.
To enable the merging use `mergeDataSets=true` in `@DBunit` annotation or in `dbunit.yml` configuration.
IMPORTANT: Only *array properties* such as `value` and `executeScriptsAfter` from @DataSet will be merged.
Following is an example of merging:
----
@RunWith(JUnit4.class)
@DBUnit(mergeDataSets = true) <1>
@DataSet(value="yml/tweet.yml", executeScriptsAfter = "addUser.sql", executeStatementsBefore = "INSERT INTO USER VALUES (8,'user8')")
public class MergeDataSetsIt {@Rule
public EntityManagerProvider emProvider = EntityManagerProvider.instance("rules-it");@Rule
public DBUnitRule dbUnitRule = DBUnitRule.instance(emProvider.connection());
@Test
@DataSet(value="yml/user.yml", executeScriptsAfter = "tweets.sql", executeStatementsBefore = "INSERT INTO USER VALUES (9,'user9')", strategy = SeedStrategy.INSERT)
public void shouldMergeDataSetsFromClassAndMethod() {
List users = em().createQuery("select u from User u").getResultList(); //2 users from user.yml plus 1 from class level 'executeStatementsBefore' and 1 user from method level 'executeStatementsBefore'
assertThat(users).isNotNull().isNotEmpty().hasSize(4);
User user = (User) em().createQuery("select u from User u where u.id = 9").getSingleResult(); <2>
assertThat(user).isNotNull();
assertThat(user.getId()).isEqualTo(9);
user = (User) em().createQuery("select u from User u where u.id = 1").getSingleResult();
assertThat(user.getTweets()).isNotEmpty(); <3>
assertThat(user.getTweets().get(0).getContent()).isEqualTo("dbunit rules again!");
}
@AfterClass
public static void afterTest() {
User user = (User) em().createQuery("select u from User u where u.id = 10").getSingleResult();<4>
assertThat(user).isNotNull();
assertThat(user.getId()).isEqualTo(10);
Tweet tweet = (Tweet) em().createQuery("select t from Tweet t where t.id = 10").getSingleResult();//scripts after
assertThat(tweet).isNotNull();
assertThat(tweet.getId()).isEqualTo("10");
}
}
----
<1> Activates the merging of datasets
<2> User with id = 8 was inserted by `executeStatementsBefore` from class level dataset.
<3> tweets comes from `tweet.yml` declared on class level dataset.
<4> User with id = 10 was inserted by `addUser.sql` from class level dataset.TIP: Source code of example above can be https://github.com/database-rider/database-rider/blob/master/rider-core/src/test/java/com/github/database/rider/core/MergeDataSetsIt.java[found here^].
TIP: Junit5 example can be https://github.com/database-rider/database-rider/blob/master/rider-junit5/src/test/java/com/github/database/rider/junit5/MergeDataSetsJUnit5It.java[found here^] and CDI https://github.com/database-rider/database-rider/blob/master/rider-cdi/src/test/java/com/github/database/rider/cdi/MergeDataSetsCDIIt.java[example here^].
=== DataSet Replacers
A DataSet replacer is a `placeholder` used in a dataset file which will be replaced during test execution.
DBRider comes with a `Date Replacer`, `Null replacer`, `Include replacer` and a `Custom replacer`.
TIP: For complete source code of replacers examples, https://github.com/database-rider/database-rider/blob/master/rider-core/src/test/java/com/github/database/rider/core/replacers/[see here^].
==== Date replacer
Following is an example test using a date replacer:
.date-replacements.yml
----
TWEET:
- ID: "1"
CONTENT: "dbunit rules!"
DATE: "[DAY,NOW]"
USER_ID: 1
----.DateReplacementsIt.java
[source,java]
----
@Before
public void setup(){
now = Calendar.getInstance();
}@Test
@DataSet(value = "date-replacements.yml",disableConstraints = true) //disabled constraints so we can have a TWEET with inexistent USER_ID
public void shouldReplaceDateWithNowPlaceHolder() {
Tweet tweet = (Tweet) EntityManagerProvider.em().createQuery("select t from Tweet t where t.id = '1'").getSingleResult();
assertThat(tweet).isNotNull();
assertThat(tweet.getDate().get(Calendar.DAY_OF_MONTH)).isEqualTo(now.get(Calendar.DAY_OF_MONTH));
assertThat(tweet.getDate().get(Calendar.HOUR_OF_DAY)).isEqualTo(now.get(Calendar.HOUR_OF_DAY));
}
----==== Null replacer
.null-replacements.yml
----
TWEET:
- ID: "1"
CONTENT: "[null]"
USER_ID: 1
- ID: "2"
CONTENT: "null"
USER_ID: 1
----.NullReplacementsIt.java
[source,java]
----
@Test
@DataSet(value = "null-replacements.yml", disableConstraints = true)
public void shouldReplaceNullPlaceholder() {
Tweet tweet = (Tweet) EntityManagerProvider.em().createQuery("select t from Tweet t where t.id = '1'").getSingleResult();
assertThat(tweet).isNotNull();
assertThat(tweet.getContent()).isNull();Tweet tweet2 = (Tweet) EntityManagerProvider.em().createQuery("select t from Tweet t where t.id = '2'").getSingleResult();
assertThat(tweet2).isNotNull();
assertThat(tweet2.getContent()).isNotNull().isEqualTo("null");
}
----==== Include replacer
This replacer let you include the content of a file inside a dataset column. For example, imagine `doc.xml` file is located at `resources/datasets/xml/doc.xml` with following content:
```
Anything in this file will be included into the column with [INCLUDE] replacement!
```Now you can include it's content in any dataset using `[INCLUDE]` prefix:
.datasets/yml/include-xml-replacements.yml
```
TWEET:
- ID: "1"
CONTENT: "[INCLUDE]datasets/xml/doc.xml"
```You just have to enable the include replacer:
```
@Test
@DataSet(replacers = IncludeReplacer.class, value = "datasets/yml/include-xml-replacements.yml")
public void shouldReplaceXmlFileContent() {
Tweet tweet = (Tweet) EntityManagerProvider.em().createQuery("select t from Tweet t where t.id = '1'").getSingleResult();assertThat(tweet).isNotNull();
assertThat(tweet.getContent()).contains("" + NEW_LINE +
"Anything in this file will be included into the column with [INCLUDE] replacement!");
}```
[TIP]
====
You can also enable include replacer for all tests via https://github.com/database-rider/database-rider#332-dbunit-configuration[dbunit-config.yml] global config:.src/test/resources/dbunit.yml:
----
cacheConnection: false
cacheTableNames: false
leakHunter: true
properties:
batchedStatements: true
qualifiedTableNames: true
schema:
replacers: [!!com.github.database.rider.core.replacers.IncludeReplacer {}]
----====
==== Custom replacer
The custom replacer makes it possible to create your own replacers.
First we need to implement the `Replacer` interface:
.CustomReplacer.java
[source,java]
----
/**
* Example implementation of Replacer which replaces string 'FOO' for 'BAR'
*
*/
public class CustomReplacer implements Replacer {@Override
public void addReplacements(ReplacementDataSet dataSet) {
dataSet.addReplacementSubstring("FOO", "BAR");
}@Override
public boolean equals(Object o) {
if (this == o) return true;
return o != null && getClass() == o.getClass();
}@Override
public int hashCode() {
return Objects.hash(getClass());
}
}
----.custom-replacements.yml
----
TWEET:
- ID: "1"
CONTENT: "FOO"
USER_ID: 1
----.CustomReplacementIt.java
[source,java]
----
@DBUnit(replacers = CustomReplacer.class) <1>
public class CustomReplacementIt {@Rule
public EntityManagerProvider emProvider = EntityManagerProvider.instance("rules-it");@Rule
public DBUnitRule dbUnitRule = DBUnitRule.instance("rules-it", emProvider.connection());@Test
@DataSet(value = "datasets/yml/custom-replacements.yml", disableConstraints = true, executorId = "rules-it")
public void shouldReplaceFoo() {
Tweet tweet = (Tweet) EntityManagerProvider.em().createQuery("select t from Tweet t where t.id = '1'").getSingleResult();
assertThat(tweet).isNotNull();
assertThat(tweet.getContent()).isNotNull().isEqualTo("BAR");
}
}
----
<1> Custom replacer is enabled via `@DBUnit` annotation `replacers` attribute.[TIP]
====
You can also register a custom replacer for all tests via https://github.com/database-rider/database-rider#332-dbunit-configuration[dbunit-config.yml] global config:.src/test/resources/dbunit.yml:
----
cacheConnection: false
cacheTableNames: false
leakHunter: true
properties:
batchedStatements: true
qualifiedTableNames: true
schema: public
batchSize: 200
fetchSize: 200
allowEmptyFields: true
escapePattern: "[?]"
datatypeFactory: !!com.github.database.rider.core.configuration.DBUnitConfigTest$MockDataTypeFactory {}
replacers: [!!com.github.database.rider.core.replacers.CustomReplacer {}]
----====
=== Scriptable DataSets
Scriptable datasets allows the use of `Javascript` and `Groovy` scripting inside datasets, for that you need to prefix the column content with `groovy:` or `js:`:
.userWithRandomId.yml
----
USER:
- ID: groovy:new Random().nextInt()
NAME: "@realpestano"
- ID: js:Math.floor(Math.random() * 999999)
NAME: "@dbunit"
----[source, java]
----
@Test
@DataSet(value = "datasets/yml/userWithRandomId.yml")
public void shouldReplaceUserIdUsingScriptInDataset() {
User user = (User) EntityManagerProvider.em().createQuery("select u from User u where u.name = '@dbunit'").getSingleResult();
assertThat(user).isNotNull();
assertThat(user.getId()).isNotNull();
}
----[IMPORTANT]
====
For Groovy you need following dependency in classpath:----
org.codehaus.groovy
groovy-all
2.4.6
test
----
====
TIP: For complete source code of scriptable datasets examples, https://github.com/database-rider/database-rider/blob/master/rider-core/src/test/java/com/github/database/rider/core/ScriptReplacementsIt.java#L19[see here^].=== DataSet providers
A dataset provider is a *Java class responsible for defining a dataset* instead of having `yml`, `json`, `xml` files representing your datasets. Following are the steps for creating and using a dataset provider:
. First create a class which implements `DataSetProvider` interface:
+
[source, java]
----
public class UserDataSetProvider implements DataSetProvider {@Override
public IDataSet provide() throws DataSetException {
DataSetBuilder builder = new DataSetBuilder();
IDataSet dataSet = builder
.defaultValue("id", -1) //default value for all tables that don't provide a value for 'id' column
.table("USER") //start adding rows to 'USER' table
.column("ID",1)
.column(name,"@realpestano")
.row() //keeps adding rows to the current table
.column(id,2)
.column("NAME","@dbunit")
.table("TWEET") //starts adding rows to 'TWEET' table
.defaultValue("LIKES", 99) //default value only for table tweet, the value will be used if column is not specified
.column("ID","abcdef12345")
.column("CONTENT","dbunit rules!")
.column("DATE","[DAY,NOW]")
.table("FOLLOWER").column(id,1)
.column("USER_ID",1)
.column("FOLLOWER_ID",2)
.table("USER")// we still can add rows to table already added
.column(name,"@new row")
.build();
return dataSet;
}
}
----
+
The above DataSet provider will generate a dataset like below:
+
----
FOLLOWER:
- ID: 1
USER_ID: 1
FOLLOWER_ID: 2TWEET:
- ID: "abcdef12345"
CONTENT: "dbunit rules!"
DATE: "2019-05-14 19:26:56.0"
LIKES: 99USER:
- ID: 1
NAME: "@dbunit"
- ID: 2
NAME: "@dbrider"
- ID: -1
NAME: "@new row"
----
+
TIP: For more complex dataset examples see https://github.com/database-rider/database-rider/blob/master/rider-core/src/test/java/com/github/database/rider/core/dataset/builder/DataSetBuilderTest.java[DataSetBuilder tests here^].
+[NOTE]
====
The above dataset can be declared using `columns`...`values` syntax:----
builder.defaultValue("id", -1)
.table("user")
.columns("id", "name")
.values(1, "@dbrider")
.values(2, "@dbunit")
.values(null, "@dbunit3")//will use default value
.table("tweet")
.defaultValue("likes", 99)
.columns("id", "content", "date")
.values("abcdef12345", "dbunit rules!", "[DAY,NOW]")
.table("follower")
.columns("id", "user_id", "follower_id")
.values(1, 1, 2)
.build();
----TIP: For datasets with lots of rows and few columns this approach can fit better.
====
. Now use the DataSet provider in `@DataSet` annotation:
+
[source, java]
----
@Test
@DataSet(provider = UserDataSetProvider.class, cleanBefore = true)
public void shouldSeedDatabaseProgrammatically() {
List users = EntityManagerProvider.em().createQuery("select u from User u ").getResultList();
assertThat(users).
isNotNull().
isNotEmpty().hasSize(3).
extracting("name").
contains("@dbunit", "@dbrider", "@new row");
}
----
+
TIP: For more examples, see dataset provider https://github.com/database-rider/database-rider/blob/master/rider-core/src/test/java/com/github/database/rider/core/DataSetProviderIt.java[tests here^].NOTE: You can also use DataSetProvider in `@ExpectedDataset` annotation.
==== DataSet providers DBUnit config
By default DataSetProviders will use <> configured in `dbunit.yml` file so you *will not be able* to use `@DBUnit` to configure providers.
However, you can pass a DBUnit configuration when creating your dataset provider:
```
DBUnitConfig config = new DBUnitConfig().cacheTableNames(true)
.addDBUnitProperty("caseSensitiveTableNames", true);
DataSetBuilder builder = new DataSetBuilder(config);
builder.table("USER")
.row()
.column("ID", 1)
.column("NAME", "@dbunit")
.row()
.column("ID", 2)
.column("NAME", "@dbrider");
return builder.build();
```NOTE: Configuration from `@DataSet` is used the same way as in file based datasets.
=== RiderDSL
If you cannot rely on `@DataSet` annotation because your test runner will not read it (e.g cucumber test runner, spock, kotlin test etc...) or because you don't like annotations, you can use *RiderDSL* to create datasets:
[source, java]
----
@Test
/*same as: @DataSet(value = "yml/user.yml", cleanBefore=true)
@DBUnit(caseSensitiveTableNames = false) */
public void shouldSeedDatabaseUsingRiderDSL() {
RiderDSL.withConnection(emProvider.connection())
.withDataSetConfig(new DataSetConfig("datasets/yml/user.yml")
.cleanBefore(true))
.withDBUnitConfig(new DBUnitConfig().addDBUnitProperty("caseSensitiveTableNames", false))
.createDataSet();
List users = EntityManagerProvider.em().createQuery("select u from User u ").getResultList();
assertThat(users).
isNotNull().
isNotEmpty().
hasSize(2);
}
----TIP: See more https://github.com/database-rider/database-rider/blob/master/rider-core/src/test/java/com/github/database/rider/core/dsl/RiderDSLIt.java#L24[examples here^].
Since `v1.15.0` you can also assert db state with RiderDSL as in `@ExpectedDataSet`:
[source, java]
----
/**
* same as:
* @DataSet(cleanBefore = true)
* @ExpectedDataSet(value = "yml/expectedUsers.yml", ignoreCols = "id")
*/
@Test
public void shouldMatchExpectedDataSet() throws DatabaseUnitException {
RiderDSL.withConnection(emProvider.connection())
.cleanDB();
User u = new User();
u.setName("expected user1");
User u2 = new User();
u2.setName("expected user2");
tx().begin();
em().persist(u);
em().persist(u2);
tx().commit();
withConnection(emProvider.connection())
.withDataSetConfig(new DataSetConfig("yml/expectedUsers.yml"))
.expectDataSet(new ExpectedDataSetConfig().ignoreCols("id"));
}
----
TIP: See more https://github.com/database-rider/database-rider/blob/master/rider-core/src/test/java/com/github/database/rider/core/dsl/RiderDSLIt.java#L292-L375[examples here^].=== Dynamic connection config
In order to have dynamic JDBC connection on your tests one can use system properties, see example below:
[source,java]
----
@RunWith(JUnit4.class)
public class EntityManagerSystemConfigOverrideTestIt {
private static final String PROP_KEY_URL = "javax.persistence.jdbc.url";
private static final String PROP_VALUE_URL = "jdbc:hsqldb:mem:susi;DB_CLOSE_DELAY=-1";
private static final String PROP_KEY_DRIVER = "javax.persistence.jdbc.driver";
private static final String PROP_KEY_USER = "javax.persistence.jdbc.user";
private static final String PROP_KEY_PASSWORD = "javax.persistence.jdbc.password";@Rule
public EntityManagerProvider emProvider = EntityManagerProvider.instance("rules-it");@Rule
public DBUnitRule dbUnitRule = DBUnitRule.instance(emProvider.connection());@BeforeClass
public static void setup() {
System.clearProperty(PROP_KEY_URL);
System.clearProperty(PROP_KEY_DRIVER);
System.clearProperty(PROP_KEY_USER);
System.clearProperty(PROP_KEY_PASSWORD);
System.setProperty(PROP_KEY_URL, "jdbc:hsqldb:mem:susi;DB_CLOSE_DELAY=-1");
System.setProperty(PROP_KEY_DRIVER, "org.hsqldb.jdbc.JDBCDriver");
System.setProperty(PROP_KEY_USER, "sa");
System.setProperty(PROP_KEY_PASSWORD, "");
}@AfterClass
public static void tearDown() {
System.clearProperty(PROP_KEY_URL);
System.clearProperty(PROP_KEY_DRIVER);
System.clearProperty(PROP_KEY_USER);
System.clearProperty(PROP_KEY_PASSWORD);
}//tests using new connection
----This way the https://github.com/database-rider/database-rider/blob/78b0ae11eeed17354f8adc6c35640a160a1447ee/rider-core/src/test/resources/META-INF/persistence.xml#L4-L20[original persistence.xml^] configuration was replaced dynamically before test execution.
TIP: see source code of example above https://github.com/database-rider/database-rider/blob/78b0ae11eeed17354f8adc6c35640a160a1447ee/rider-core/src/test/java/com/github/database/rider/core/EntityManagerDynamicOverrideTestIt.java[here].
=== Transactional Tests
In case of `ExpectedDataSet` you'll usually need a transaction to modify database in order to match expected dataset. In such case you can use a *transactional* test:
[source, java, subs="quotes"]
----
@Test
@DataSet(*transactional=true*)
@ExpectedDataSet(value = "yml/expectedUsers.yml",ignoreCols = "id")
public void shouldMatchExpectedDataSet() {
User u = new User();
u.setName("non expected user1");
User u2 = new User();
u2.setName("non expected user2");
emProvider.em().persist(u);
emProvider.em().persist(u2);
}
----Note that Database Rider will start a transaction before test and commit the transaction *after* test execution but *before* expected dataset comparison.
Below is a pure JDBC example where commented code is not needed because the test is transactional:
[source, java, linenums]
----
@Test
@DataSet(cleanBefore = true, transactional = true)
@ExpectedDataSet(value = "usersInserted.yml")
public void shouldInserUsers() throws SQLException {
Connection connection = flyway.getDataSource().getConnection();
//connection.setAutoCommit(false); //transactional=true
java.sql.Statement statement = connection.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,
ResultSet.CONCUR_UPDATABLE);statement.addBatch("INSERT INTO User VALUES (1, 'user1')");
statement.addBatch("INSERT INTO User VALUES (2, 'user2')");
statement.addBatch("INSERT INTO User VALUES (3, 'user3')");
statement.executeBatch();
//connection.commit();
//connection.setAutoCommit(false);
}
----
TIP: Above example code (which uses JUnit5 and Flyway) can be https://github.com/database-rider/database-rider/blob/master/rider-junit5/src/test/java/com/github/database/rider/junit5/FlywayIt.java#L26[found here^].=== EntityManagerProvider
It is a component which holds JPA entity managers for your tests. To activate it just use the EntityManagerProvider rule in your test use:
[source,java]
----
@RunWith(JUnit4.class)
public class DatabaseRiderIt {@Rule
public EntityManagerProvider emProvider = EntityManagerProvider.instance("PU-NAME");<1>}
----
<1> It will retrieve the entity manager based on a test persistence.xml and store in into EntityManagerProvider which can hold multiple entity managers.NOTE: You can use @BeforeClass instead of junit rule to instantiate the provider.
IMPORTANT: EntityManagerProvider will cache entity manager instance to avoid creating database multiple times, you just need to be careful with JPA first level cache between tests (EntityManagerProvider Rule and <> clears first level cache before each test).
Now you can use emProvider.getConnection() to retrieve jdbc connection and emProvider.em() to retrieve underlying entityManager.
*PU-NAME* refers to test persistence.xml unit name:
.src/test/resources/META-INF/persistence.xml
[source,java]
----
com.github.database.rider.model.User
com.github.database.rider.model.Tweet
com.github.database.rider.model.Follower
----
NOTE: It will only work with *transaction-type="RESOURCE_LOCAL"* because internally it uses Persistence.createEntityManagerFactory(unitName) to get entityManager instance.Above JPA configuration depends on hsqldb (an in memory database) and eclipse link (JPA provider):
[source,xml]
----org.eclipse.persistence
eclipselink
2.5.2
testorg.hsqldb
hsqldb
2.3.3
test----
NOTE: A hibernate entity manager config sample can be https://github.com/database-rider/database-rider/blob/master/rider-examples/src/test/resources/META-INF/persistence.xml[found here^].
TIP: EntityManager provider utility also can be used in other contexts like a CDI producer, https://github.com/database-rider/database-rider/blob/master/rider-cdi/src/test/java/com/github/database/rider/cdi/EntityManagerProducer.java#L21[see here].
== CDI module
If you use CDI in your tests then you should give a try in Database Rider https://github.com/database-rider/database-rider/tree/master/rider-cdi[CDI module^]:
[source,xml]
----com.github.database-rider
rider-cdi
1.44.0
test----
TIP: Use `jakarta` maven classifier if you use jakarta-cdi or jakarta-persistence
=== DBUnit Interceptor
CDI module main component is a CDI interceptor which configures datasets before your tests. To enable DBUnit interceptor you'll need
configure it in you test beans.xml:.src/test/resources/META-INF/beans.xml
[source,xml]
----
com.github.database.rider.cdi.DBUnitInterceptorImpl
----
and then enable it in your tests by using *@DBUnitInterceptor* annotation (class or method level):
[source,java]
----
@RunWith(CdiTestRunner.class)
@DBUnitInterceptor
public class DeltaspikeUsingInterceptorIt {@Inject
DeltaSpikeContactService contactService;@Test
@DataSet("datasets/contacts.yml")
public void shouldQueryAllCompanies() {
assertNotNull(contactService);
assertThat(contactService.findCompanies()).hasSize(4);
}
}
----[IMPORTANT]
====
Make sure the test class itself is a CDI bean so it can be intercepted by `DBUnitInterceptor`. If you're using https://deltaspike.apache.org/documentation/test-control.html[Deltaspike test control^] just enable the following
property in test/resources/META-INF/apache-deltaspike.properties:
----
deltaspike.testcontrol.use_test_class_as_cdi_bean=true
----
====== Cucumber module
this module brings a Cucumber runner which is CDI aware.
NOTE: If you don't use CDI you'll need to https://github.com/database-rider/database-rider#programmatic-creating-datasets[create datasets programmatically] because Cucumber `official` runner https://github.com/cucumber/cucumber-jvm/issues/393[doesn't support JUnit rules^].
[source,xml]
----com.github.database-rider
rider-cucumber
1.44.0
test----
Now you just need to use *CdiCucumberTestRunner*.
=== Examples
.feature file (src/test/resources/features/contacts.feature)
----
Feature: Contacts test
As a user of contacts repository
I want to crud contacts
So that I can expose contacts serviceScenario Outline: search contacts
Given we have a list of constacts
When we search contacts by name ""
Then we should find contactsExamples: examples1
| name | result |
| delta | 1 |
| sp | 2 |
| querydsl | 1 |
| abcd | 0 |Scenario: delete a contact
Given we have a list of contacts
When we delete contact by id 1
Then we should not find contact 1
----.Cucumber cdi runner
[source,java]
----
package com.github.database.rider.examples.cucumber;import com.github.database.rider.cucumber.CdiCucumberTestRunner;
import cucumber.api.CucumberOptions;
import org.junit.runner.RunWith;@RunWith(CdiCucumberTestRunner.class)
@CucumberOptions(
features = {"src/test/resources/features/contacts.feature"},
plugin = {"json:target/cucumber.json"}
//glue = "com.github.database.rider.examples.glues" <1>
)
public class ContactFeature {
}
----<1> You can use glues so step definitions and the runner can be in different packages for reuse between features.
.Step definitions
[source,java]
----
package com.github.database.rider.examples.cucumber; //<1>import com.github.database.rider.api.dataset.DataSet;
import cucumber.api.java.en.Given;
import cucumber.api.java.en.Then;
import cucumber.api.java.en.When;
import org.example.jpadomain.Contact;
import org.example.jpadomain.Contact_;
import org.example.service.deltaspike.ContactRepository;import javax.inject.Inject;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;@DBUnitInterceptor <2>
public class ContactSteps {@Inject
ContactRepository contactRepository;Long count;
@Given("^we have a list of contacts")
@DataSet("datasets/contacts.yml") //<2>
public void given() {
assertEquals(contactRepository.count(), new Long(3));
}@When("^^we search contacts by name \"([^\"]*)\"$")
public void we_search_contacts_by_name_(String name) throws Throwable {
Contact contact = new Contact();
contact.setName(name);
count = contactRepository.countLike(contact, Contact_.name);
}@Then("^we should find (\\d+) contacts$")
public void we_should_find_result_contacts(Long result) throws Throwable {
assertEquals(result,count);
}@When("^we delete contact by id (\\d+)$")
public void we_delete_contact_by_id(long id) throws Throwable {
contactRepository.remove(contactRepository.findBy(id));
}@Then("^we should not find contact (\\d+)$")
public void we_should_not_find_contacts_in_database(long id) throws Throwable {
assertNull(contactRepository.findBy(id));
}
}
----<1> Step definitions must be in the same package of the runner. To use different package you can use *glues* as commented above.
<2> Activates DBUnit CDI interceptor which will read @DataSet annotation in cucumber steps to prepare the database.== Programmatic creating datasets
You can create datasets without JUnit Rule or CDI as we saw above, here is a pure cucumber example (for the same https://github.com/database-rider/database-rider#51-examples[feature above]):
NOTE: If you are looking for a way to *define datasets programmatically* look into <>.
[source,java,linenums]
----
@RunWith(Cucumber.class)
@CucumberOptions(
features = {"src/test/resources/features/contacts-without-cdi.feature"},
plugin = {"json:target/cucumber.json"}
//glue = "com.github.database.rider.examples.glues"
)
public class ContactFeatureWithoutCDI {
}
----And here are the step definitions:
[source,java,linenums]
----
public class ContactStepsWithoutCDI {EntityManagerProvider entityManagerProvider = EntityManagerProvider.newInstance("customerDB");
DataSetExecutor dbunitExecutor;
Long count;
@Before
public void setUp(){
dbunitExecutor = DataSetExecutorImpl.instance(new ConnectionHolderImpl(entityManagerProvider.connection()));
em().clear();//important to clear JPA first level cache between scenarios
}@Given("^we have a list of contacts2$")
public void given() {
dbunitExecutor.createDataSet(new DataSetConfig("contacts.yml"));
assertEquals(em().createQuery("select count(c.id) from Contact c").getSingleResult(), new Long(3));
}@When("^^we search contacts by name \"([^\"]*)\"2$")
public void we_search_contacts_by_name_(String name) throws Throwable {
Contact contact = new Contact();
contact.setName(name);
Query query = em().createQuery("select count(c.id) from Contact c where UPPER(c.name) like :name");
query.setParameter("name","%"+name.toUpperCase()+"%");
count = (Long) query.getSingleResult();
}@Then("^we should find (\\d+) contacts2$")
public void we_should_find_result_contacts(Long result) throws Throwable {
assertEquals(result,count);
}@When("^we delete contact by id (\\d+) 2$")
public void we_delete_contact_by_id(long id) throws Throwable {
tx().begin();
em().remove(em().find(Contact.class,id));
tx().commit();
}@Then("^we should not find contact (\\d+) 2$")
public void we_should_not_find_contacts_in_database(long id) throws Throwable {
assertNull(em().find(Contact.class,id));
}
}
----TIP: For a fluent API to create datasets, you can use <> instead of DataSetExecutor.
== JUnit 5
http://junit.org/junit5/[JUnit 5] is the new version of JUnit and comes with a new extension model, so instead of *rules* you will use extensions in your tests. See example below:
[source,xml]
----com.github.database-rider
rider-junit5
1.44.0
test----
TIP: Use `jakarta` maven classifier if your project uses jakarta dependencies such as jakarta-persistence
[source,java,linenums]
----
@ExtendWith(DBUnitExtension.class)
@RunWith(JUnitPlatform.class)
public class DBUnitJUnit5Test {private ConnectionHolder connectionHolder = () -> instance("junit5-pu").connection(); <1>
@BeforeAll
@DataSet("empty.yml")
public static void setUp() {
}@BeforeEach
@DataSet("users.yml")
public static void setUp() {
}@Test
@DataSet(value = "usersWithTweet.yml")
public void shouldListUsers() {
List users = em().createQuery("select u from User u").getResultList();
assertThat(users).isNotNull().isNotEmpty().hasSize(2);
}
----
<1> DBUnit extension will get JDBC connection by reflection so you need to declare a *field* or *method* with `ConnectionHolder` as return type.[IMPORTANT]
====
If you use the EntityManagerProducer in your junit5 tests ensure to use the class from the package `com.github.database.rider.junit5.util`. This class works but is marked as @Deprecated and will be removed in the 2.0.0 release. For testing purposes you can try the 'new one' from `com.github.database.rider.core.util`. To make this working you need to import '@DBRider' and DBUnitExtension from the `com.github.database.rider.junit5.incubating` packages. Don't forget to set these imports to their defaults when version 2.0.0 is released.====
[IMPORTANT]
====If you use SpringBoot extension for JUnit5 you don't need to declare the field or method, see an https://github.com/database-rider/database-rider/blob/master/rider-examples/spring-boot-dbunit-sample/src/test/java/com/github/database/rider/springboot/SpringBootDBUnitTest.java#L19[example here^].
TIP: If you're using JUnit4 and SpringTestRunner, see <>
====TIP: You can configure JDBC connection using @DBUnit annotation or dbunit.yml, see <>.
[NOTE]
====
You can use `@DBRider` (at test or method level) to enable the extension:[source,java]
----
@RunWith(JUnitPlatform.class)
public class DBRiderAnnotationIt {private ConnectionHolder connectionHolder = () ->
EntityManagerProvider.instance("junit5-pu").connection();@DBRider //shortcut for @ExtendWith(DBUnitExtension.class) and @Test
@DataSet(value = "usersWithTweet.yml")
public void shouldListUsers() {
List users = EntityManagerProvider.em().
createQuery("select u from User u").getResultList();
assertThat(users).isNotNull().isNotEmpty().hasSize(2);
assertThat(users.get(0)).isEqualTo(new User(1));
}
}
----
====== Spring
Add Database Rider Spring extension to your project
[source,xml]
----com.github.database-rider
rider-spring
1.44.0
test----
To enable Database Rider tests use `@DBRider` annotation (from `rider-spring` module), example:
[source, java, linenums]
----
@RunWith(SpringRunner.class)
@SpringBootTest
@DBRider
public class SpringBootDBUnitTest {@Autowired
private UserRepository userRepository;@Test
@DataSet("users.yml")
@ExpectedDataSet("expectedUsers.yml")
public void shouldDeleteUser() throws Exception {
assertThat(userRepository).isNotNull();
assertThat(userRepository.count()).isEqualTo(3);
userRepository.delete(userRepository.findOne(2L));
}
}
----Database Rider will access to database via dataSource registered in application context.
IMPORTANT: This module is designed to work with *JUnit4* and *SpringRunner*, for JUnit5 please use `@DBRider` annotation from `JUnit5` module, see an https://github.com/database-rider/database-rider/blob/master/rider-examples/spring-boot-dbunit-sample/src/test/java/com/github/database/rider/springboot/SpringBootDBUnitTest.java#L19[example here^].
== Quarkus
As Quarkus tests are CDI beans you can just use `@DBRider` from <>:
```
@QuarkusTest
@DBRider
public class QuarkusDBUnitTest {@Inject
BookRepository repository;@Test
@DataSet(value = "books.yml")
public void shouldFindAllBooks() {
List books = repository.findAll().list();
assertThat(books)
.isNotNull()
.hasSize(4)
.extracting("title")
.contains("H2G2","Dune", "Nineteen Eighty-Four", "The Silmarillion");
}@Test
@DataSet(value = "books.yml")
public void shouldFindAllBooksViaRestApi() {
given()
.when().get("/api/books")
.then()
.statusCode(OK.getStatusCode())
.body("", hasSize(4))
.body("title", hasItem("The Silmarillion"));
}
}```
TIP: See full https://github.com/database-rider/database-rider/blob/master/rider-examples/quarkus-dbunit-sample/src/test/java/com/github/quarkus/sample/QuarkusDBUnitTest.java[example here^].
== Micronaut
You can use DBRider in Micronaut JUnit5 tests:
```
@MicronautTest
@com.github.database.rider.junit5.api.DBRider
public class PetRepositoryTest {@Inject
PetRepository petRepository;@Test
@DataSet("pets.yml")
void testRetrievePetAndOwner() {
Pet lassie = petRepository.findByName("Lassie").orElse(null);
assertNotNull(lassie);
assertEquals("Lassie", lassie.getName());
assertEquals("Fred", lassie.getOwner().getName());
}
}
```TIP: See full https://github.com/database-rider/database-rider/blob/master/rider-examples/rider-micronaut/src/test/java/example/repositories/PetRepositoryTest.java[example here^].
== Leak Hunter
Leak hunter is a component based on https://vladmihalcea.com/2016/07/12/the-best-way-to-detect-database-connection-leaks/[this blog post^] which counts open jdbc connections before and after test execution.
To enable it just use *leakHunter = true* in `@DBUnit` annotation, example:
[source, java, linenums]
----
@RunWith(JUnit4.class)
@DBUnit(leakHunter = true)
public class LeakHunterIt {@Rule
public DBUnitRule dbUnitRule = DBUnitRule.instance(new ConnectionHolderImpl(getConnection()));@Rule
public ExpectedException exception = ExpectedException.none();@Test
@DataSet("yml/user.yml")
public void shouldFindConnectionLeak() {
exception.expect(LeakHunterException.class); <1>
exception.expectMessage("Execution of method shouldFindConnectionLeak left 1 open connection(s).");
createLeak();
}@Test
@DataSet("yml/user.yml")
public void shouldFindTwoConnectionLeaks() {
exception.expect(LeakHunterException.class);
exception.expectMessage("Execution of method shouldFindTwoConnectionLeaks left 2 open connection(s).");
createLeak();
createLeak();
}@Test
@DataSet("yml/user.yml")
@DBUnit(leakHunter = false)
public void shouldNotFindConnectionLeakWhenHunterIsDisabled() {
createLeak();
}}
----
<1> If number of connections after test execution are greater than before then a *LeakHunterException* will be raised.TIP: Complete source code of example above can be https://github.com/database-rider/database-rider/blob/master/rider-core/src/test/java/com/github/database/rider/core/LeakHunterIt.java[found here^].
== Export DataSets
Manual creation of datasets is a very error prone task. In order to export database state *after test* execution into datasets files or <> one can use *@ExportDataSet* Annotation or use DataSetExporter component.
=== Example
[source, java, linenums]
----
@Test
@DataSet("datasets/yml/users.yml")
@ExportDataSet(format = DataSetFormat.XML,outputName="target/exported/xml/allTables.xml")
public void shouldExportAllTablesInXMLFormat() {
//data inserted inside method can be exported
}
----After above test execution all tables will be exported to a xml dataset.
NOTE: *XML*, *YML*, *JSON*, *XLS*, *XLSX* and *CSV* formats are supported.
For generating `DataSetBuilder` code you just need to specify *builderType* attribute in `@ExportDataSet`:
[source, java]
----
@Test
@DataSet("datasets/yml/users.yml") //<1>
@ExportDataSet(format = DataSetFormat.XML, outputName = "target/exported/xml/AllTables.xml", builderType = BuilderType.DEFAULT)
public void shouldExportDataSetAsBuilderInDefaultSyntax() {
//AllTables.java file containing DataSetBuilder code will be generated along with AllTables.xml file.
}@Test
@DataSet("datasets/yml/users.yml") //<1>
@ExportDataSet(format = DataSetFormat.XML, outputName = "target/exported/xml/AllTables2.xml", builderType = BuilderType.COLUMNS_VALUES)
public void shouldExportDataSetAsBuilderInColumnValuesSyntax() {
//AllTables.java file containing DataSetBuilder code will be generated along with AllTables2.xml file.
}
----TIP: Full example above (and other related tests) can be https://github.com/database-rider/database-rider/blob/master/rider-core/src/test/java/com/github/database/rider/core/exporter/ExportDataSetIt.java#L32[found here^].
=== Configuration
Following table shows all exporter configuration options:
[cols="3*", options="header"]
|===
|Name | Description | Default
|format| Exported dataset file format.| YML
|includeTables| A list of table names to include in exported dataset.| Default is empty which means *ALL tables*.
|queryList| A list of select statements which the result will used in exported dataset.| {}
|dependentTables| If true will bring dependent tables of declared includeTables.| false
|outputName| Name (and path) of output file.| ""
|====== Programatic export
You can also export DataSets without `@ExportDataSet` by using DataSetExporter component programmatically:
[source,java,linenums]
----
@Test
@DataSet(cleanBefore=true)
public void shouldExportYMLDataSetWithoutAnnotations() throws SQLException, DatabaseUnitException{
tx().begin();
User u1 = new User();
u1.setName("u1");
em().persist(u1);//just insert a user and assert it is present in exported dataset
tx().commit();
DataSetExporter.getInstance().export(emProvider.connection(),
new DataSetExportConfig().outputFileName("target/user.yml"));
File ymlDataSet = new File("target/user.yml");
assertThat(ymlDataSet).exists();
assertThat(contentOf(ymlDataSet)).
contains("USER:"+NEW_LINE +
" - ID: 1"+NEW_LINE +
" NAME: \"u1\""+NEW_LINE);}
----=== DBUnit addon
You can export datasets using https://forge.jboss.org/[JBoss forge^], see https://github.com/database-rider/dbunit-addon/[DBUnit Addon^].
== Examples
There are a lot of examples that can also be used as documentation.
The examples module which contains:
* https://github.com/database-rider/database-rider/tree/master/rider-examples/jpa-productivity-boosters[JPA productivity boosters^]
* https://github.com/database-rider/database-rider/tree/master/rider-examples/dbunit-tomee-appcomposer-sample[DBUnit Application Composer^]
* https://github.com/database-rider/database-rider/tree/master/rider-examples/jOOQ-DBUnit-flyway-example/[jOOQ Flyway DBUnit^]
* https://github.com/database-rider/database-rider/tree/master/rider-examples/spring-boot-dbunit-sample/[SpringBoot Data DBUnit^]
* https://github.com/rmpestano/spring-events/blob/master/src/test/java/com/sambrannen/spring/events/repository/EventRepositoryTests.java#L47[Gradle, SpringBoot and JUnit5 example^]
* https://github.com/database-rider/database-rider/tree/master/rider-examples/quarkus-dbunit-sample/[Quarkus DBUnit^]
* https://github.com/database-rider/database-rider/tree/master/rider-examples/rider-micronaut/[Rider Micronaut^]And also each module contain a lot of tests that you can use as example.
== Changelog
See https://github.com/database-rider/database-rider/releases[project releases on github^] for tracking changes per release.
== Snapshots
Snapshots are available in maven central, to use it just add the following snippet in your pom.xml:
[source,xml]
----
snapshots
libs-snapshot
https://oss.sonatype.org/content/repositories/snapshots
----