Java EE 6 Testing with Arquillian Persistence Extension

Java EE 6 Testing with Arquillian Persistence Extension

Today I will describe my newly discovered library which is an extension to the great JBoss Arquillian project.

The mission of the Arquillian project is to provide a simple test harness that abstracts away all container lifecycle and deployment from the test logic so developers can easily produce a broad range of integration tests for their enterprise Java applications.

Arquillian persistence extension allows you to test JPA related code without filling up the database with test data. All you have to do is to populate .yml file with your data specified. That will be best described with an example.

First of all add arquillian persistence dependencies to your project:

<dependency>
            <groupId>org.jboss.arquillian.extension</groupId>
            <artifactId>arquillian-persistence-api</artifactId>
            <version>${version.arquillian_persistence}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.jboss.arquillian.extension</groupId>
            <artifactId>arquillian-persistence-impl</artifactId>
            <version>${version.arquillian_persistence}</version>
            <scope>test</scope>
        </dependency>

where ‘${version.arquillian_persistence}’ is equal to ‘1.0.0.Alpha2’. I assume that you have the basic arquillian tests (for EJB’s and CDI) working, If not read on.

I have found myself setting up arquillian enabled project quite cumbersome, always ending up with some classpath dependency problems while trying to remote test my classes. For those of you new to Arquillian I would strongly recommend starting up with working example provided by Bartosz Majsak and available on github as a ‘Beer-Advisor‘ project.

The ‘Beer-Advisor’ project can be quickly refactored to suit your needs, it lack comments but its so clearly written that it shouldn’t be a problem to grasp the idea.

Ok then, lets get going with the arquillian persistence extension, we are going to use two nice annotations to help us testing our JPA layer: @UsingDataSet and @ShouldMatchDataSet.

We start off with doing some setup, I will copy part of the example from the Beer-Advisor project which you can easily clone and try for yourself:

Having an entity class Beer:

@Entity
public class Beer implements Serializable
{
   private static final long serialVersionUID = 5892013208071126314L;

   @Id
   @GeneratedValue(strategy = GenerationType.AUTO)
   private Long id;

   @Basic
   private String name;

   @Basic
   private BigDecimal price;

   @Basic
   private BigDecimal alcohol;

   @Basic
   private String code;

   @Enumerated(EnumType.STRING)
   private Type type;

   // If lazy then Glassfish Embedded is facing this problem: https://bugs.eclipse.org/bugs/show_bug.cgi?id=323403
   @ManyToOne(optional = false, cascade = {CascadeType.PERSIST, CascadeType.MERGE})
   private Brewery brewery;

   Beer()
   {
      // to satisfy JPA
   }

   public Beer(Brewery brewery, Type type, String name, BigDecimal price, BigDecimal alcohol)
   {
      this.name = name;
      this.price = price;
      this.alcohol = alcohol;
      this.type = type;
      this.brewery = brewery;
      this.brewery.addBeer(this);
   }
........ Getters and Setters ...............

and simple DAO for managing Beer objects on the persistence layer:

@RequestScoped
public class JpaBeerRepository implements BeerRepository
{
   @PersistenceContext
   private EntityManager em;

   @Override
   public Beer getById(Long id)
   {
      return em.find(Beer.class, id);
   }
..........

@Override
   public Set<Beer> fetchAll()
   {
      CriteriaBuilder criteriaBuilder = em.getCriteriaBuilder();
      CriteriaQuery<Beer> query = criteriaBuilder.createQuery(Beer.class);
      Root<Beer> from = query.from(Beer.class);
      CriteriaQuery<Beer> select = query.select(from);

      Set<Beer> result = new HashSet<Beer>();
      result.addAll(em.createQuery(select).getResultList());

      return result;
   }

   @Override
   public void save(Beer beer)
   {
      if (beer.getId() == null)
      {
         em.persist(beer);
      }
      else
      {
         em.merge(beer);
      }
   }
..................

We can easily test our DAO by creating datasets in yml format and test our logic against it:

Beer:
  - id: 1
    alcohol: 4.5
    name: "Mocny Full"
    price: 1.0
    type: LAGER
    brewery_id: 1
  - id: 2
    alcohol: 55.0
    name: "End of history"
    price: 765
    type: BLOND_ALE
    brewery_id: 2
  - id: 3
    alcohol: 41.0
    name: "Sink The Bismarck!"
    price: 64.0
    type: QUADRUPEL_IPA
    brewery_id: 2
  - id: 4
    alcohol: 8.5
    name: "Delirium Tremens"
    price: 10.0
    type: PALE_ALE
    brewery_id: 3
  - id: 5
    alcohol: 8.4
    name: "Pauwel Kwak"
    price: 4.0
    type: AMBER
    brewery_id: 4

You should put your .yml file into test/resources/datasets folder in you maven project

@UsingDataSet annotation will populate our database with the data specified in beers.yml file

   @Test
   @UsingDataSet("beers.yml")
   public void shouldReturnBeerByItsId() throws Exception
   {
      // given
      Long beerId = 1L;
      String expectedName = "Mocny Full";

      // when
      Beer beers = beerRepository.getById(beerId);

      // then
      assertThat(beers.getName()).isEqualTo(expectedName);
   }

Now, lets say we want to add new Beer and see if the database will have a specific data in it, for this purpose we are going to use @ShouldMatchDataSet(..)

@Test
    @ShouldMatchDataSet("expected-insert-beer.yml")
    public void shouldSaveNewBeer() throws Exception{
        //when
        Beer b = new Beer();
                //set the necessary fields on the beer object
        beerRepository.save(b);
        assertThat(beerRepository).isNotNull();
    }

after inserting our Beer object to the database through the EntityManager we should end up with a single beer record in our database, exactly like the one specified in ‘expected-insert-beer.yml’.

4 responses on “Java EE 6 Testing with Arquillian Persistence Extension

  1. Bartosz Majsak December 22, 2011 at 11:08 am

    Thanks a lot for this nice blog post about Persistence Extension. You are the very fist geek who wrote about it! It’s the best Christmas gift 🙂

    Besides ‘Beer Advisor’ app which was created for the demo purposes during JDD in Krakow, there is also a showcase which is illustrating another features of this extension – https://github.com/arquillian/arquillian-showcase/tree/master/jpa-persistence-extension

    One thing worth to mention is that YAML is not the only format available. You can also use other DBUnit formats such as Flat XML or Excel (JSON is on the roadmap too). Also ‘datasets’ folder is just handy convention, you can put your data sets anywhere on the classpath.

    As I guess every tool in the alpha stage it still have some issues, but I’m working hard to make it better. I hopefully will release Alpha3 this year. This will fix WAR/EAR deployments and will give you a possibility to seed database using plain SQL. Stay tuned!

    Please let me know if you have any suggestions or general feedback. Of course you are more than welcome to fork and hack around and help us make it even better 🙂

    Thanks again!
    Bartosz

    P.S. I really like your remark about the comments 🙂

  2. Control Room Software January 20, 2012 at 10:27 am

    Thank you for sharing for this coding information

  3. Michael Brill February 16, 2012 at 12:47 pm

    First of all thank you for this comprehensive Tutorial. Since we are going TDD in our new project, Arquillian Persistence Extension seems to be a nice and painless way to write integration tests. However we encountered some problems in our test scenarios we couldn’t get rid of. In our setup (JBoss AS 7.1.0.CR1b, Arquillian 1.0.0.CR7) the 1.0.0.Alpha3 Version doesn’t seem to work at all. When running the test we get the Exception mentioned in [SHRINKWRAP-93] on GitHub. 1.0.0.Alpha2 works for that part, but doesn’t seem to do any database operations. Instead it complains about missing dataset. We went in the code with the debugger, but all paths found where actually correct.

    It would be a pity if we’d have to switch to another framework since it quiet fits our concept. If someone has ever seen this behaviour before I would be grateful for some hints.

    Cheers,
    Michael

  4. admin February 16, 2012 at 1:35 pm

    Hello Michael, I guess you would have to read about this extension a bit more and go through the examples available on github. The nice thing about this extension is that you define the dataset before the test as well as expected data after the test without actually using the database, you should use yaml files instead.

Leave a Reply