dadrus / jpa-unit

JUnit extension to test javax.persistence entities
Apache License 2.0
29 stars 9 forks source link
cdi concordion cucumber database java jpa jpa-test jpa-unit junit junit5 mongodb neo4j persistence test testing

Build Status Quality Gate Coverage Status Technical Debt Maven Central

JPA Unit

Implements JUnit 4 runner and rule, as well as JUnit 5 extension to enable easy testing of javax.persistence entities with an arbitrary persistence provider. Both JPA 2.0, as well as JPA 2.1 is supported (See Issues for limitations).

Features

Credits

The implementation is inspired by the Arquillian Persistence Extension. Some of the code fragments are extracted out of it and adopted to suit the needs.

Maven Integraton

To be able to use the JPA Unit you will have to add some dependencies to your Maven project. For easier dependency management, there is a bom available which you can add to your dependencyManagement section:

<dependencyManagement>
  <dependency>
    <groupId>com.github.dadrus.jpa-unit</groupId>
    <artifactId>jpa-unit-bom</artifactId>
    <version>${jpa-unit.version}</version>
    <type>pom</type>
    <scope>import</scope>
  </dependency>
</dependencyManagement>

The actual dependencies are listed in sections addressing the different possible integration types.

JPA Unit with JUnit 4

To work with JUnit 4, you would need to add jpa-unit4 to your test dependencies:

<dependency>
  <groupId>com.github.dadrus.jpa-unit</groupId>
  <artifactId>jpa-unit4</artifactId>
  <version>${jpa-unit.version}</version>
</dependency>

The basic requirements on the code level are the presence of either

Example using JpaUnitRunner:

@RunWith(JpaUnitRunner.class)
public class MyTest {

    @PersistenceContext(unitName = "my-test-unit")
    private EntityManager manager;

    @Test
    public void someTest() {
        // your code here
    }
}

Example using JpaUnitRule:

public class MyTest {

    @Rule
    public JpaUnitRule rule = new JpaUnitRule(getClass());

    @PersistenceContext(unitName = "my-test-unit")
    private EntityManager manager;

    @Test
    public void someTest() {
        // your code here
    }
}

JPA Unit with JUnit 5

To work with JUnit 5, you would need to add jpa-unit5 to your test dependencies:

<dependency>
  <groupId>com.github.dadrus.jpa-unit</groupId>
  <artifactId>jpa-unit5</artifactId>
  <version>${jpa-unit.version}</version>
</dependency>

On the code level, there is no much choice for JUnit 5

Example:

@ExtendWith(JpaUnit.class)
public class MyTest {

    @PersistenceContext(unitName = "my-test-unit")
    private EntityManager manager;

    @Test
    public void someTest() {
        // your code here
    }
}

Basic Configuration

Irrespectively the JUnit version, the presence of either

is required. Irrespective of the used configuration, the EntityManagerFactory instance is acquired once and lives for the duration of the entire test suite implemented by the given test class.

In both cases the reference to the persistence unit is required as well (e.g. @PersistenceContext(unitName = "my-test-unit") or @PersistenceUnit(unitName = "my-test-unit")). Thus, given the presence of a persistence provider configuration, the examples, shown above, already implement full functional tests.

Like in any JPA application, you have to define a persistence.xml file in the META-INF directory which includes the JPA provider and persistence-unit configuration. For test purposes the transaction-type of the configured persistence-unit must be RESOURCE_LOCAL.

Property replacement

It is possible to configure PersistenceProperties within @PersistenceContext to override values in persistence.xml. In addition it is possible to use system properties to specify them from outside of the test. For example:

-Dtest.jdbc.url=jdbc:oracle:thin:@myHost:1521:DB

@ExtendWith(JpaUnit.class)
public class MyTest {

    @PersistenceContext(unitName = "my-test-unit", properties = {@PersistenceProperty(name = "javax.persistence.jdbc.url", value = "${test.jdbc.url}")})
    private EntityManager manager;

    @Test
    public void someTest() {
        // your code here
    }
}

Control the Behavior

To control the test behavior, JPA Unit comes with a handful of annotations and some utility classes. All these annotations can be applied on class and method level, where the latter always takes precedence over the former. JPA Unit follows the concept of configuration by exception whenever possible. To support this concept its API consists mainly of annotations with meaningful defaults (if the annotation is not present) used to drive the test.

All these elements are described in more detail below.

Transactional Tests

Like already written above automatic transaction management is active if the test uses an EntityManager instance controlled by JPA Unit. To tweak the required behavior you can use the @Transactional annotation either on a test class to apply the same behavior for all tests, or on a single test. This annotation has following properties:

Example which disables transactional support for all tests implemented by a given class:

@RunWith(JpaUnitRunner.class)
@Transactional(TransactionMode.DISABLED)
public class MyTest {

    @PersistenceContext(unitName = "my-test-unit")
    private EntityManager manager;

    @Test
    public void someTest() {
        // your code here
    }
}

TransactionSupport becomes handy, when fine graned transaction management is desired or automatic transaction management is disabled (e.g. the test injects EntityManagerFactory). Following methods are available:

Here a usage example:

@RunWith(JpaUnitRunner.class)
@Transactional(TransactionMode.DISABLED)
public class MyTest {

    @PersistenceContext(unitName = "my-test-unit")
    private EntityManager manager;

    @Test
    public void someTest() {
        newTransaction(manager).execute(() -> {
            // some code wrapped in a transaction
        });

    int result = newTransaction(manager)
        .clearContextOnCommit(true)
        .execute(() -> {
            // some code wrapped in a transaction
            return 1;
        });
    }
}

Seeding the Database

Creating ad-hoc object graphs in a test to seed the database can be a complex task on the one hand and made the test less readable. On the other hand it is usually not the goal of a test case, rather a prerequisite. To address this, JPA Unit provides an alternative way in a form of database fixtures, which are easy configurable and can be applied for all tests or for a single test. To achieve this JPA Unit uses the concept of data sets. In essence, data sets are files containing data to be inserted into the database. Since data sets are database specific, see the corresponding database specific sections for details on supported types and formats.

To seed the database using data set files put the @InitialDataSets annotation either on the test itself or on the test class. This annotation has following properties:

Usage example:

@RunWith(JpaUnitRunner.class)
@InitialDataSets("datasets/initial-data.json")
public class MyTest {

    @PersistenceContext(unitName = "my-test-unit")
    private EntityManager manager;

    @Test
    public void someTest() {
        // your code here
    }
}

Running Custom Catabase Scripts

Seeding the database as described above introduces an additional abstraction level, which is not always desired on one hand. On other hand, there might be a need to disable specific database constraint checks before a database cleanup might be performed (latter only possible in a post test execution step). Usage of plain scripts (e.g. SQL) comes in handy here to execute any action directly on the database level. Simply put @ApplyScriptBefore and/or @ApplyScriptAfter annotation on your test class and/or directly on your test method. Corresponding scripts will be executed before and/or after test method accordingly. If there is definition on both, test method level annotation takes precedence.

Both annotation have the following properties:

Usage example:

@RunWith(JpaUnitRunner.class)
@ApplyScriptBefore("scrips/some-script.script")
public class MyTest {

    @PersistenceContext(unitName = "my-test-unit")
    private EntityManager manager;

    @Test
    @ApplyScriptAfter({
        "scrips/some-other-script-1.script",
        "scrips/some-other-script-2.script"
    })
    public void someTest() {
        // your code here
    }
}

Database Content Verification

Asserting database state directly from testing code might imply a huge amount of work. @ExpectedDataSets comes in handy here. Just put this annotation either on a test class to apply the same assertions for all tests, or on a single test method (the latter takes precedence) and JPA Unit will use the referenced files to check whether the database contains entries you are expecting after the test execution.

The @ExpectedDataSets annotation has the following properties:

Both orderBy and excludeColumns properties can be used to define columns with and without dotted notation. With dotted notation one can explicitly define the table/collection in addition to the actual field/property (see also the example below).

Usage example:

@RunWith(JpaUnitRunner.class)
public class MyTest {

    @PersistenceContext(unitName = "my-test-unit")
    private EntityManager manager;

    private Depositor depositor;

    @Before
    public void createTestData() throws OperationNotSupportedException {
        depositor = new Depositor("Max", "Doe");
        depositor.addContactDetail(new ContactDetail(ContactType.EMAIL, "john.doe@acme.com"));
        depositor.addContactDetail(new ContactDetail(ContactType.TELEPHONE, "+1 22 2222 2222"));
        depositor.addContactDetail(new ContactDetail(ContactType.MOBILE, "+1 11 1111 1111"));
        final InstantAccessAccount instantAccessAccount = new InstantAccessAccount(depositor);
        final GiroAccount giroAccount = new GiroAccount(depositor);
        giroAccount.setCreditLimit(1000.0f);
        giroAccount.deposit(100.0f);
        giroAccount.transfer(150.0f, instantAccessAccount);
    }

    @Test
    @ExpectedDataSets(value = "datasets/expected-data.json", excludeColumns = {
            "ID", "DEPOSITOR_ID", "ACCOUNT_ID", "VERSION"
    }, orderBy = {
            "CONTACT_DETAIL.TYPE", "ACCOUNT_ENTRY.TYPE"
    })
    public void test1() {
        manager.persist(depositor);
    }
}

Cleaning the Database

Strategy based Cleanup

By default the database content is entirely erased before each test. If you want to control this behavior, @Cleanup annotation is your friend. It defines when database cleanup should be triggered and which cleanup strategy to apply. As always, you can use this annotation globally on a class level or on a method level. The latter takes precedence.

The @Cleanup annotation has the following properties:

Usage example:

@RunWith(JpaUnitRunner.class)
@Cleanup(TransactionMode.BEFORE) // change the default phase from AFTER to BEFORE
public class MyTest {

    @PersistenceContext(unitName = "my-test-unit")
    private EntityManager manager;

    @Test
    public void someTest() {
        // your code here
    }

    @Test
    @Cleanup(phase = CleanupPhase.AFTER, strategy = CleanupStrategy.USED_ROWS_ONLY)
    public void otherTest() {
        // your code here
    }
}

Using Custom Scripts

If automatic cleanup as described in Strategy based Cleanup does not suit your needs, @CleanupUsingScripts might be your friend. You can use it to execute custom scripts to clean your database before or after the test. Just put this annotation either on the test itself or on the test class. As always the definition applied on the test method level takes precedence.

This annotation has following properties:

Usage example:

@RunWith(JpaUnitRunner.class)
public class MyTest {

    @PersistenceContext(unitName = "my-test-unit")
    private EntityManager manager;

    @Test
    @CleanupUsingScripts(phase = CleanupPhase.AFTER, value = "scripts/delete-all.script")
    public void otherTest() {
        // your code here
    }
}

Controlling Second Level Cache

The JPA L2 cache can be a two-edged sword if configured or used improperly. Therefore it is crucial to test the corresponding behavior as early as possible. JPA Unit enables this by the usage of the @CleanupCache annotation either on a test class, to apply the same behavior for all tests, or on a single test level to define whether and when the JPA L2 cache should be evicted . Please note: The behavior of the second level can be configured in the persistence.xml. If @CleanupCache is used and the defined phase (see below) is not NONE, the second level cache will be evicted regardless the settings defined in the persistence.xml. This annotation has following properties:

Example which evicts the JPA L2 cache before the execution of each test method implemented by a given class:

@RunWith(JpaUnitRunner.class)
@CleanupCache(TransactionMode.BEFORE)
public class MyTest {

    @PersistenceContext(unitName = "my-test-unit")
    private EntityManager manager;

    @Test
    public void someTest() {
        // your code here
    }
}

Bootstrapping of DB Schema & Contents

Bootstrapping of the data base schema, as well as the handling of its evolution over a period of time is a crucial topic. To enable a data base schema & contents setup close to the productive environment in which the JPA provider usually relies on this given DB setup, the corresponding database specific actions need to be done before the JPA provider is loaded by accessing the data base directly. JPA Unit enables this by the usage of the @Bootstrapping annotation. A dedicated method of a test class, which implements a data base scheme & contents setup can be annotated with this annotation and is required to have one parameter of type DataSource. JPA Unit will execute this method very early in its bootstrapping process. Because of this neither EntityManager nor EntityManagerFactory cannot be used at this time.

For tests, which use this feature, the JPA provider should be configured not to drop and create the data base schema on start, rather to verify it. For e.g. Hibernate this can be achieved by setting the hibernate.hbm2ddl.auto property to the value validate.

Usage example (bootstrapping with FlywayDB):

@RunWith(JpaUnitRunner.class)
public class FlywaydbTest {

    @PersistenceContext(unitName = "my-verification-unit")
    private EntityManager manager;

    @Bootstrapping
    public void prepareDataBase(final DataSource ds) {
        // creates db schema and puts some data
        final Flyway flyway = new Flyway();
        flyway.setDataSource(ds);
        flyway.clean();
        flyway.migrate();
    }

    @Test
    public void someTest() {
        // your test specific code here
    }
}

Supported Databases

Depending on the used database, you will have to add a dependency for a database specific JPA-Unit plugin.

RDBMS Databases

For all relational databases the jpa-unit-rdbms dependency needs to be added:

<dependency>
  <groupId>com.github.dadrus.jpa-unit</groupId>
  <artifactId>jpa-unit-rdbms</artifactId>
  <version>${jpa-unit.version}</version>
</dependency>

Here JPA Unit makes use of the standard

The last two are optional (depending on the requirements of the underlying database).

properties to access the database directly.

Here an example of a persistence.xml file which configures EclipseLink and H2 database:

<persistence version="2.1"
  xmlns="http://xmlns.jcp.org/xml/ns/persistence" 
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence 
    http://www.oracle.com/webfolder/technetwork/jsc/xml/ns/persistence/persistence_2_1.xsd">

  <persistence-unit name="my-test-unit" transaction-type="RESOURCE_LOCAL">
    <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>

    <!-- your classes converters, etc -->

    <properties>
      <property name="eclipselink.ddl-generation" value="drop-and-create-tables" />
      <property name="eclipselink.target-database" 
          value="org.eclipse.persistence.platform.database.H2Platform" />
      <property name="javax.persistence.jdbc.driver" value="org.h2.Driver" />
      <property name="javax.persistence.jdbc.url" 
          value="jdbc:h2:mem:serviceEnablerDB;DB_CLOSE_DELAY=-1" />
      <property name="javax.persistence.jdbc.user" value="test" />
      <property name="javax.persistence.jdbc.password" value="test" />
    </properties>
  </persistence-unit>
</persistence>

Data Set Format

Thanks to DBUnit, which is used internally for all RDBMS, following data set formats are supported:

Here some data set examples:

<dataset>
    <DEPOSITOR id="100" version="1" name="John" surname="Doe" />
    <ADDRESS id="100" city="SomeCity" country="SomeCountry" street="SomeStreet 1" 
             zip_code="12345" owner_id="100"/>
    <ADDRESS id="101" city="SomeOtherCity" country="SomeOtherCountry" street="SomeStreet 2" 
             zip_code="54321" owner_id="100"/>
<dataset>
DEPOSITOR:
  - id: 100
    version: 1
    name: John
    surname: Doe

ADDRESS:
  - id: 100
    city: SomeCity
    country: SomeCountry
    street: SomeStreet 1
    zip_code: 12345
    owner_id: 100
  - id: 101
    city: SomeOtherCity
    country: SomeOtherCountry
    street: SomeStreet 2
    zip_code: 54321
    owner_id: 100
"DEPOSITOR": [
    { "id": "100", "version": "1", "name": "John", "surname": "Doe" }
],
"ADDRESS": [
    { "id":"100", "city":"SomeCity", "country": "SomeCountry", "street": "SomeStreet 1", 
      "zip_code": "12345", "owner_id": "100" },
    { "id":"101", "city":"SomeOtherCity", "country": "SomeOtherCountry", "street": "SomeStreet 2", 
      "zip_code": "54321", "owner_id": "100" }
]

DBUnit specific configuration

All DBUnit specific settings can be configured by just making a dbunit.properties file available in the classpath. Long and short property names are supported.

Please note, that JPA-Unit configures the required DBUnit datatypeFactory and metadataHandler automatically based on the used JDBC driver.

MongoDB

For MongoDB, the jpa-unit-mongodb dependency needs to be added:

<dependency>
  <groupId>com.github.dadrus.jpa-unit</groupId>
  <artifactId>jpa-unit-mongodb</artifactId>
  <version>${jpa-unit.version}</version>
</dependency>

JPA Unit needs to connect to a running MongoDB instance. This is done using mongo-java-driver. Usage of an in-process, in-memory MongoDB implementations, like Fongo is not possible. To overcome this limitation, or made it at least less painful, one can use e.g.

Supported JPA Provider

Since JPA does not address NoSQL databases, each JPA provider defines its own properties. These properties are also the only dependencies to a specific JPA provider implementation. As of todays JPA Unit MongoDB extension can use the properties of the following JPA provider:

Data Set Format

Default data set format for MongoDB is JSON. In a simple case it must comply with the following example structure:

{
  "collection_name_1": [
    {
      "property_1": "value_1",
      "property_2": "value_2"
    },
    {
      "property_3": NumberLong(10),
      "property_4": { "$date": "2017-06-07T15:19:10.460Z" }
    }
  ],

  "collection_name_2": [
    {
      "property_5": 4,
      "property_7": "value_7"
    }
  ]
}

If indexes (for more information on MongoDB indexes and types see MongoDB Indexes) need to be included as well, the following structure applies:

{
  "collection_name_1": {
    "indexes": [
      {
        "index": {
          "property_1": 1
        },
        "index": {
          "property_2": 1,
          "options": {
            "unique": true,
            "default_language": "english"
          }
        }
      },
      {
        "index": {
          "property_3": 1,
          "property_4": -1
        }
      }
    ],
    "data": [
      {
        "property_1": "value_1",
        "property_2": "value_2"
      },
      {
        "property_3": NumberLong(10),
        "property_4": { "$date": "2017-06-07T15:19:10.460Z" }
      }
    ]
  }
}

Please note, that in this case the collection document consists of two subdocuments. The first one - indexes is where the indexes are defined. Basically this is which fields of the collection are going to be indexed. The second one - data, where all documents, which belong to the collection under test, are defined. In both cases all the types defined by MongoDB are supported.

Neo4j

For Neo4j, the jpa-unit-neo4j dependency needs to be added:

<dependency>
  <groupId>com.github.dadrus.jpa-unit</groupId>
  <artifactId>jpa-unit-neo4j</artifactId>
  <version>${jpa-unit.version}</version>
</dependency>

JPA Unit needs to connect to a running Neo4j instance. This is done using Neo4j JDBC Driver, which as a neat side effect makes bootstrapping (see Bootstrapping of DB Schema & Contents) of the DB possible e.g. using LIQUIGRAPH. Usage of an in-process, in-memory Neo4j implementation is only possible if the embedded data base is configured to expose BOLT or HTTP interfaces. This can be achieved by the use of e.g.

Supported JPA Provider

Since JPA does not address NoSQL databases, each JPA provider defines its own properties. These properties are also the only dependencies to a specific JPA provider implementation. As of todays JPA Unit Node4j extension can use the properties of the following JPA provider:

Even the last two support Neo4j in an embedded mode only, both can be used if the embedded Neo4j database exposes HTTP or BOLT interfaces (like available with Neo4j Harness). Since there is no possibility to define the corresponding interfaces (BOLT or HTTP) in a standard way (by the means of the regular persistence.xml), JPA-Unit makes use of a jpa-unit.properties file, which has to be made available on the class path and which has to define the following properties:

Same approach can be used if Hibernate OGM Neo4j extension is used in embedded mode.

A special note on Kundera: It still depends on a pretty old Neo4j (1.8.1) version. So even JPA-Unit's neo4j extension understands the configuration dialect of Kundera, I didn't find a version of Noe4j Harness, which can expose BOLT or HTTP protocols and is binary compatible with the version used by Kundera. With other words, as long as Kundera is not updated to use a more recent version of Neo4j, the usage of this JPA provider will most probably be not possible.

Data Set Format

Thanks to jgrapht, which is used internally for graph handling, following data set formats are supported:

If you want to generate/export data out of an existing Neo4j instance APOC can be really helpful. Be however aware, that the data exported by APOC does not fully comply with GraphML. APOC generated file does not include key elements definitions for label and labels data elements for edge, respectively node elements. It also adds additional attributes (label and labels) to node and edge elements which are not defined by GraphML. The first one needs to be addressed by adding the missing <key id="labels" for="node" attr.name="labels" attr.type="string"/> and <key id="label" for="edge" attr.name="label" attr.type="string"/> to the graph element. The second one can be ignored - schema compliance is not enforced by JPA-Unit's neo4j extension.

Here's an example:

<?xml version="1.0" encoding="UTF-8"?>
<graphml xmlns="http://graphml.graphdrawing.org/xmlns" 
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
  xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns http://graphml.graphdrawing.org/xmlns/1.0/graphml.xsd">
  <key id="name" for="node" attr.name="name" attr.type="string"/>
  <key id="id" for="node" attr.name="id" attr.type="long"/>
  <key id="labels" for="node" attr.name="labels" attr.type="string"/>
  <key id="label" for="edge" attr.name="label" attr.type="string"/>
  <graph id="G" edgedefault="directed">
    <node id="1" labels=":Person">
      <data key="labels">:Person</data>
      <data key="name">Tom</data>
      <data key="id">1</data>
    </node>
    <node id="2" labels=":Person">
      <data key="labels">:Person</data>
      <data key="name">Jerry</data>
      <data key="id">2</data>
    </node>
    <edge id="3" source="1" target="2" label="friend">
      <data key="label">friend</data>
    </edge>
  </graph>
</graphml>

CDI Integration

To be able to use the JPA Unit with CDI, all you need in addition to your CDI test dependency, like DeltaSpike Test-Control Module or Gunnar's CDI Test, is to add the following dependency to your Maven project :

<dependency>
  <groupId>com.github.dadrus.jpa-unit</groupId>
  <artifactId>jpa-unit-cdi</artifactId>
  <version>${jpa-unit.version}</version>
</dependency>

This dependency implements a CDI extension, which proxies the configured EntityManager producer. During a JPA Unit test run it uses the EntityManager configured in the test class instance. In all other cases it just uses the proxied producer.

Usage example:

@RunWith(CdiTestRunner.class)
public class CdiEnabledJpaUnitTest {

    @Rule
    public JpaUnitRule rule = new JpaUnitRule(getClass());

    @PersistenceContext(unitName = "my-test-unit")
    private static EntityManager manager;

    @Inject
    private SomeRepository repo;

    @Test
    public void someTest() {
        // use CDI managed objects, like the repo from above
    }
}

Cucumber Integration

Cucumber is a BDD test framework. To be able to use JPA Unit with it, all you need in addition to cucumber dependencies is to add the following dependency to your Maven project :

<dependency>
  <groupId>com.github.dadrus.jpa-unit</groupId>
  <artifactId>jpa-unit-cucumber</artifactId>
  <version>${jpa-unit.version}</version>
</dependency>

This dependency implements a Cucumber extension (ObjectFactory) which intercepts all cucumber feature glue methods to enable the usage of JPA Unit annotations.

Since each feature/scenario glue, compared to regular JUnit tests, implements a single test specification, JPA Unit disables automatic data base cleanup. To avoid stale data between the executions of different scenarios or more general different tests, you should take care of the cleanup by yourself. This is the only difference to the regular behavior. This cleanup can be achieved, e.g. using the @Cleanup annotation on e.g. a method annotated with the cucumber @After annotation.

Analogue to regular JUnit tests a cucumber glue needs to reference either an EntityManager or an EntityManagerFactory. The EntityManagerFactory lives for the duration of the scenario execution.

Same rules as for regular JUnit tests apply for the EntityManager as well: An EntityManager for TRANSACTION PersistenceContextType lives only for the duration of the execution of the glue method. An EntityManager for EXTENDED PersistenceContextType has the life time of the EntityManagerFactory and is closed after the last glue method is executed. Latter configuration might be a better choice for cucumber glue.

Usage example:

@RunWith(Cucumber.class)
public class CucumberTest {
    // According to cucumber, this class should not implement any tests
}
public class CucumberGlue {

    @Rule
    public JpaUnitRule rule = new JpaUnitRule(getClass());

    @PersistenceContext(unitName = "my-test-unit", type = PersistenceContextType.EXTENDED)
    private EntityManager manager;

    @Given("^an some existing data in the db$")
    @InitialDataSets("datasets/initial-data.json")
    public void seedDatabase() {
        // data base is seeded thanks to the jpa unit annotation
    }

    @When("^a new object is inserted$")
    public void updataData() {
        // e.g. manager.persist(new SomeEntity());
    }

    @Then("^it is expected in the db")
    @ExpectedDataSets(value = "datasets/expected-data.json")
    public void verifyData() {
        // db verification is done thanks to the jpa unit annotation
    }

    @After
    @Cleanup
    public void cleanupDb() {
        // to avoid stale data
    } 
}

If you would like to use cucumber with JPA Unit and CDI, you will have to follow the requirements from CDI Integration. However, since the usage of multiple runners is not possible, you'll have to start the CDI container manually. Here an example with Deltaspike:

@RunWith(Cucumber.class)
public class CucumberTest {
    // According to cucumber, this class should not implement any tests
    // but we can implement global setup and tear down functionality here.

    private static CdiContainer cdiContainer;

    @BeforeClass
    public static void startContainer() {
        cdiContainer = CdiContainerLoader.getCdiContainer();
        cdiContainer.boot();
    }

    @AfterClass
    public static void stopContainer() {
        cdiContainer.shutdown();
    }
}

Now you can inject your dependencies into glue objects.

Concordion Integration

Concordion is a BDD test framework. To be able to use JPA Unit with it, all you need in addition to concordion dependencies is to add the following dependency to your Maven project :

<dependency>
  <groupId>com.github.dadrus.jpa-unit</groupId>
  <artifactId>jpa-unit-concordion</artifactId>
  <version>${jpa-unit.version}</version>
</dependency>

This dependency implements a specialized ConcordionRunner - the JpaUnitConcordionRunner which you have to use with the JUnit @RunWith annotation on the class level. This runner hooks into concordion implementation and intercepts all fixture methods referenced from corresponding specifications, thus enables the usage of JPA Unit annotations on fixture methods.

Since each fixture/specification(-example), compared to a regular JUnit tests, implements a single test specification, JPA Unit disables automatic data base cleanup. To avoid stale data between the executions of different scenarios or more general different tests, you should take care of the cleanup by yourself. This is the only difference to the regular behavior. This cleanup can be achieved, e.g. using the @Cleanup annotation on e.g. a method annotated with the concordion @AfterSpecification annotation.

Analogue to regular JUnit tests a concordion fixture needs to reference either an EntityManager or an EntityManagerFactory. The EntityManagerFactory lives for the duration of the scenario execution.

Same rules as for regular JUnit tests apply for the EntityManager as well: An EntityManager for TRANSACTION PersistenceContextType lives only for the duration of the execution of the fixture method. An EntityManager for EXTENDED PersistenceContextType has the life time of the EntityManagerFactory and is closed after the last fixture method is executed. Latter configuration might be a better choice for concordion fixtures.

Usage example:

@RunWith(JpaUnitConcordionRunner.class)
public class ConcordionFixture {

    @PersistenceContext(unitName = "my-test-unit", type = PersistenceContextType.EXTENDED)
    private EntityManager manager;

    public Depositor createNewCustomer(final String customerName) {
        final String[] nameParts = customerName.split(" ");
        final Depositor depositor = new Depositor(nameParts[0], nameParts[1]);
        new InstantAccessAccount(depositor);
        return depositor;
    }

    public void finalizeOnboarding(final Depositor depositor) {
        manager.persist(depositor);
    }

    @ExpectedDataSets(value = "datasets/max-payne-data.json", excludeColumns = {
            "ID", "DEPOSITOR_ID", "ACCOUNT_ID", "VERSION", "accounts"
    })
    @Cleanup(phase = CleanupPhase.AFTER)
    public void verifyExistenceOfExpectedObjects() {
        // The check is done via @ExpectedDataSets annotation
    }
}

The associated specification looks like this:

# Create New Depositor

During the on-boarding of a new banking customer for an instant access account a new depositor as well
as a new instant access account have to be created. 

### [Example](- "Onboard a new customer")

Given a new customer *[Max Payne](- "#customer = createNewCustomer(#TEXT)")*, applying for an instant
access account

When the [onboarding process completes](- "finalizeOnboarding(#customer)")

Then a new depositor object and a new instant access account object are 
[present in the system](- "verifyExistenceOfExpectedObjects()").

If you would like to use concordion with JPA Unit and CDI, you will have to follow the requirements from CDI Integration. However, since the usage of multiple runners is not possible, you'll have to start the CDI container manually. Here's an excerpt demonstrating CDI usage:

@RunWith(JpaUnitConcordionRunner.class)
public class ConcordionFixture {
    private static CdiContainer cdiContainer;

    @BeforeSpecification
    public static void startContainer() {
        cdiContainer = CdiContainerLoader.getCdiContainer();
        cdiContainer.boot();
    }

    @AfterSpecification
    public static void stopContainer() {
        cdiContainer.shutdown();
    }

    // ...
}

Now you can inject your dependencies into the fixture class.

If you organize your fixtures/specifications in a hierarchical story, it will make more sense to start and stop the CDI container in the root fixture using methods annotated with @BeforeSuite, respectively @AfterSuite.