June 20, 2016

Unit Testing JPA...Stop Integration Testing!

Introduction
I want to start by asking a simple question.
"How do you unit test your JPA classes?"
Now, before you answer look carefully at the question.  The key words are unit test.  Not test but unit test.

Conversation
In my experience, after asking this question, the conversation goes something like this.
"How do you unit test your JPA domain objects?"
"We've developed this shared project which starts an in-memory Derby database (See my previous blog article about how to do this for integration testing) and it automatically runs scripts in your project to build the database and insert data for the tests."
 "Where is this shared project?"
"It's in Nexus.  We pull it in as a Maven dependency."
"I see it in the POM.  You know this dependency doesn't have <scope>test</scope>"
 "Huh?"
"Never mind.  Where's the source code for the project?"
 "The person who made it doesn't work here anymore so we don't know where the source code is.  But we haven't had to change it."
"Where's the documentation?"
"Umm...we just copy stuff from existing projects and change the DDLs and queries"
"Why are you starting Derby for a unit test?  Unit tests must be easy to maintain and fast to run.  You can't be starting any frameworks like JPA or relying on external resources like a database.  They make the unit tests complicated and slow running."
 "Well, the classes use JPA, you need a database to test them."
"No, you don't.  You don't need to start a database.  JPA relies heavily on annotations.   All you need to do is make sure all the classes, fields, and getters are annotated correctly. So just unit test the annotations and the values of their properties."
"But that won't tell you if it works with the database."
"So?  You are supposed to be writing simple and fast unit tests! Not integration tests! For a unit test all you need to know is if the JPA classes are annotated properly. If they're annotated properly they'll work."
"But what if the databases changes?"
"Good question, but not for a unit test.  For a unit test all you need to know is that what was working before is still working properly.  For frameworks like JPA that depend on annotations to work properly, your unit tests need to make sure the annotations haven't been messed around with."
"But how do you know if the annotations are right? You have to run against a database to get them right."
"Well, what if you weren't using JPA, what if you were writing the SQL manually?  Would you right a unit test to connect to the a database and keep messing around with the SQL in your code until you got it right?  Of course not.  That would be insane!  Instead, what you would do is use a tool like SQL Developer, connect to the database, and work on the query until it runs correctly.  Then, after you've gotten the query correct, you'd copy and paste the query into your code.  You know the query works - you just ran it in SQL Developer - so no need to connect to a database from your unit test at all.  Your unit test only needs to assert that the code generates the query properly.  If you are using JPA, it's fundamentally the same thing.  The difference is with JPA you need to get the annotations correct.  So, do the JPA work somewhere else, then, when you got it correct, copy & paste it into your project and unit test the annotations."
"But where do you do this work?  Can SQL Developer help figure out JPA annotations?....Wait! I think Toad can.  Do we have more licenses for that?"
"Ugh!  No!  You create a JPA research project which starts a JPA implementation so you can play around with the JPA annotations.  In this research project, ideally you'd connect to the real project's development database, but you can actually connect to whatever database that has the data you need.  Doing all this work in a research project is actually much better for the real project because you get rid of the in-memory database from the real project and you also get rid of trying to replicate your project's real database in Derby.
"Where do we get a research project like this?"
"Umm, you just create one; Right-click -> Create -> New project." 
 "You mean everyone has to create their own research project?  Seems like a waste."
"Ugh!"
If you have had a conversation similar to this, please let me know.  I'd love to hear your stories. 

Example
But with this all being said, how do you unit test the annotations of you JPA objects.  Well it's not really that difficult.  The Java reflection API give access to a classes annotations.  So let's see what this might look like.

Suppose listing 1 is a Person object.  This Person object is part of your domain model and is setup to be handled by JPA to persist data to the database. 

Listing 1: Person and Phone Object Model
package org.thoth.jpa.UnitTesting;

import java.util.ArrayList;
import java.util.List;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;

/**
 * (JavaCodeGeeks, 2015)
 */
@Entity
@Table(name = "T_PERSON")
  public class Person {

  private Long id;
  private String firstName;
  private String lastName;
  private List<Phone> phones = new ArrayList<>();

  @Id
  @GeneratedValue()
  public Long getId() {
    return id;
  }

  public void setId(Long id) {
    this.id = id;
  }

  @Column(name = "FIRST_NAME")
  public String getFirstName() {
    return firstName;
  }

  public void setFirstName(String firstName) {
    this.firstName = firstName;
  }

  @Column(name = "LAST_NAME")
  public String getLastName() {
    return lastName;
  }

  public void setLastName(String lastName) {
    this.lastName = lastName;
  }

  @OneToMany(mappedBy = "person", fetch = FetchType.LAZY)
  public List<Phone> getPhones() {
    return phones;
  }
}

The code in listing 1 is just an example, so it's very simple.  In real applications, the domain objects and their relationships to other objects will get complex.  But this is enough for demonstration purposes.  Now, the next thing you want to do is unit test this object. Remember, the key words are unit test. You don't want to be starting any frameworks or databases.  It's the annotations and their properties which make the Person object work properly, so that's what you want to unit test.  Listing 2 shows what a unit test for the Person object may look like.

Listing 2: PersonTest Unit Test

package org.thoth.jpa.UnitTesting;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import org.junit.Assert;
import org.junit.Test;

/**
 * @author Michael Remijan mjremijan@yahoo.com @mjremijan
 */
public class PersonTest {
  @Test
  public void typeAnnotations() {
    // assert
    AssertAnnotations.assertType(
        Person.class, Entity.class, Table.class);
  }


  @Test
  public void fieldAnnotations() {
    // assert
    AssertAnnotations.assertField(Person.class, "id");
    AssertAnnotations.assertField(Person.class, "firstName");
    AssertAnnotations.assertField(Person.class, "lastName");
    AssertAnnotations.assertField(Person.class, "phones");
  }


  @Test
  public void methodAnnotations() {
    // assert
    AssertAnnotations.assertMethod(
        Person.class, "getId", Id.class, GeneratedValue.class);

    AssertAnnotations.assertMethod(
        Person.class, "getFirstName", Column.class);

    AssertAnnotations.assertMethod(
        Person.class, "getLastName", Column.class);

    AssertAnnotations.assertMethod(
        Person.class, "getPhones", OneToMany.class);
  }


  @Test
  public void entity() {
    // setup
    Entity a
    = ReflectTool.getClassAnnotation(Person.class, Entity.class);

    // assert
    Assert.assertEquals("", a.name());
  }


  @Test
  public void table() {
    // setup
    Table t
    = ReflectTool.getClassAnnotation(Person.class, Table.class);

    // assert
    Assert.assertEquals("T_PERSON", t.name());
  }


  @Test
  public void id() {
    // setup
    GeneratedValue a
    = ReflectTool.getMethodAnnotation(
        Person.class, "getId", GeneratedValue.class);

    // assert
    Assert.assertEquals("", a.generator());
    Assert.assertEquals(GenerationType.AUTO, a.strategy());
  }


  @Test
  public void firstName() {
    // setup
    Column c
    = ReflectTool.getMethodAnnotation(
        Person.class, "getFirstName", Column.class);

    // assert
    Assert.assertEquals("FIRST_NAME", c.name());
  }


  @Test
  public void lastName() {
    // setup
    Column c
    = ReflectTool.getMethodAnnotation(
        Person.class, "getLastName", Column.class);

    // assert
    Assert.assertEquals("LAST_NAME", c.name());
  }


  @Test
  public void phones() {
    // setup
    OneToMany a
    = ReflectTool.getMethodAnnotation(
        Person.class, "getPhones", OneToMany.class);

    // assert
    Assert.assertEquals("person", a.mappedBy());
    Assert.assertEquals(FetchType.LAZY, a.fetch());
  }
}

For this unit test, I created a couple simple helper classes: AssertAnnotations and ReflectTool since these can obviously be reused in other tests.  AssertAnnotations and ReflectTool are shown in listing 3 and 4 respectively.  But before moving on to these helper classes, let's look at PersonTest in more detail.

Line 19 is the #typeAnnotations method.  This method asserts the annotations on the Person class itself.  Line 21 calls the #assertType method and passes Person.class as the first parameter then after that the list of annotations expected on the class.  It's important to note the #assertType method will check that the annotations passed to it are the only annotations on the class. In this case, Person.class must only have the Entity and Table annotations.  If someone adds an  annotation or removes an annotation, #assertType will throw an AssertionError.

Line 27 is the #fieldAnnotations method. This method asserts the annotations on fields of the Person class.  Lines 29-32 call the #assertField method.  The first parameter is Person.class.  The second parameter is the name of the field.  But then after that something is missing; where is the list of annotations?  Well in this case there are no annotations!  None of the fields in this class are annotated.  By passing no annotations to the #assertField method, it will check to make sure the field has no annotations.  Of course if you JPA object uses annotations on the fields instead of the getter method, then you would put in the list of expected annotations.  It's important to note the #assertField method will check that the annotations passed to it are the only annotations on the field. If someone adds an annotation or removes an annotation, #assertField will throw an AssertionError.

Line 37 is the #methodAnnotations method.  This method asserts the annotations on the getter methods of the Person class. Lines 39-49 call the #assertMethod method.  The first parameter is Person.class.  The second parameter is the name of the getter method.  The remaining parameters are the expected annotations.  It's important to note the #assertMethod method will check that the annotations passed to it are the only annotations on the getter.  If someone adds an annotation or removes an annotation, #assertMethod will throw an AssertionError.  For example, on line 40, the "getId" method must only have the Id and GeneratedValue annotations and no others.

At this point PersonTest has asserted the annotations on the class, its fields, and its getter methods.  But, annotations have values too.  For example, line 17 of the Person class is @Table(name = "T_PERSON").  The name of the table is vitally important to the correct operation of this JPA object so the unit test must make sure to check it.

Line 64 is the #table method.  It uses the ReflectTool on Line 68 to get the Table annotation from the Person class.  Then line 71 asserts the name of the table is "T_PERSON".

The rest of the unit test method in PersonTest assert the values of the annotations in the Person class.  Line 83 asserts the GeneratedValue annotation has no generator and Line 84 asserts the generation type.  Lines 96 and 108 assert the names of the database table columns.  Lines 120-121 assert the relationship type between the Person object and the Phone object.

After looking at PersonTest in more detail, let's look at help classes: AssertAnnotations and ReflectTool.  I'm not going to say anything about these classes; they aren't all that complicated.

Listing 3: AssertAnnotations helper

package org.thoth.jpa.UnitTesting;

import java.lang.annotation.Annotation;
import java.util.Arrays;
import java.util.List;

/**
 * @author Michael Remijan mjremijan@yahoo.com @mjremijan
 */
public class AssertAnnotations {
  private static void assertAnnotations(
      List<Class> annotationClasses, List<Annotation> annotations) {
    // length
    if (annotationClasses.size() != annotations.size()) {
      throw new AssertionError(
        String.format("Expected %d annotations, but found %d"
          , annotationClasses.size(), annotations.size()
      ));
    }

    // exists
    annotationClasses.forEach(
      ac -> {
        long cnt
          = annotations.stream()
            .filter(a -> a.annotationType().isAssignableFrom(ac))
            .count();
        if (cnt == 0) {
          throw new AssertionError(
            String.format("No annotation of type %s found", ac.getName())
          );
        }
      }
    );
  }


  public static void assertType(Class c, Class... annotationClasses) {
    assertAnnotations(
        Arrays.asList(annotationClasses)
      , Arrays.asList(c.getAnnotations())
    );
  }


  public static void assertField(
      Class c, String fieldName, Class... annotationClasses) {
    try {
      assertAnnotations(
        Arrays.asList(annotationClasses)
        , Arrays.asList(c.getDeclaredField(fieldName).getAnnotations())
      );
    } catch (NoSuchFieldException nsfe) {
      throw new AssertionError(nsfe);
    }
  }


  public static void assertMethod(
      Class c, String getterName, Class...annotationClasses) {
    try {
      assertAnnotations(
        Arrays.asList(annotationClasses)
        , Arrays.asList(c.getDeclaredMethod(getterName).getAnnotations())
      );
    } catch (NoSuchMethodException nsfe) {
      throw new AssertionError(nsfe);
    }
  }
}

Listing 4: ReflectTool helper

package org.thoth.jpa.UnitTesting;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

/**
 * @author Michael Remijan mjremijan@yahoo.com @mjremijan
 */
public class ReflectTool {
  public static <T extends Annotation> T getMethodAnnotation(
      Class<?> c, String methodName, Class<T> annotation) {
    try {
      Method m = c.getDeclaredMethod(methodName);
      return (T)m.getAnnotation(annotation);
    } catch (NoSuchMethodException nsme) {
      throw new RuntimeException(nsme);
    }
  }

  public static <T extends Annotation> T getFieldAnnotation(
      Class<?> c, String fieldName, Class<T> annotation) {
    try {
      Field f = c.getDeclaredField(fieldName);
      return (T)f.getAnnotation(annotation);
    } catch (NoSuchFieldException nsme) {
      throw new RuntimeException(nsme);
    }
  }

  public static <T extends Annotation> T getClassAnnotation(
      Class<?> c, Class<T> annotation) {
    return (T) c.getAnnotation(annotation);
  }
}

That's it.  I hope this is helpful.

References
https://www.javacodegeeks.com/2015/02/jpa-tutorial.html#relationships_onetomany

June 17, 2016

Java EE Guardians moving EE 8 forward at JavaOne 2016 San Francisco

The Java EE Guardians is a group of individuals and organizations from all around the world who stand in support of an open standard for Java enterprise development and are committed to moving Java EE forward.  The group started the first quarter of 2016 in response to Oracle's apparent lack of commitment to Java EE 8.  On the Java EE Guardians website, http://javaee-guardians.io, the group has published evidence showing that the activity of EE 8 JSRs lead by Oracle have slowed significantly (Lack of.., 2016).
Sadly, this lack of commitment to Java EE 8 seems to extend to JavaOne 2016 in San Francisco.  The Java EE Guardians have attempted to contact the EE 8 JSRs specification leads about their intentions to present at JavaOne on the progress of their JSRs.  They have either responded in silence or by saying no.  So the Java EE Guardians have rallied the community to make sure EE 8 has a good representation at JavaOne.  The community actions are summarized in the table below, showing the community members who have stepped up and submitted proposals to JavaOne to ensure the JSRs are represented.
If the current situation remains unchanged, it will be the first time active JSR specification leads from the Java steward will not be presenting their progress to the community in such high numbers; an unprecedented, never-before-seen situation in JavaOne history.  We sincerely hope that most, if not all, of the session proposals put forth by the community are accepted if Oracle specification leads do not step up in time.  It is critical for all the people attending JavaOne to know that standards-based Java enterprise development is moving forward.
Table 1: Java EE 8 JSR JavaOne 2016 Session Proposals
Description
Java EE 8 JSR
Version
Specification Lead
JavaOne 2016 Session Proposal
Java™ Platform, Enterprise Edition Specification
8
Linda Demichiel (Oracle)
William Shannon (Oracle)
Java EE 8 Community Panel
  • Reza Rahman
  • Werner Keil (JCP Executive Committee)
  • Michael Remijan
  • Mark Little (Red Hat)
  • Kevin Sutter (IBM)
  • Oracle contacted to participate, no response so far
Aligning Java EE 8 with Java SE 8 - The Missing Links
  • Reza Rahman
  • Ryan Cuprak
  • Michael Remijan
Java EE 8 Recipes
  • Josh Juneau
Rapid Development Tools for Java EE 8
  • Gaurav Gupta
Java™ Servlet Specification
4.0
Edward Burns (Oracle)
Shing Wai Chan (Oracle)
What’s Coming in Servlet 4
  • Murat Yener
  • Alex Theedom
Java™ Message Service (JMS)
2.1
Nigel Deakin (Oracle)
What’s new with JMS 2.1
  • Ivar Grimstad
JMS BOF
  • Ivar Grimstad
JAX-RS: The JavaTM API for RESTful Web Services
2.1
Santiago Pericasgeertsen (Oracle)
Marek Potociar (Oracle)
JAX-RS 2.1
  • Sebastian Daschner
JavaServer Faces (JSF)
2.3
Edward Burns (Oracle)
Manfred Riem (Oracle)
JSF or MVC, What do I Use?
  • Josh Juneau
JSF 2.3 in Action
  • Kito Mann
Model-View-Controller (MVC)
**NEW**
1.0
Santiago Pericasgeertsen (Oracle)
Manfred Riem (Oracle)
What's new with MVC 1.0?
  • Ivar Grimstad
Modern Web Apps with HTML5 Web Components, Polymer, Java EE MVC 1.0 and JAX-RS
  • Kito Mann
JSF or MVC, What do I Use?
  • Josh Juneau
Java Persistence (JPA)
2.1 MR
Linda Demichiel (Oracle)
Lukas Jungmann (Oracle)
What's Next for JPA? (BOF)
  • Patrycja Wegrzynowicz
  • Michael Remijan
Contexts and Dependency Injection (CDI)
2.0
Antoine Sabot-Durand (Red Hat)
CDI 2.0 in live coding
  • Antoine Sabot-Durand
Micro services and more with CDI on Java SE
  • Antoine Sabot-Durand
Mutate Java EE 7 DNA with CDI portable extensions
  • Antoine Sabot-Durand
Java API for JSON Processing (JSON-P)
1.1
Kinman Chung (Oracle)
What's New in JSON-P 1.1?
  • Werner Keil
Java API for JSON Binding (JSON-B)
**NEW*
1.0
Dmitry Kornilov (Oracle)
JSON-B 1.0
  • Dmitry Kornilov (has submitted)
Java™ EE Security API
Alexander Kosowski (Oracle)
What's new with Java EE Security?
  • Ivar Grimstad
  • Werner Keil
Concurrency Utilities for Java EE 1.0
No change from EE 7
Liberating EJB Concurrency Features for the Community
  • Reza Rahman
JavaOne, and similar conferences, are a critical part in keeping Java strong (Krill, 2015).  They help showcase new technologies and the direction of the industry.  They also allow the community to voice how the trends of today will become the standards of tomorrow.  This has been especially true of EE 7, which brought to the community standards for developing with: HTML5, WebSockets, JSON, Messaging, Batch, Concurrency, Dependency Injection, RESTful Web Services, and non-blocking I/O (Krill, 2013).  EE 7 was released in 2013 (JSR 342, 2011) and it has had a lot to celebrate over the last few years.   It has been well supported by the major application server providers and well adopted by the community (Rahman, 2015).  Modern EE 7 servers are lightweight, fast, and quickly evolving to support architectural changes in the industry (Daschner, 2016; JAX Editorial Team, 2016).  EE 8 promises to bring an MVC standard, JSON binding, more support for HTML 5 and HTTP 2, better CDI integration, and more (Gupta, n.d.).  But this won't happen without community involvement organized by strong leadership from Oracle.    
Visit the Java EE Guardians website, http://javaee-guardians.io, or its Google group, https://groups.google.com/forum/#!forum/javaee-guardians, and add your voice in support of Java EE.
References
JSR 366: Java Platform, Enterprise Edition 8 (Java EE 8) Specification. (2014, August 26).  jcp.org. Retrieved June 1, 2016, from https://jcp.org/en/jsr/detail?id=366
JSR 342: JavaTM Platform, Enterprise Edition 7 (Java EE 7) Specification. (2011, March 01).  jcp.org. Retrieved June 1, 2016, from https://www.jcp.org/en/jsr/detail?id=342
Rahman, R. (2015, June, 08). The Ghosts of Java EE 7 in Production: Past, Present and Future.  blogs.oracle.com. Retrieved June 1, 2016, from https://blogs.oracle.com/reza/entry/the_ghosts_of_java_ee
Lack of Java EE 8 Progress. (2016, May). javaee-guardians.io. Retrieved May 31, 2016 from http://javaee-guardians.io/lack-of-java-ee-8-progress/
Krill, P. (2015, October, 23). Java developers carry hopes and fears to JavaOne. infoworld.com. Retrieved June 2, 2016 from http://www.infoworld.com/article/2996549/java/java-developers-carry-hopes-fears-to-javaone.html
Krill, P. (2013, June, 13). 11 hot improvements to Java EE 7. infoworld.com. Retrieved June 2, 2016 from http://www.infoworld.com/article/2606994/java/105268-11-hot-improvements-to-Java-EE-7.html#slide13
Daschner, S. (2016, April, 9). Stop Saying "heavyweight". blog.sebastian-daschner.com. Retrieved April 11, 2016 from https://blog.sebastian-daschner.com/entries/stop_saying_heavyweight
JAX Editorial Team. (2016, May, 19). “Java EE’s heavyweight label is just mythology”. jaxenter.com. Retrieved May 20, 2016 from https://jaxenter.com/java-ees-heavyweight-label-is-just-mythology-126363.html
Gupta, A. (n.d.). Java EE 8 Status. blog.arungupta.me. Retrieved June 1, 2016 from http://blog.arungupta.me/javaee8