Thursday, 4 April 2013

Mocking Static Methods in Groovy

Using Groovy to test not only other Groovy classes but also Java classes is an increasing popular approach given frameworks like Spock which facilitate Test Driven and Behaviour Driven Development.

A common problem when testing though is having to deal with legacy code and more often than not having to mock static methods calls. When writing tests in Groovy, the approach to mocking static calls will depend on the type of class you're testing and what type of class has the static method in.

If the Groovy class you're testing makes calls a static method on another Groovy class, then you could use the ExpandoMetaClass which allows you to dynamically add methods, constructors, properties and static methods. The below Account Groovy class has a static method called getType(). The Customer Groovy class you're trying to test calls the getType() static method within it's getAccountType() method:


class Customer {

 String getAccountType() {
  return Account.getType();
 }
 
}

class Account {

 static String getType() {
  return "Personal";
 }

}

class CustomerTest extends GroovyTestCase {
 
 void testGetAccountType() {
  
  Customer cust = new Customer()
  assert cust.getAccountType() == "Personal"

  Account.metaClass.static.getType = {return "Business"}
  
  assert cust.getAccountType() == "Business"
    
 }

}

The CustomerTest Groovy class shows how the getType static method can be overridden using the metaClass property. This isn't strictly mocking, more dynamically changing a class. An alternative approach is to use Spock's GroovyMock to achieve a similar result. (A more detailed description of Spock's mocking and stubbing features can be found here:)


import spock.lang.*

class CustomerTest extends Specification {
 
 def "get account type"() {
   
  setup: 
   GroovyMock(Account, global: true)
   Account.getType() >> "Business"
   Customer cust = new Customer()
  
  when: "obtaining a customer's account type"
   def type = cust.getAccountType()

  then: "the type will be a Business account"
   type == "Business"
    
 }

}

If the Account class is a Java not Groovy class then we can still mock it out using the above methods. The problem with mocking Java static methods though, when using Groovy for testing, is when they are called from Java classes and these calling classes are the ones you are trying to test.

For example, if we now have a Customer Java class and also an Account Java class, and a CustomerTest Groovy class, the ExpandoMetaClass and GroovyMock approaches will not work globally but will work locally. The below CustomerTest will pass.


public class Customer {

 public String getAccountType() {
  return Account.getType();
 }
 
}

public class Account {

 public static String getType() {
  return "Personal";
 }

}
class CustomerTest extends GroovyTestCase {
 
 void testGetAccountType() {
  
  Customer cust = new Customer()
  assert cust.getAccountType() == "Personal"

  Account.metaClass.static.getType = {return "Business"}
  
  assert Account.getType() == "Business"
  assert cust.getAccountType() == "Personal"
    
 }

}

The way around this is to use a mocking framework like Powermock or JMockit. The below amended CustomerTest Groovy class shows how Powermock can be integrated into a GroovyTestCase:


import groovy.mock.interceptor.*
import org.powermock.api.mockito.PowerMockito;
import static org.mockito.Mockito.*;
import org.powermock.core.classloader.annotations.PrepareForTest
import org.junit.runner.RunWith;
import org.powermock.modules.junit4.PowerMockRunner

@RunWith( PowerMockRunner.class )
@PrepareForTest(Account.class)
class CustomerTest extends GroovyTestCase {
 
 public void testGetAccountType() { 
  
  Customer cust = new Customer()
  assert cust.getAccountType() == "Personal"

  PowerMockito.mockStatic(Account.class);
  when(Account.getType()).thenReturn("Business");
    
  assert cust.getAccountType() == "Business"
 
 }

}
Using Mockito's when to mock the return value of the static getType() call enables "Business" to be returned instead of "Personal". This example shows how when writing Groovy tests to test Java classes, using a framework like Powermock can fill the void of mocking Java static methods when called from the Java classes you are trying to test.