Mock static method with GroovyMock or similar in Spock - unit-testing

First-timer here, apologies if I've missed anything.
I'm hoping to get around a call to a static method using Spock. Feedback would be great
With groovy mocks, I thought I'd be able to get past the static call but haven't found it.
For background, I'm in the process of retrofitting tests in legacy java. Refactoring is prohibited. I'm using spock-0.7 with groovy-1.8.
The call to the static method is chained with an instance call in this form:
public class ClassUnderTest{
public void methodUnderTest(Parameter param){
//everything else commented out
Thing someThing = ClassWithStatic.staticMethodThatReturnsAnInstance().instanceMethod(param);
}
}
staticMethod returns an instance of ClassWithStatic
instanceMethod returns the Thing needed in the rest of the method
If I directly exercise the global mock, it returns the mocked instance ok:
def exerciseTheStaticMock(){
given:
def globalMock = GroovyMock(ClassWithStatic,global: true)
def instanceMock = Mock(ClassWithStatic)
when:
println(ClassWithStatic.staticMethodThatReturnsAnInstance().instanceMethod(testParam))
then:
interaction{
1 * ClassWithStatic.staticMethodThatReturnsAnInstance() >> instanceMock
1 * instanceMock.instanceMethod(_) >> returnThing
}
}
But if I run the methodUnderTest from the ClassUnderTest:
def failingAttemptToGetPastStatic(){
given:
def globalMock = GroovyMock(ClassWithStatic,global: true)
def instanceMock = Mock(ClassWithStatic)
ClassUnderTest myClassUnderTest = new ClassUnderTest()
when:
myClassUnderTest.methodUnderTest(testParam)
then:
interaction{
1 * ClassWithStatic.staticMethodThatReturnsAnInstance() >> instanceMock
1 * instanceMock.instanceMethod(_) >> returnThing
}
}
It throws down a real instance of ClassWithStatic that goes on to fail in its instanceMethod.

Spock can only mock static methods implemented in Groovy. For mocking static methods implemented in Java, you'll need to use a tool like GroovyMock , PowerMock or JMockit.
PS: Given that these tools pull of some deep tricks in order to achieve their goals, I'd be interested to hear if and how well they work together with tests implemented in Groovy/Spock (rather than Java/JUnit).

Here is how I solved my similar issue (mocking a static method call which is being called from another static class) with Spock (v1.0) and PowerMock (v1.6.4)
import org.junit.Rule
import org.powermock.core.classloader.annotations.PowerMockIgnore
import org.powermock.core.classloader.annotations.PrepareForTest
import org.powermock.modules.junit4.rule.PowerMockRule
import spock.lang.Specification
import static org.powermock.api.mockito.PowerMockito.mockStatic
import static org.powermock.api.mockito.PowerMockito.when
#PrepareForTest([YourStaticClass.class])
#PowerMockIgnore(["javax.xml.*", "ch.qos.logback.*", "org.slf4j.*"])
class YourSpockSpec extends Specification {
#Rule
Powermocked powermocked = new Powermocked();
def "something something something something"() {
mockStatic(YourStaticClass.class)
when: 'something something'
def mocked = Mock(YourClass)
mocked.someMethod(_) >> "return me"
when(YourStaticClass.someStaticMethod(xyz)).thenReturn(mocked)
then: 'expect something'
YourStaticClass.someStaticMethod(xyz).someMethod(abc) == "return me"
}
}
The #PowerMockIgnore annotation is optional, only use it if there is some conflicts with existing libraries

A workaround would be to wrap the static method call into an instance method.
class BeingTested {
public void methodA() {
...
// was:
// OtherClass.staticMethod();
// replaced with:
wrapperMethod();
...
}
// add a wrapper method for testing purpose
void wrapperMethod() {
OtherClass.staticMethod();
}
}
Now you can use a Spy to mock out the static method.
class BeingTestedSpec extends Specification {
#Subject BeingTested object = new BeingTested()
def "test static method"() {
given: "a spy into the object"
def spyObject = Spy(object)
when: "methodA is called"
spyObject.methodA()
then: "the static method wrapper is called"
1 * spyObject.wrapperMethod() >> {}
}
}
You can also stub in canned response for the wrapper method if it's supposed to return a value. This solution uses only Spock built-in functions and works with both Java and Groovy classes without any dependencies on PowerMock or GroovyMock.

The way I've gotten around static methods in Groovy/Spock is by creating proxy classes that are substituted out in the actual code. These proxy classes simply return the static method that you need. You would just pass in the proxy classes to the constructor of the class you're testing.
Thus, when you write your tests, you'd reach out to the proxy class (that will then return the static method) and you should be able to test that way.

I have recently found 'spock.mockfree' package, it helps mocking final classes and static classes/methods.
It is quite simple as with this framework, in this case, you would need only to Spy() the class under test and #MockStatic the static method you need.
Example:
We used a static method returnA of StaticMethodClass class
public class StaticMethodClass {
public static String returnA() {
return "A";
}
}
here is the calling code
public class CallStaticMethodClass {
public String useStatic() {
return StaticMethodClass.returnA();
}
}
Now we need to test the useStatic method of CallStaticMethodClass class But spock itself does not support mock static methods, and we support
class CallStaticMethodClassTest extends Specification {
def 'call static method is mocked method'() {
given:
CallStaticMethodClass callStaticMethodClass = Spy()
println("useStatic")
expect:
callStaticMethodClass.useStatic() == 'M'
}
#MockStatic(StaticMethodClass)
public static String returnA() {
return "M";
}
}
We use the #MockStatic annotation to mark which class needs to be mocked
Directly implement the static method that requires mocking under it, the method signature remains the same, but the implementation is different.
Link to the framework:
https://github.com/sayweee/spock-mockfree/blob/498e09dc95f841c4061fa8224fcaccfc53904c67/README.md

Related

Is it possible to test this Kotlin class?

I am trying to write unit test cases for a Kotlin class A and trying to mock return client() call present in test() method to unit test test() method:
A.kt kotlin class:
class A (par 1, par 2) : B(par 1, par 2) {
override fun test(item: String): Boolean {
return client()
}
}
B.kt kotlin class:
abstract class B(par 1, par 2) {
private val client: WebClient by lazy {
//does something
}
protected fun client(): WebClient = client
}
Is it possible to mock the client() call in test() method?
And if possible how to do it and what library should I use for mocking?
This should be doable using Mockito testing framework. However, mocking an object returned from a object's method would require mocking the object itself.
val mockOfA = Mockito.mock(A::class)
val mockOfClient = Mockito.mock(WebClient:class)
Mockito.when(mockOfA.test(anyString())).thenReturn(mockOfClient) // Mind your original snippet return type Boolean from A.test()
If your class A implements internal logic you want to test in your test scenario, there are two possible paths:
Create a Spy instead of a Mock. Mocks do only whatever you explicitly set them up to do. Spy 'falls back' to original implementation when it is not given any mock behaviour.
Rewrite you class A so that it depends on external dependency of a ClientProvider to get an instance of WebClient to return in method test() and inject a mock of said provider to your object of type A during the test.

mocking a function call

Using Spock framework for java unit testing.
When unit testing a method method1() and method1 is calling a method method2(),In method2() having a code statement as below :
Config config = new Config();
TimeZone tz=TimeZone.getTimeZone(config.getProps().getProperty(Constants.SERVER_TIMEZONE));
The call config.getProps().getProperty(Constants.SERVER_TIMEZONE)
returns America/Cambridge_Bay
In getProps method the property files is fetched from weblogic domain and it will not be available in spcok, its taking path as null.
Please suggest how can this function call be mocked in spock.
We can use meta class injection to mock response method2 in unit testing given block. Here ClassName is the class to which method2 belongs to.
ClassName.metaClass.method2 = { inputParameters ->
return "America/Cambridge_Bay"
}
Also it is advisable to use #ConfineMetaClass([ClassName]) annotation on the unit test to confine the meta class injection changes to your test case.
Lets start with an example which simulates your situation:
class Config {
Properties getProps() {
def props = new Properties()
props.setProperty(Constants.SERVER_TIMEZONE, 'America/Cambridge_Bay')
props
}
}
class Constants {
static String SERVER_TIMEZONE = 'TIMEZONE'
}
Config config = new Config()
def timeZoneID = config.getProps().getProperty(Constants.SERVER_TIMEZONE)
def tz = TimeZone.getTimeZone(timeZoneID)
assert tz.ID == 'America/Cambridge_Bay'
Since method2() doesn't get a Config instance injected into it, mocks are out of the question. So we'll use Groovy's metaClass, at the class level (since instance level is out of the question too, for the same reason). You can override Config.getProps() like this:
Config.metaClass.getProps {
def props = new Properties()
props.setProperty(Constants.SERVER_TIMEZONE, 'Etc/UTC')
props
}
So you can write your Spock test roughly like this:
// import Constants
// import Config class
class FooSpec extends Specification {
#ConfineMetaClassChanges
def "test stuff"() {
when:
Config.metaClass.getProps {
def props = new Properties()
props.setProperty(Constants.SERVER_TIMEZONE, 'America/Cambridge_Bay')
props
}
// Do more stuff
then:
// Check results
}
}
PS
If you can modify method2() to have Config injected, that would be preferable since then you can use Groovy's MockFor.

Why do I have to extend PowerMockTestCase?

The below test throws java.lang.IllegalStateException: no last call on a mock available when I don't extend from the PowerMockTestCase.
The error disappears as soon as I extend from PowerMockTestCase. Why exactly is this happening?
import static org.junit.Assert.assertEquals;
import org.easymock.EasyMock;
import org.powermock.api.easymock.PowerMock;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.testng.PowerMockTestCase;
#PrepareForTest({ IdGenerator.class, ServiceRegistartor.class })
public class SnippetTest extends PowerMockTestCase{
#org.testng.annotations.Test
public void testRegisterService() throws Exception {
long expectedId = 42;
// We create a new instance of test class under test as usually.
ServiceRegistartor tested = new ServiceRegistartor();
// This is the way to tell PowerMock to mock all static methods of a
// given class
PowerMock.mockStatic(IdGenerator.class);
/*
* The static method call to IdGenerator.generateNewId() expectation.
* This is why we need PowerMock.
*/
EasyMock.expect(IdGenerator.generateNewId()).andReturn(expectedId).once();
// Note how we replay the class, not the instance!
PowerMock.replay(IdGenerator.class);
long actualId = tested.registerService(new Object());
// Note how we verify the class, not the instance!
PowerMock.verify(IdGenerator.class);
// Assert that the ID is correct
assertEquals(expectedId, actualId);
}
}
While using PowerMock for static mocking, there is a class level instrumentation happening to make your mocking work. PowerMockTestCase class has a code (method beforePowerMockTestClass()) to switch your regular class loader to powermock class loader which orchestrates mocking injection. Hence you need to extend this class for static mock to work.
You need to have the PowerMock class-loaders configured so that the static classes can be intercepted (defined using the #PrepareForTest annotation).
You don't have to extend from PowerMockTestCase. For most cases you can also configure TestNG with a PowerMockObjectFactory instead:
#PrepareForTest({ IdGenerator.class, ServiceRegistartor.class })
public class SnippetTest {
#ObjectFactory
public IObjectFactory objectFactory() {
return new PowerMockObjectFactory();
}
#org.testng.annotations.Test
public void testRegisterService() throws Exception {
...
}
}

Mocking a Superclass in Spock

How can you unit test a class that has a superclass in Spock that invokes method calls form its superclass? Or how do you mock a superclass in Spock?
Ex:
class Bar {
def method1(parm1){
//Method actions
}
}
class Foo extends Bar {
def method2(param1, param2) {
//Method actions
super.method1(param1)
}
}
How can I mock behavior of class Bar?
You might use your class Foo as a Spy. The spy will create an instance of your class Foo but gives you the possibility of mocking any public methods declared in your spies class hierarchy.
def fooInstance = Spy(Foo)
fooInstance.method1(_) >> 'return value'
The following code will help you to specify what methods are called actually.
setup:
def fooInstance = Spy(Foo)
when: "this try-catch block is debug code"
try {
// do something with Foo or Bar
}
catch (Exception e) {
}
then:
0 * fooInstance._
This site may be useful. -> Spock Mock Cheatsheet
If you are using Spock to test Kotlin classes, you need to make the superclass methods open so that Spock can stub them.

How to mock private methods in controller in grails 2.2.4

I have a private method which was mocked in grails 1.3.7 using metaclass but now that I upgraded grails version to 2.2.4, the same mocking fails.
Method to test has a call to private method
private def MyPrivateMeth1(def arg1, def arg2) {
...
}
Mocking is something like this
MyController.metaClass.private.MyPrivateMeth1 = { a, b ->
...
}
Try using the #TestFor annotation, which will give you a controller variable. You can then alter the metaclass of that, as well as incorporating Kamil Mikolajczyk and araxn1d's suggestions. So, your whole test should probably look like this:
#TestFor(MyController)
class MyControllerTests {
setup() {
controller.metaClass.MyPrivateMeth1 = {def arg1, def arg2 ->
//you can make your mocked method return whatever you wish here
return [key1: "foo", key2: "bar"]
}
}
void testForSomething() {
//Some code here that invokes an action or two in your controller which in turn invokes the private method
}
}
Make sure to have def (or String, Long, or whatever declaration you use) on the arguments of your mock method precisely as they are on the actual private method in the controller, or your test will try to use the class's normal private method first.
Here's a similar test in Spock:
import spock.lang.Specification
import spock.lang.Unroll
import grails.test.mixin.*
import org.junit.*
#TestFor(MyController)
class MyControllerSpec {
def "test that thing"() {
setup:
controller.metaClass.MyPrivateMeth1 = {def arg1, def arg2 -> return someOutput }
expect:
controller.someAction() == expectedRender
where:
someOutput | expectedRender
[key1: "foo", key2: "bar"] | "expected render from controller"
}
}
It seems that you need to declare types of closure arguments (its 100% if that arguments have actual types, for example Long, but not sure about def, but you need to try):
MyController.metaClass.MyPrivateMeth1 = { def a, def b -> ... }
I believe you don't need the .private. part
MyController.metaClass.MyPrivateMeth1 = { a, b -> ... }
should be enough, however I would specify parameter types explicitly
And, by the way, you should keep java naming conventions - methods names should start with lowercase character
For unit tests I have used Reflection for private methods. Something similar to this should work...
Method method = BehaviourService.getDeclaredMethod("behaviourValidConstraints",User.class,Behaviour.class)
method.setAccessible(true)
boolean valid = ((Boolean)method.invoke(service, user,b)).booleanValue()
First you get the method with getDeclaredMethod setting the name and the parameter types, you set it accesible and finally tou call it with method.invoke passing the object that has the method and the parameters. The result is an Object so you have to cast it.
I know there must be a better solution, but this one is the only one I've found that works
Edit: Sorry, what's above is for making a call to a private method.
I think that I've mocked a private method before just doing...
MyController.metaClass.myPrivateMeth1 { a, b ->
...
}
Just like you wrote it but without the .private and the = sign. Also, as Kamil said, you should follow java naming conventions for method names...