3

I have:

    SomeClass a = spy(SomeClass.class)

SomeClass has private int myValue.

I want to set myVlaue to 3.

How can I do that using Mockito?

(If not possible with Mockito, what is my next best option preferably without having to discard my code that uses Mockito).

Notes:

  • Do not suggest to mock getMyValue(), I already know I can do that.
  • Do not suggest to add setMyValue(int newMyValue), I do not want it.
Maciej Kowalski
  • 25,605
  • 12
  • 54
  • 63
Gilad Baruchian
  • 930
  • 3
  • 14
  • 30
  • I think the problem here is that your tests are getting too tied up in your implementation. A better question is "what is so important about this int at mocking time?" Can you not construct an object of SomeClass with the right value? You do know you can do spy(myObject) to spy on a real instance? – Ashley Frieze Nov 03 '16 at 23:01
  • @AshleyFrieze because I want to test a real function in SomeClass, and that function uses myValue. I could change the code so SomeClass always calls getMyValue() instead of the variable itself and then mock getMyValue() to return 3, but this is not a safe practice and I am looking for the more advanced capability that I just described. – Gilad Baruchian Nov 06 '16 at 12:27
  • 1
    This sort of whitebox testing is a "test smell". You don't seem to be testing the behaviour of your class via its public interface. That suggests a lack of separation of concerns. – Ashley Frieze Nov 06 '16 at 15:26

4 Answers4

5

The best option I came up with so far is reflection:

Field myVarField = SomeClass.class.getDeclaredField("myVarField");
myVarField.setAccessible(true);
myVarField.set(someClass,3);
Gilad Baruchian
  • 930
  • 3
  • 14
  • 30
  • 2
    Reflection is the correct answer. Other than with `@InjectMocks` or [the recently removed internal class Whitebox](http://stackoverflow.com/q/40280918/1426891), Mockito doesn't concern itself at all with fields, so you need to handle field access yourself or call setters on your spy. – Jeff Bowman Nov 01 '16 at 16:16
  • @JeffBowman does mockito have a rationale for not supporting fields mocking or its just a limitation? if its not a limitation then what is the recommended way? – Gilad Baruchian Nov 06 '16 at 12:35
  • i'd be surprised if mockito recommend mocking getters and use only getters – Gilad Baruchian Nov 06 '16 at 12:37
  • Moved to separate answer. – Jeff Bowman Nov 06 '16 at 16:53
  • 1
    @GiladBaruchian if `a` is a value object, it should be possible to set the value before using the object. In a perfect world the value object also is immutable. If `a` is a stateful unit, the test should be agnostic of its internal state and only mock the interface - or spy on it. Mocking should always be preferred over spying, if possible. In case `a` is both: a value object *and* a unit with its own behaviour, the design most likely is flawed. – Amadán Aug 20 '18 at 07:12
2

Reflection is the correct answer. Other than with @InjectMocks or the recently removed internal class Whitebox, Mockito doesn't concern itself at all with fields, so you need to handle field access yourself or call setters on your spy.

As for rationale, I haven't seen one, but remember what Mockito does: It offers tooling and neat syntax to record and verify interactions—method calls, to be more specific—by internally creating a dynamic subclass (proxy) that overrides all methods. With fields there's nothing to record or verify, because the JVM just sets the value; working with fields would be a little out of Mockito's scope and introduce a lot of caveats about which fields can be changed (private? static? final?) and what you can do with them (not stub or verify multiple interactions).

Though Mockito doesn't explicitly recommend using getters, they are one of the few seams where you could override/track state accesses, so there's something to be said for using them all across Java for encapsulation's sake. If you can't set and read values like this:

mockitoMockWithFields.fieldA = 42;
return mockitoMockWithFields.fieldB;

Then all you're looking for Mockito to do is to defeat encapsulation or finality; Mockito can't do either natively, even for methods, so it's probably pretty reasonable for you to defeat those yourself or with a different library.

Community
  • 1
  • 1
Jeff Bowman
  • 90,959
  • 16
  • 217
  • 251
  • I think it is bad advice to use reflection for setting the state of dependencies in unit tests. ideally there should not be any mutable dependencies other then mocks. – Amadán Aug 20 '18 at 07:00
  • @Amadán: I don't think it's a great idea for most cases either, but this particular question has ruled out better solutions like visibility changes, mocking getters, and adding setters. My first choice in this case would be to write a fake implementation, but reflection is certainly an attractive option given the (arbitrary) constraints in the question. – Jeff Bowman Aug 20 '18 at 15:31
1

Try the Following Code,

use org.springframework.test.util.ReflectionTestUtils
ReflectionTestUtils.setField(yourClass, "yourFieldName", yourFieldValue);
Thomas
  • 1,445
  • 14
  • 30
erad
  • 31
  • 1
0

What exactly is a in your test?

1) If a is a dependency:

You shouldn't force an internal state onto it but configure the mock to return a desired value on a certain call, e.g. Mockito.doReturn("my current value is: 3").when(a).getText().

The basic idea behind that is that a unit test should be independend of the implementation of other units (and thereby also be independent of flaws in those implementations). Manipulation of a dependency's internal state causes close coupling of your test to another unit. The result would be a complex, hardly testable scenario.

2) If a is your system under test:

You may provide an option to inject myValue, e.g. a package private method setMyValue(final int value). A classic example would be inserting a timestamp provider into your system under test (e.g. Supplier<LocalDateTime>) to inject a fix timestamp for testing (the test couldn't know which timestamp was used for a certain operation, so it would need to inject a fix timestamp for a well defined test scenario). Such utility methods for unit tests should be package private.

On a side note: Unit tests must be simple and easy to read and understand. Don't call a mock "a", use a name that fits the nature of it! Also I use to call the system under test "sut" in all my tests. Thus it is easy to distinguish between what's being tested and what's a dependency.

Amadán
  • 718
  • 5
  • 18