Mocking member variables of a class using Mockito

JavaMockingMockito

Java Problem Overview


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 . ?

Java Solutions


Solution 1 - Java

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).

Solution 2 - Java

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.

Solution 3 - Java

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 :))

Solution 4 - Java

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.

Solution 5 - Java

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.

Solution 6 - Java

You can mock member variables of a Mockito Mock with ReflectionTestUtils

ReflectionTestUtils.setField(yourMock, "memberFieldName", value);

Solution 7 - Java

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

Whitebox.setInternalState(first, "second", sec);

Solution 8 - Java

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));

Solution 9 - Java

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.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionAnand HemmigeView Question on Stackoverflow
Solution 1 - JavakittylystView Answer on Stackoverflow
Solution 2 - JavaJanningView Answer on Stackoverflow
Solution 3 - JavasoulcheckView Answer on Stackoverflow
Solution 4 - Javauser1509463View Answer on Stackoverflow
Solution 5 - JavadaveView Answer on Stackoverflow
Solution 6 - JavaGayan WeerakuttiView Answer on Stackoverflow
Solution 7 - JavaSzymon ZwolińskiView Answer on Stackoverflow
Solution 8 - JavajwepurchaseView Answer on Stackoverflow
Solution 9 - JavaRogérioView Answer on Stackoverflow