Using Spock to mock private static final variables in Java
Asked Answered
A

2

11

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")
Ajay answered 18/12, 2014 at 17:43 Comment(5)
I guess it depends on how it's being tested, but IMO using a framework designed to do what you're trying to do is the better approach, and more broadly applicable, e.g., java.dzone.com/articles/mocking-static-methods-groovy and https://mcmap.net/q/599925/-mock-static-method-with-groovymock-or-similar-in-spock etc.Trilateration
Thanks for your comment @DaveNewton - especially with Peter Niederwieser stating that only groovy defined methods can be mocked in Spock. My case is specific to class fields/variables, but I'll check to see if one of the other frameworks would be able to do this for me. Thanks.Ajay
I know it is not what you are looking for, but a while ago I played around with the combination of Spock and PowerMock to mock static methods. I don't remember if it also works for (final) static members, just try for yourself. But what I actually want to say is that if your code is so hard to test, you should not upgrade your tests or test tools but refactor the code for testability. I think whenever you need tricks like the ones you want or tools to give you what you want, this is a code smell.Hamford
Why without adding other frameworks? Any particular reason?Teaser
The main issue is that it seems a lot of frameworks can handle one difficult testing scenario (mocking finals, mocking constructors, etc.), and in the past I've tried adding each framework to get the one piece I need, and that just makes a mess. I'd prefer to keep the dependencies as minimalistic as possible - which will also help reduce the different coding paradigms introduced by each of the different frameworks.Ajay
M
13

Based on what I learned from https://mcmap.net/q/766991/-unit-testing-of-a-class-with-staticloggerbinder, 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.

Marler answered 20/11, 2015 at 8:37 Comment(0)
T
0

Either you need to use PowerMock (or another similar solution), or refactor your code. Spock does not support mocking of private/static/final methods on its own. This limitation is also present in Mockito, so that must give you a hint on best-practices.

Teaser answered 11/6, 2015 at 20:26 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.