Mocking Static Methods in Groovy
Join the DZone community and get the full member experience.
Join For FreeUsing 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.
Published at DZone with permission of Geraint Jones, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments