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")
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.
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.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With