What do I use instead of Whitebox in Mockito 2.2 to set fields?
Asked Answered
P

7

48

When using Mockito 1.9.x I have been using Whiteboxto set values of fields to "inject" mocks. Se example below:

@Before
public void setUp() {

    eventHandler = new ProcessEventHandler();
    securityService = new SecurityServiceMock();
    registrationService = mock(RegistrationService.class);

    Whitebox.setInternalState(eventHandler, "registrationService", registrationService);
    Whitebox.setInternalState(eventHandler, "securityService", securityService);
}

I really like this approach, but now that I tried to upgrade to Mockito 2.2.7 I noticed (or rather, my IDE noticed and told me quite a few times) that Whitebox was no longer to be found in Mockito.

I have found one alternative, that can work as a replacement, and that is org.powermock.reflect.Whitebox, the problem with that is that I get another dependency (Powermock), just to use Whitebox.

Powermock also have a class named Whitebox, but unfortunately it looks as if it can not be used with Mockito 2.2.x

Is there any good alternatives in Mockito that I can use to manually "inject" fields, now that Whitebox is no longer available?


Solution

I wrote in a comment in response to the post made of @JeffBowman. In short I chose to copy the code of WhiteBox, and use that, since it is used in most of the test cases and the class does not have dependencies to other classes. It was the fastest path to solve this issue.

Note The solution that @bcody suggest is a better alternative, if you are using spring, it ads no extra code for you to maintain. I got that information to late :(

Punchball answered 27/10, 2016 at 9:26 Comment(5)
Does Mockito's @InjectMocks annotation not help?Bristol
I don't know how they work in 2.2. In 1.x my experience is that if they for some reason fail to inject mockito is silent and I get a NullPointerException in the class where the injection failed. When using whitebox I get a exception clearly telling me that it failed. I started by using @InjectMocks, but thought that WhiteBox was better, so did my co-workers, so now we have a lot of code that is using WhiteBox, making upgrade timeconsuming, if we need to do something else.Punchball
Here is a link to a blog post talking about the issues that exists with @InjectMocks, it has the same recommendations as the accepted answer has: tedvinke.wordpress.com/2014/02/13/…Punchball
I ended up copying the Whitebox code too, it's a very simple class that streamlines what I needed to do. Here's the code, in case anyone else needs it raw.githubusercontent.com/mockito/mockito/…Otology
Here with fest-reflect api you can find an easy to use fluent API for reflection support. This is what I use as an alternative to Mockito's Whiltebox.Cyano
P
16

Note that Whitebox was always in the org.mockito.internal package. Beyond the incrementing of the major version number, the internal designation is a giveaway that the package may be subject to breaking changes.

If you do want to make it a point to set otherwise-inaccessible fields in your test, you can do so in the same way that setInternalState does, which is just to identify the field in the hierarchy, call setAccessible on it, and then set it. The full code is here on grepcode. You can also examine a number of other ways to set inaccessible state in tests.

public static void setInternalState(Object target, String field, Object value) {
    Class<?> c = target.getClass();
    try {
        Field f = getFieldFromHierarchy(c, field);  // Checks superclasses.
        f.setAccessible(true);
        f.set(target, value);
    } catch (Exception e) {
        throw new RuntimeException(
            "Unable to set internal state on a private field. [...]", e);
    }
}

However, in situations like this, my general advice is to stop fighting the tools: Java's four levels of encapsulation (public, protected, package, private) are not necessarily granular enough to express the degree of protection you're trying to express, and it's often much easier to add a well-documented initialization method or constructor override to override the dependencies as you're trying to do reflectively. If you put your tests in the same Java package as the class it tests, you can often even make the fields or method/constructor package-private, which is also a good reason to set up parallel source folders src and tests (etc) that represent two halves of the same Java package.

Though some treat this additional method or constructor as "API pollution", I see it instead as coding to the requirements of one of your class's most important consumers—its own test. If you need a pristine external interface, you can easily define one separately such that you can hide any details you'd like. However, you may find you like the ability to inject any real or mock implementation directly into your now-more-flexible component, at which point you may want to look into dependency injection patterns or frameworks.

Passionate answered 27/10, 2016 at 17:55 Comment(7)
In cases like this, however, one should be able to use the @InjectMocks and @Mock annotations, right? So, not the case of fighting the tool, but of not properly using what it provides.Repute
@Rogério Depends on how comfortable you are with @InjectMocks's caveats. I've seen enough problems with its use that I'm no longer recommending it myself. You're welcome to, though.Passionate
@Rogério - I used to think it was great (@InjectMocks), but after a while I started to see problems with this technique and started to use Whitebox, it gave better error messages if something was wrong.Punchball
You're probably right about @InjectMocks; indeed it's a feature that hasn't received much attention from Mockito developers. Of course, as JMockit's developer, I use the @Tested annotation which is much more advanced at this point 8^)Repute
@JeffBowman I ended up copying the code for WhiteBox, as it could be done that way, and searched and replaced the importstatement. It is not a nice solution, since we have many modules, and I need to make it awailable in all modules. You have a good point in that tests is one of a class most important consumer; why not make it simpler for them? Changeing the way to think is harder than changing technology - we will see how me and my colleges will do this in the future. I am not totaly convinced yet, I need to work with my self a bit first. Your philosophy It has its pro's.Punchball
Yes. At last I have to change private methods to "package" visibility and that just enables its usage in the tests. That seems the way with least impact. Thanks.Mitre
Mirror for Whitebox implementation: android.googlesource.com/platform/external/mockito/+/…Simba
M
86

If you are using Spring (the spring-test library specifically), you can simply use ReflectionTestUtils.setField instead of Whitebox.setInternalState

Martins answered 19/6, 2017 at 12:38 Comment(2)
That would have been a better solution than copying the Whitebox code. I wish I knew about this class earlier :)Punchball
an example would be more helpfulFloriaflorian
P
16

Note that Whitebox was always in the org.mockito.internal package. Beyond the incrementing of the major version number, the internal designation is a giveaway that the package may be subject to breaking changes.

If you do want to make it a point to set otherwise-inaccessible fields in your test, you can do so in the same way that setInternalState does, which is just to identify the field in the hierarchy, call setAccessible on it, and then set it. The full code is here on grepcode. You can also examine a number of other ways to set inaccessible state in tests.

public static void setInternalState(Object target, String field, Object value) {
    Class<?> c = target.getClass();
    try {
        Field f = getFieldFromHierarchy(c, field);  // Checks superclasses.
        f.setAccessible(true);
        f.set(target, value);
    } catch (Exception e) {
        throw new RuntimeException(
            "Unable to set internal state on a private field. [...]", e);
    }
}

However, in situations like this, my general advice is to stop fighting the tools: Java's four levels of encapsulation (public, protected, package, private) are not necessarily granular enough to express the degree of protection you're trying to express, and it's often much easier to add a well-documented initialization method or constructor override to override the dependencies as you're trying to do reflectively. If you put your tests in the same Java package as the class it tests, you can often even make the fields or method/constructor package-private, which is also a good reason to set up parallel source folders src and tests (etc) that represent two halves of the same Java package.

Though some treat this additional method or constructor as "API pollution", I see it instead as coding to the requirements of one of your class's most important consumers—its own test. If you need a pristine external interface, you can easily define one separately such that you can hide any details you'd like. However, you may find you like the ability to inject any real or mock implementation directly into your now-more-flexible component, at which point you may want to look into dependency injection patterns or frameworks.

Passionate answered 27/10, 2016 at 17:55 Comment(7)
In cases like this, however, one should be able to use the @InjectMocks and @Mock annotations, right? So, not the case of fighting the tool, but of not properly using what it provides.Repute
@Rogério Depends on how comfortable you are with @InjectMocks's caveats. I've seen enough problems with its use that I'm no longer recommending it myself. You're welcome to, though.Passionate
@Rogério - I used to think it was great (@InjectMocks), but after a while I started to see problems with this technique and started to use Whitebox, it gave better error messages if something was wrong.Punchball
You're probably right about @InjectMocks; indeed it's a feature that hasn't received much attention from Mockito developers. Of course, as JMockit's developer, I use the @Tested annotation which is much more advanced at this point 8^)Repute
@JeffBowman I ended up copying the code for WhiteBox, as it could be done that way, and searched and replaced the importstatement. It is not a nice solution, since we have many modules, and I need to make it awailable in all modules. You have a good point in that tests is one of a class most important consumer; why not make it simpler for them? Changeing the way to think is harder than changing technology - we will see how me and my colleges will do this in the future. I am not totaly convinced yet, I need to work with my self a bit first. Your philosophy It has its pro's.Punchball
Yes. At last I have to change private methods to "package" visibility and that just enables its usage in the tests. That seems the way with least impact. Thanks.Mitre
Mirror for Whitebox implementation: android.googlesource.com/platform/external/mockito/+/…Simba
M
11

You can use FieldSetter in Mockito2.x

    import org.mockito.internal.util.reflection.FieldSetter;
 FieldSetter.setField(eventHandler,eventHandler.getClass().getDeclaredField("securityService"), securityService);
Melvinmelvina answered 11/4, 2019 at 19:3 Comment(1)
Just as Whitebox, this class can suddenly change (since it is in the internal package). But this looks like the replacement for Whitebox in mockito.Punchball
K
10

The cleanest, neatest and most portable way without reinventing the wheel is to use Apache Commons' FieldUtils. https://commons.apache.org/proper/commons-lang/apidocs/org/apache/commons/lang3/reflect/FieldUtils.html

The answer to your question would then be

public static void setStaticFieldValue(
        @NonNull final Class<?> clz,
        @NonNull final String fieldName,
        @NonNull final Object value) throws Exception {
    final Field f = FieldUtils.getField(clz, fieldName, true);
    FieldUtils.removeFinalModifier(f);
    f.set(null, value);
}
Kitsch answered 5/6, 2018 at 12:7 Comment(1)
This! And the rest of the apache commons reflect docs (like MethodUtils, ConstructorUtils, ...) can be found here: commons.apache.org/proper/commons-lang/apidocs/org/apache/…Yangyangtze
C
2

Here with fest-reflect api you can find an easy to use fluent API for reflection support. This is what I use as an alternative to Mockito's Whiltebox.

Cyano answered 18/6, 2019 at 8:8 Comment(0)
P
0

The important distinction between plain old Java and plain new Java is getDeclaredField/s returns private fields.

public static Object getField(Object o, String name) {
    try {
        Field f = o.getClass().getDeclaredField(name);
        f.setAccessible(true);
        return f.get(o);
    } catch (Exception ex) {
        log.info(o.getClass() + " " + name + " " + ex.getMessage(), ex);
    }
    return null;
}

public static void setField(Object o, String name, Object value) {
    try {
        Field f = o.getClass().getDeclaredField(name);
        f.setAccessible(true);
        f.set(o, value);
    } catch (Exception ex) {
        log.info(o.getClass() + " " + name + " " + ex.getMessage(), ex);
    }
}
Parimutuel answered 19/7, 2023 at 23:9 Comment(0)
I
-2

PowerMock has Whitebox as part of its public API. This is part of powermock-reflect.

Instinctive answered 1/11, 2022 at 9:20 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.