Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using Spock to mock private static final variables in Java

I'm trying to write some Spock tests with Groovy to test some Java code (specifically a servlet Filter). I have some private static and private static final variables that I would like to mock, but I can't determine if there is a way to do this. I know metaClass is available for methods, is there anything similar for variables?

For instance, I have:

public class MyFilter implements Filter {
  private static WebResource RESOURCE;
  private static final String CACHE_KEY = "key-to-be-used-for-cache";
  ... actual methods, etc ...
}

I've tried using Mock(MyFilter), as well as using Java reflection to change the value (based on this question and answer Change private static final field using Java reflection).

I would like to do this without adding something like Mockito or other frameworks, if that's possible, just use plain Groovy and Spock.

Thanks for any ideas!

UPDATE 1

At least for private static variables I did get the following to work:

Field field = MyFilter.class.getDeclaredField("CACHE_KEY")
field.setAccessible(true)
field.set(null, "new-key-value")

But I still haven't been able to get around the final aspect.

UPDATE 2

Thanks to Xv. I can now set this with the following:

Field field = MyFilter.class.getDeclaredField("CACHE_KEY")
field.setAccessible(true)

Field modifiersField = Field.class.getDeclaredField("modifiers")
modifiersField.setAccessible(true);
modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);

field.set(null, "new-key-value")
like image 860
mnd Avatar asked Dec 18 '14 17:12

mnd


People also ask

How to mock private and static methods?

For Mockito, there is no direct support to mock private and static methods. In order to test private methods, you will need to refactor the code to change the access to protected (or package) and you will have to avoid static/final methods.

How do you mock a method in Spock?

In Spock, a Mock may behave the same as a Stub. So we can say to mocked objects that, for a given method call, it should return the given data. So generally, this line says: itemProvider. getItems will be called once with ['item-'id'] argument and return given array.

Does Spock use JUnit?

Both Groovy and Java build and run on the JVM. A large enterprise build can run both JUnit and Spock tests in the same time. Spock uses the JUnit runner infrastructure and therefore is compatible with all existing Java infrastructure. For example, code coverage with Spock is possible in the same way as JUnit.


1 Answers

Based on what I learned from https://stackoverflow.com/a/25031713/239408, this works for me in spock

import java.lang.reflect.Field
import java.lang.reflect.Modifier

...

    def setup() {

        Field field = BackendCredentials.getDeclaredField("logger")
        field.setAccessible(true);

        Field modifiersField = Field.class.getDeclaredField("modifiers");
        modifiersField.setAccessible(true);
        modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);

        field.set(null, Mock(Logger))
    }

Looks like you are missing the unsetting of the Modifier.FINAL flag.

like image 84
xverges Avatar answered Oct 15 '22 03:10

xverges