Chariot Training Classes

Training Courses

I run Chariot's training and mentoring services. We provide training in AngularJS, HTML5, Spring, Hibernate, Maven, Scala, and more.

Chariot Education Services

Technology

Chariot Emerging Tech

Learn about upcoming technologies and trends from my colleagues at Chariot Solutions.

Resources

Chariot Conferences

Podcasts

« Maven Search Site Online | Main | RailsConf 2011 - My Personal Recap »
Sunday
Jul172011

Testing Entity Validations with a Mock Entity - Roo in Action Corner

The following post is ancillary material from the upcoming book Spring Roo in Action, by Ken Rimple and Srini Penchikala, with Gordon Dickens. You can purchase the MEAP edition of the book, and participate in the author forum, at www.manning.com/rimple.

In Spring Roo in Action, Chapter 3, I discuss how Roo automatically executes the Bean Validators when persisting a live entity. However, when running unit tests, we don't have a live entity at all, nor do we have a Spring container - so how can we exercise the validation without actually hitting our Roo application and the database?

The answer is that we have to bootstrap the validation framework within the test ourselves. We can use the CourseDataOnDemand class's getNewTransientEntityName method to generate a valid, transient JPA entity. Then, we can:

  1. Mock static entity methods, such as findById, to bring back pre-fabricated class instances of your entity
  2. Initialize the validation engine, bootstrapping a JSR-303 bean validation framework engine, and perform validation on your entity
  3. Set any appropriate properties to apply to a particular test condition
  4. Initialize a test instance of the entity validator and assert the appropriate validation results are returned

The concept in action...

Given a Student entity with the following definition:


@RooEntity
@RooJavaBean
@RooToString
public class Student {
  
  @NotNull
  private String emergencyContactInfo;

  ...
}

The listing below shows a unit test method that ensures the NotNull validation fires against missing emergency contact information on the Student entity:

@Test
public void testStudentMissingEmergencyContactValidation() {
  // setup our test data
  StudentDataOnDemand dod = new StudentDataOnDemand();

  // tell the mock to expect this call 
  Student.findStudent(1L); 
  // tell the mocking API to expect a return from the prior call in the form of
  // a new student from the test data generator, dod
  AnnotationDrivenStaticEntityMockingControl.expectReturn(
     dod.getNewTransientStudent(0)); 

  // put our mock in playback mode
  AnnotationDrivenStaticEntityMockingControl.playback(); 

  // Setup the validator API in our unit test
  LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean();
  validator.afterPropertiesSet(); 

  // execute the call from the mock, set the emergency contact field
  // to an invalid value 
  Student student = Student.findStudent(1L);
  student.setEmergencyContactInfo(null);
  
  // execute validation, check for violations
  Set<ConstraintViolation<Student>> violations = 
    validator.validate(student, Default.class);

  // do we have one?
  Assert.assertEquals(1, violations.size());

  // now, check the constraint violations to check for our specific error
  ConstraintViolation<Student> violation = violations.iterator().next();
  
  // contains the right message?
  Assert.assertEquals("{javax.validation.constraints.NotNull.message}", 
    violation.getMessageTemplate());
   
  // from the right field?
  Assert.assertEquals("emergencyContactInfo", 
    violation.getPropertyPath().toString());
}

Analysis

The test starts with a declaration of a StudentOnDemand object, which we'll use to generate our test data. We'll get into the more advanced uses of the DataOnDemand Framework later in the chapter. For now, keep in mind that we can use this class to create an instance of an Entity, with randomly assigned, valid data. We then require that the test calls the Student.findStudent method, passing it a key of 1L. Next, we'll tell the entity mocking framework that the call should return a new transient Student instance. At this point, we've defined our static mocking behavior, so we'll put the mocking framework into playback mode.

Next, we issue the actual Student.findById(1L) call, this time storing the result as the member variable student. This call will trip the mock, which will return a new transient instance. We then set the emergencyContactInfo field to null, so that it becomes invalid, as it is annotated with a @NotNull annotation. Now we are ready to set up our bean validation framework.

We create a LocalValidatorFactoryBean instance, which will boot the Bean Validation Framework in the afterPropertiesSet() method, which is defined for any Spring Bean implementing InitializingBean. We must call this method ourselves, because Spring is not involved in our unit test. Now we're ready to run our validation and assert the proper behavior has occurred.

We call our validator's validate method, passing it the student instance and the standard Default validation group, which will trigger validation. We'll then check that we only have one validation failure, and that the message template for the error is the same as the one for the @NotNull validation. We also check to ensure that the field that caused the validation was our emergencyContactInfo field.

In our answer callback, we can launch the Bean Validation Framework, and execute the validate method against our entity. In this way, we can exercise our bean instance any way we want, and instead of persisting the entity, can perform the validation phase and exit gracefully.

Caveats...

There are a few things slightly wrong here. First of all, the Data on Demand classes actually use Spring to inject relationships to each other, which I've logged a bug against as ROO-2497. You can override the setup of the data on demand class and manually create the DoD of the referring one, which is fine. They have slated to work on this bug for Roo 1.2, so it should be fixed sometime in the next few months.

Also, realize that this is NOT easy to do, compared to writing an integration test. However, this test runs markedly faster. If you have some sophisticated logic that you've attached to a

@AssertTrue
annotation, this is the way to test it in isolation.

About this post

Did you find this post useful? Ken and Gordon both teach courses in Spring, Hibernate, Integration, Maven, Rails and more for Chariot Solutions. Visit our Education Services page for details on upcoming courses, including August's Hibernate with Spring.

PrintView Printer Friendly Version

EmailEmail Article to Friend

Reader Comments

There are no comments for this journal entry. To create a new comment, use the form below.

PostPost a New Comment

Enter your information below to add a new comment.

My response is on my own website »
Author Email (optional):
Author URL (optional):
Post:
 
Some HTML allowed: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <code> <em> <i> <strike> <strong>