Testing Java Code in 2013

Beyond JUnit w/ Hamcrest, Mockito, Spring, and Spock!
{by Jeff Sheets}

These slides are up on GitHub!


About Me

Objectives Assuming...

  • Spring MVC + JPA/Hibernate
  • Using Maven {}
  • Using Eclipse/STS/GGTS or RAD { }
  • We want tools newer than JUnit 3
  • Possibly use Groovy for tests (but not prod code)
  • Please ask questions!
  • Everyone in this room is an expert!

Why test your code?

  • Developers -> Code
  • Senior Developers -> Code and Test
  • Chuck Norris -> continuously deploys to prod with 100% test coverage -- in COBOL  

Really why test?

  • Obvious (you wouldn't be in this room otherwise)
  • Molds your testable coding style
  • Faster than redeploy-test-repeat
  • Coding with confidence
  • Continuous Deployment  

Keep It Simple

  • Do all of your tests pass?
  • Jenkins Server (in the cloud!)
  • Test Independence
  • Not too brittle

Sample App

Unit vs Integration vs Functional

  • unit: JUnit+Mockito or Spock
  • int: Spring Transactional and Wiring
  • func: Geb, Selenium, Cloud...
  • Others???
  • How to separate the types?

Separating Testing types - by Dir

  • src/java/main
  • src/java/test
  • src/java/integration ???
  • src/groovy/test ---> for spock
  • src/groovy/integration
  • Maven makes this very difficult!

Separating Testing types - by File

  • MyServiceTest.java
  • MyServiceIT.java //Maven failsafe plugin pattern
  • MyServiceSpec.groovy //Spock
  • MyServiceSpecIT.groovy //Spock + failsafe
  • Failsafe setup in Pom.xml

Traditional JUnit


//Setup your Eclipse Favorites for code suggestions
import static org.junit.Assert.assertEquals;

/* ... */

assertEquals(2013, result.getYear().intValue());
assertEquals("JunitMotors", result.getMake().getName());
assertEquals(make.getId(), result.getMake().getId());
assertEquals(car.getDescription(), result.getDescription());



import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;

/* ... */

assertThat(result.getYear().intValue(), is(equalTo(2013)));
assertThat(result.getMake().getName(), is("JunitMotors"));
assertThat(result.getMake().getId(), is(make.getId()));
assertThat(result.getDescription(), is(car.getDescription()));


Hamcrest Details

  • Readable assertions (actual before expected)
  • Hamcrest Docs
  • ProviderRepositoryIT.java

assertThat(result, not(nullValue()));
assertThat(result, hasSize(original.size() + 1));

//This requires overridden provider.equals method
assertThat(result, hasItem(provider));

Custom Matchers

  • Implement Matcher interface
  • matches() and describeTo() methods
  • Use static methods for readability
  • CustomMatchers.java and PropertiesIT.java

import static com.sheetsj.test.matchers.CustomMatchers.isStringInjected;

/* ... */

assertThat(anotherProperty, isStringInjected());


import static org.fest.assertions.Assertions.assertThat;

/* ... */

assertThat(hasAuthority(userDetails, demoUser.getRole()));

Checking Thrown Exceptions

JUnit 4 actually provides a BAD way

//Don't copy this!
public void testIndexOutOfBoundsException() {
    Object a = service.getAllResults().get(2);
    Object b = service.getAllForA(a).get(2);

The Better Way

Use ExpectedException @Rule (UserServiceTest.java)

public ExpectedException thrown = ExpectedException.none();

public void shouldThrowExceptionWhenUserNotFound() {
  thrown.expectMessage("not found");  //NOTE: something funny here

The Custom Way

  • BusinessValidationExceptionMatchers.java
  • Custom Hamcrest matchers for sourceField and arguments

public static void expect(String message, ExpectedException thrown)

/** From UserServiceTest.java */
public void shouldThrowBusinessExceptionWhenUsernameTooShort() {
  //Uses custom expect matcher using equalTo() instead of containsString()
  expect("username", "username.too.short", thrown, "ab");

Mockito Annotations

  • We've all used Mockito? JMock? Others?
  • Why? To isolate our Unit Tests!
  • Mock method return values
  • Verify mock methods were called
  • Spy to mock method on class-under-test

The Old Mockito Way


private WorkItemRepository workItemRepository = mock(WorkItemRepository.class);
private WorkItemService service = new WorkItemService(workItemRepository);

/* .. inside a test method .. */

The New Mockito Way


public class WorkItemServiceTest {
  private WorkItemRepository workItemRepository;
  private WorkItemService service = new WorkItemService();

Those Double-Crossing Spies

  • Spies are a code smell
  • But sometimes it is just easier (legacy)

private WorkItemService service = new WorkItemService();

/* ... in test method ... */
     .selectWorkItemsByMakeAndYear("bmw", "2008", allWorkItems);
Collection<WorkItem> results = service.findAllByMakeAndYear("bmw", "2008");

//Showing nested matchers in contains and sameInstance
assertThat(results, contains(sameInstance(expected)));

Spring Integration Tests

  • Are you testing your ORM/SQL?
  • {Stored Procs? Sorry!}
  • What about Spring Wiring?
  • {Especially any injected properties}

Transactional Tests

Auto-rollback from real database!

             classes={RootConfig.class, PersistenceConfig.class})
public abstract class IntegrationTestBaseClass extends 
                  AbstractTransactionalJUnit4SpringContextTests {

Transactional Tests 2

Test now talks to real DB

public class WorkItemRepositoryIT extends IntegrationTestBaseClass {
  List<WorkItem> original = workItemRepository.findAll();
  Manufacturer make = manufacturerRepository.save(new Manufacturer("JUnitMake"));
  Car car = carRepository.save(new Car(2013, make, "JunitModel", "LT FWD 3.6L V6 DOHC 24V"));
  Provider provider = providerRepository.save(new Provider("Junit Tire Shop", "Shadow Lake"));
  WorkItem workItem = new WorkItem(car, new Date(), "Oil Change", provider, 18123L, 60.12, "Took 1:45 so got free oil change coupon");
  workItem = workItemRepository.save(workItem);
  List<WorkItem> result = workItemRepository.findAll();

Mad Props

  • Integration Test our Properties too
  • Especially if coming from database table
  • Two styles: 1) All props in PropertiesIT.java

public class PropertiesIT extends IntegrationTestBaseClass {

  private String anotherProperty;
  public void testAnotherProperty() {
    assertThat(anotherProperty, isStringInjected());

Mad Props 2

  • 2) Test on injected class
  • {Better but needs protected getter}

public class UserServiceIT extends IntegrationTestBaseClass {
  private UserService userService;
  public void testPropertyIsInjected() {
    assertThat(userService.getAnotherProperty(), isStringInjected());

Spock Testing Framework

  • "The Enterprise Ready Specification Framework"
  • Yes, Spock is Groovy
  • But tests Java classes fine (or better)
  • Read the Intro Docs
  • IANASE (I Am Not A Spock Expert)

Spock Setup


Spock Setup 2

  • 2) Build Path for src/test/groovy
  • 3) Output Folder for src/test/groovy
  • 4) Profit???

Spock Setup Gotcha's


Spock Test Code

Basic Layout

class WorkItemServiceSpecTest extends Specification {
  def service = new WorkItemService()
  def "getYearAsString works for simple case"() {
    given: 'a normal date'
    def date = Date.parse("MM/dd/yyyy", "08/01/2013")
    when: 'getYearAsString is called'
    def result = service.getYearAsString(date)
    then: 'year is correct on result'
    result == "2013"

Expect: for functional code

def "getYearAsString works for simple case using expect block"() {
  given: 'a normal date'
  def date = Date.parse("MM/dd/yyyy", "08/01/2013")
  expect: 'getYearAsString is correct when called'
  service.getYearAsString(date) == "2013"

Spock Mocks Setup

class WorkItemServiceSpecTest extends Specification {
  def service = new WorkItemService()
  def workItemRepository = Mock(WorkItemRepository)
  def setup() {
    service.workItemRepository = workItemRepository

Spock Mocks - Simple

def "findAllByMakeAndYear works with mocks"() {
  given: 'a bunch of work items'
  List<WorkItem> allWorkItems = buildAllWorkItems()
  when: 'findAllByMakeAndYear is called'
  def results = service.findAllByMakeAndYear('ford', '2013')
  then: 'results are correct'
  1 * workItemRepository.findAll() >> allWorkItems
  results.containsAll allWorkItems[2,4]

Spock Mocks - Wildcard _

/** not a good pattern. just showing it is possible */
def "findAllByMakeAndYear with many tests at once"() {
  given: 'a bunch of work items'
  List<WorkItem> allWorkItems = buildAllWorkItems()
  _ * workItemRepository.findAll() >> allWorkItems
  expect: 'findAllByMakeAndYear is called and has correct results'
  service.findAllByMakeAndYear('ford', '2013').containsAll(
  service.findAllByMakeAndYear('ford', '2012').containsAll(
  service.findAllByMakeAndYear('chevy', '2013').empty
  !service.findAllByMakeAndYear('ford', '2014')

Spock - Multiple when/then

def "findAllByMakeAndYear with many tests at once but multiple blocks"() {
  given: 'a bunch of work items'
  List allWorkItems = buildAllWorkItems()
  when: 'findAllByMakeAndYear is called for Ford and 2013'
  def results = service.findAllByMakeAndYear('ford', '2013')
  then: 'results are correct'
  1 * workItemRepository.findAll() >> allWorkItems
  results.containsAll allWorkItems[2,4]
  when: 'findAllByMakeAndYear is called for Ford and 2012'
  results = service.findAllByMakeAndYear('ford', '2012')
  then: 'results are correct'
  1 * workItemRepository.findAll() >> allWorkItems
  results.containsAll allWorkItems[0..1]

Spock @Unroll Magic FTW!

  • Wow. Killer Feature!    
  • Parameterized Tests (anyone doing JUnit style?)

def "getYearAsString with #inputDate returns #result"
                                (String inputDate, String result) {
expect: 'getYearAsString is correct when called'
              Date.parse("MM/dd/yyyy", inputDate)) == result
  inputDate    | result
  "01/01/1999" | "1999"
  "02/01/2000" | "2000"
  "03/01/199"  | "199"
  '03/01/2013' | '2013'	

Spock old() method

  • "Wait, WTF is going on there? That’s voodoo!" -- Rob Fletcher

def "findAll workItems and Save showing how the old method works"() {
  when: 'workItem saved, and results retrieved'
  WorkItem workItem = new WorkItem(car, new Date(), "Oil Change", provider, 18123L, 60.12, "Took 1:45 so got free oil change coupon");
  workItem = workItemRepository.save(workItem);
  List<WorkItem> result = workItemRepository.findAll();
  then: 'size was incremented'
  result.size() == old(workItemRepository.findAll()).size() + 1

Spock Wires into Spring

  • Needs spock-spring plugin
  • Just use @Autowired
  • Setup Transaction rollbacks
  • WorkItemRepositorySpecIT.groovy

                      classes=[RootConfig, PersistenceConfig])
abstract class IntegrationSpecBaseClass extends Specification {

Spock + Hamcrest?

  Yes it can be done (that() and expect())

  expect: 'findAllByMakeAndYear returns correct results'
  that result1, containsInAnyOrder(allWorkItems[2], allWorkItems[4])
  that result2, containsInAnyOrder(allWorkItems[0], allWorkItems[1])
  that result3, empty()
  that result4, empty()

Spock + Hamcrest? 2

  But groovy often easier

expect: 'findAllByMakeAndYear returns correct results'
result1.containsAll allWorkItems[2,4]

Spock Miscellaneous

  • thrown() and notThrown()
  • setup() and cleanup()
  • setupSpec() and cleanupSpec()
  • @Ignore, @IgnoreRest, @Timeout, @FailsWith
  • where: using simple arrays instead of tables
  • SpockBasics

Spock vs JUnit+Mockito+Hamcrest

  • Spock gives you power of Groovy
  • I prefer readability of Spock Specs
  • Opinions? Thoughts? Spock Questions?

Testing REST Clients

public void testGetMessage() {
    .andRespond(withSuccess("resultSuccess", MediaType.TEXT_PLAIN));
  String result = simpleRestService.getMessage();
  assertThat(result, allOf(containsString("SUCCESS"), 

Testing Mail Senders

  • MockMailSender for Spring JavaMailSender
  • Mocks it out so no mail is sent
  • Other tools spinning up real mail servers
  • Described well in this Blog Post
  • See MockMailSender.java




Java Trends in 2013

  • Cloud PaaS (Heroku & Cloudfoundry/Appfog)
  • Continuous Deployment (Cloudbees & drone.io)
  • Online IDEs (codenvy and cloud9)
  • Dropwizard / Spring Boot / No-Servlet?
  • Akka async promises in Java/Scala
  • Java 8 Functional Style (next year)