166

I am a newbie to development and to unit tests in particular . I guess my requirement is pretty simple, but I am keen to know others thoughts on this.

Suppose I have two classes like so -

public class First {

    Second second ;

    public First(){
        second = new Second();
    }

    public String doSecond(){
        return second.doSecond();
    }
}

class Second {

    public String doSecond(){
        return "Do Something";
    }
}

Let's say I am writing unit test to test First.doSecond() method. However, suppose, i want to Mock Second.doSecond() class like so. I am using Mockito to do this.

public void testFirst(){
    Second sec = mock(Second.class);
    when(sec.doSecond()).thenReturn("Stubbed Second");

    First first = new First();
    assertEquals("Stubbed Second", first.doSecond());
}

I am seeing that the mocking does not take effect and the assertion fails. Is there no way to mock the member variables of a class that I want to test . ?

Bhesh Gurung
  • 50,430
  • 22
  • 93
  • 142
Anand Hemmige
  • 3,593
  • 6
  • 21
  • 31

9 Answers9

105

You need to provide a way of accessing the member variables so you can pass in a mock (the most common ways would be a setter method or a constructor which takes a parameter).

If your code doesn't provide a way of doing this, it's incorrectly factored for TDD (Test Driven Development).

moffeltje
  • 4,521
  • 4
  • 33
  • 57
kittylyst
  • 5,640
  • 2
  • 23
  • 36
  • 4
    Thanks. I see it. I am just wondering, how I can then perform integration tests using mock where there may be many internal methods, classes that may need to be mocked, but not necessarily available to be set through a setXXX() before hand. – Anand Hemmige Jan 24 '12 at 23:18
  • 2
    Use a dependency injection framework, with a test configuration. Draw up a sequence diagram of the integration test that you're trying to make. Factor the sequence diagram into the objects that you can actually control. This means that if you are working with a framework class which has the dependent object anti-pattern you show above, then you should regard the object and its badly-factored member as a single unit in terms of the sequence diagram. Be prepared to adjust the factoring of any code you control, to make it more testable. – kittylyst Jan 24 '12 at 23:24
  • 14
    Dear @kittylyst, yes probably it is wrong from the TDD point of view or from any kind of rational point of view. But sometimes a developer works in places where nothing makes sense at all and the only target that one have is just to complete the stories you have assigned and go away. Yes, it is wrong, it makes no sense, un-qualified people takes the key decissions and all that things. So, at the end of the day, anti-patterns win for a lot. – amanas Apr 07 '15 at 15:24
  • 8
    I am curious about this, if a class member has no reason to be set from the exterior, why should we create a setter just for the purpose of testing it ? Imagine the 'Second' class here is actually a FileSystem manager or tool, initialized during the construction of the object to test. I have all reasons to want to mock this FileSystem manager, in order to test the First class, and zero reason to make it accessible. I can do this in Python, so why not with Mockito ? – Milan Apr 21 '20 at 18:11
75

This is not possible if you can't change your code. But I like dependency injection and Mockito supports it:

public class First {    
    @Resource
    Second second;

    public First() {
        second = new Second();
    }

    public String doSecond() {
        return second.doSecond();
    }
}

Your test:

@RunWith(MockitoJUnitRunner.class)
public class YourTest {
   @Mock
   Second second;

   @InjectMocks
   First first = new First();

   public void testFirst(){
      when(second.doSecond()).thenReturn("Stubbed Second");
      assertEquals("Stubbed Second", first.doSecond());
   }
}

This is very nice and easy.

Lii
  • 11,553
  • 8
  • 64
  • 88
Janning Vygen
  • 8,877
  • 9
  • 71
  • 102
  • 3
    I think this is a better answer than the other ones because InjectMocks. – sudocoder Oct 20 '15 at 20:38
  • It's funny how one gets, as a testing newbie like myself, to trust certain libraries and frameworks. I was assuming this was just a Bad Idea indicating a need to redesign... until you showed me it **is** indeed possible (very clearly and cleanly) in Mockito. – mike rodent Oct 29 '16 at 19:17
  • 14
    What is **@Resource**? – IgorGanapolsky Apr 24 '17 at 19:26
  • 3
    @IgorGanapolsky the @ Resource is a annotation created/used by the Java Spring framework. Its a way to indicate to Spring this is a bean/object managed by Spring. https://stackoverflow.com/questions/4093504/resource-vs-autowired https://www.baeldung.com/spring-annotations-resource-inject-autowire It is not a mockito thing, but because it is used in the non testing class it has to be mocked in the test. – Grez.Kev Oct 04 '18 at 13:10
  • I don't understand this answer. You say it's not possible then you show it's possible? What exactly is not possible here? – JobHunter69 Jun 17 '20 at 19:59
40

If you look closely at your code you'll see that the second property in your test is still an instance of Second, not a mock (you don't pass the mock to first in your code).

The simplest way would be to create a setter for second in First class and pass it the mock explicitly.

Like this:

public class First {

    Second second ;

    public First(){
        second = new Second();
    }

    public String doSecond(){
        return second.doSecond();
    }

    public void setSecond(Second second) {
        this.second = second;
    }


}

class Second {

    public String doSecond(){
        return "Do Something";
    }
}

....

public void testFirst(){
Second sec = mock(Second.class);
when(sec.doSecond()).thenReturn("Stubbed Second");


First first = new First();
first.setSecond(sec)
assertEquals("Stubbed Second", first.doSecond());
}

Another would be to pass a Second instance as First's constructor parameter.

If you can't modify the code, I think the only option would be to use reflection:

public void testFirst(){
    Second sec = mock(Second.class);
    when(sec.doSecond()).thenReturn("Stubbed Second");


    First first = new First();
    Field privateField = PrivateObject.class.
        getDeclaredField("second");

    privateField.setAccessible(true);

    privateField.set(first, sec);

    assertEquals("Stubbed Second", first.doSecond());
}

But you probably can, as it's rare to do tests on code you don't control (although one can imagine a scenario where you have to test an external library cause it's author didn't :))

Mistu4u
  • 5,132
  • 15
  • 53
  • 91
soulcheck
  • 36,297
  • 6
  • 91
  • 90
  • Got it. I will probably go with your first suggestion. – Anand Hemmige Jan 24 '12 at 23:14
  • Just curious, is there any way or API you are aware of that can mock an object/method at application level or package level. ? I guess what I am saying is, in the above example when I mock 'Second' object, is there a way it can override every instance of Second that is used through the lifecycle of testing. ? – Anand Hemmige Jan 24 '12 at 23:16
  • @AnandHemmige actually the second one (constructor) is cleaner, as it avoids creating unnecesary `Second´ instances. Your classes get nicely decoupled that way. – soulcheck Jan 24 '12 at 23:30
  • 10
    Mockito provides some nice annotations to let you inject your mocks into private variables. Annotate Second with `@Mock` and annotate First with `@InjectMocks` and instantiate First in the initializer. Mockito will automatically do it's best to find a place to inject the Second mock into the First instance, including setting private fields that match type. – jhericks Jan 25 '12 at 05:28
  • `@Mock` was around in 1.5 (maybe earlier, I'm not sure). 1.8.3 introduced `@InjectMocks` as well as `@Spy` and `@Captor`. – jhericks Jan 25 '12 at 16:53
  • I would recommend going with @soulcheck's second suggestion. That is, introduce a package-private constructor for `First`, into which you can pass the `Second`. You can even make the public constructor call the package-private one with `this( new Second());`. Then within your test, call the package-private constructor when you make your test object. – Dawood ibn Kareem Jan 26 '12 at 06:45
  • Further to this, I don't like `@InjectMocks` quite so much, because it doesn't work in every case. It will work for this particular example, but if there is a constructor that can be used for constructor injection (and there is some quite complex logic around this), then `@InjectMocks` won't try field injection, and the test won't work. I prefer to avoid `@InjectMocks` entirely, because of its apparent inconsistencies (and yes, I realise that it's not inconsistent if you understand its logic completely; but I don't think it's worthwhile bothering). – Dawood ibn Kareem Jan 26 '12 at 06:48
11

You can mock member variables of a Mockito Mock with ReflectionTestUtils

ReflectionTestUtils.setField(yourMock, "memberFieldName", value);
Gayan Weerakutti
  • 11,904
  • 2
  • 71
  • 68
7

If you can't change the member variable, then the other way around this is to use powerMockit and call

Second second = mock(Second.class)
when(second.doSecond()).thenReturn("Stubbed Second");
whenNew(Second.class).withAnyArguments.thenReturn(second);

Now the problem is that ANY call to new Second will return the same mocked instance. But in your simple case this will work.

user1509463
  • 121
  • 2
  • 4
7

I had the same issue where a private value was not set because Mockito does not call super constructors. Here is how I augment mocking with reflection.

First, I created a TestUtils class that contains many helpful utils including these reflection methods. Reflection access is a bit wonky to implement each time. I created these methods to test code on projects that, for one reason or another, had no mocking package and I was not invited to include it.

public class TestUtils {
    // get a static class value
    public static Object reflectValue(Class<?> classToReflect, String fieldNameValueToFetch) {
        try {
            Field reflectField  = reflectField(classToReflect, fieldNameValueToFetch);
            reflectField.setAccessible(true);
            Object reflectValue = reflectField.get(classToReflect);
            return reflectValue;
        } catch (Exception e) {
            fail("Failed to reflect "+fieldNameValueToFetch);
        }
        return null;
    }
    // get an instance value
    public static Object reflectValue(Object objToReflect, String fieldNameValueToFetch) {
        try {
            Field reflectField  = reflectField(objToReflect.getClass(), fieldNameValueToFetch);
            Object reflectValue = reflectField.get(objToReflect);
            return reflectValue;
        } catch (Exception e) {
            fail("Failed to reflect "+fieldNameValueToFetch);
        }
        return null;
    }
    // find a field in the class tree
    public static Field reflectField(Class<?> classToReflect, String fieldNameValueToFetch) {
        try {
            Field reflectField = null;
            Class<?> classForReflect = classToReflect;
            do {
                try {
                    reflectField = classForReflect.getDeclaredField(fieldNameValueToFetch);
                } catch (NoSuchFieldException e) {
                    classForReflect = classForReflect.getSuperclass();
                }
            } while (reflectField==null || classForReflect==null);
            reflectField.setAccessible(true);
            return reflectField;
        } catch (Exception e) {
            fail("Failed to reflect "+fieldNameValueToFetch +" from "+ classToReflect);
        }
        return null;
    }
    // set a value with no setter
    public static void refectSetValue(Object objToReflect, String fieldNameToSet, Object valueToSet) {
        try {
            Field reflectField  = reflectField(objToReflect.getClass(), fieldNameToSet);
            reflectField.set(objToReflect, valueToSet);
        } catch (Exception e) {
            fail("Failed to reflectively set "+ fieldNameToSet +"="+ valueToSet);
        }
    }

}

Then I can test the class with a private variable like this. This is useful for mocking deep in class trees that you have no control as well.

@Test
public void testWithRectiveMock() throws Exception {
    // mock the base class using Mockito
    ClassToMock mock = Mockito.mock(ClassToMock.class);
    TestUtils.refectSetValue(mock, "privateVariable", "newValue");
    // and this does not prevent normal mocking
    Mockito.when(mock.somthingElse()).thenReturn("anotherThing");
    // ... then do your asserts
}

I modified my code from my actual project here, in page. There could be a compile issue or two. I think you get the general idea. Feel free to grab the code and use it if you find it useful.

dave
  • 139
  • 2
  • 3
  • Could you explain your code with a actual usecase? like Public class tobeMocker(){ private ClassObject classObject; } Where classObject equals the object to be replaced. – Jasper Lankhorst Mar 23 '17 at 11:09
  • In your example, if ToBeMocker instance = new ToBeMocker(); and ClassObject someNewInstance = new ClassObject() { @Override //something like an external dependency }; then TestUtils.refelctSetValue(instance, "classObject", someNewInstance); Note that you have to figure out what you want to override for mocking. Lets say you have a database and this override will return a value so you do not need to select. Most recently I had a service bus that I did not want to actually process the message but wanted to ensure it received it. Thus, I set the private bus instance this way-Helpful? – dave Mar 25 '17 at 01:59
  • You will have to imagine there was formatting in that comment. It was removed. Also, this will not work with Java 9 as it will lock down private access. We will have to work with some other constructs once we have an official release and can work withing its actual bounds. – dave Mar 25 '17 at 02:00
  • This is truly useful, differently than other comments saying: "this is not possible" or that the code has to be refactored and, even worse, the private property/method to be made public. This should be the accepted response as it solves the issue without requiring any code refactoring. – AlexB Nov 18 '21 at 22:49
3

If you want an alternative to ReflectionTestUtils from Spring in mockito, use

Whitebox.setInternalState(first, "second", sec);
  • Welcome to Stack Overflow! There are other answers that provide the OP's question, and they were posted many years ago. When posting an answer, please make sure you add either a new solution, or a substantially better explanation, especially when answering older questions or comment on other answers. – help-info.de Apr 19 '19 at 13:14
0

Lots of others have already advised you to rethink your code to make it more testable - good advice and usually simpler than what I'm about to suggest.

If you can't change the code to make it more testable, PowerMock: https://code.google.com/p/powermock/

PowerMock extends Mockito (so you don't have to learn a new mock framework), providing additional functionality. This includes the ability to have a constructor return a mock. Powerful, but a little complicated - so use it judiciously.

You use a different Mock runner. And you need to prepare the class that is going to invoke the constructor. (Note that this is a common gotcha - prepare the class that calls the constructor, not the constructed class)

@RunWith(PowerMockRunner.class)
@PrepareForTest({First.class})

Then in your test set-up, you can use the whenNew method to have the constructor return a mock

whenNew(Second.class).withAnyArguments().thenReturn(mock(Second.class));
jwepurchase
  • 733
  • 6
  • 22
-2

Yes, this can be done, as the following test shows (written with the JMockit mocking API, which I develop):

@Test
public void testFirst(@Mocked final Second sec) {
    new NonStrictExpectations() {{ sec.doSecond(); result = "Stubbed Second"; }};

    First first = new First();
    assertEquals("Stubbed Second", first.doSecond());
}

With Mockito, however, such a test cannot be written. This is due to the way mocking is implemented in Mockito, where a subclass of the class to be mocked is created; only instances of this "mock" subclass can have mocked behavior, so you need to have the tested code use them instead of any other instance.

Rogério
  • 16,171
  • 2
  • 50
  • 63
  • 4
    the question was not whether or not JMockit is better than Mockito, but rather how to do it in Mockito. Stick to building a better product instead of looking for opportunity to trash the competition! – TheZuck Oct 14 '13 at 11:40
  • 9
    The original poster only says that he’s using Mockito; it is only implied that Mockito is a fixed and hard requirement so the hint that JMockit can handle this situation is not that inappropriate. – Bombe May 09 '14 at 18:21