How to Use Mockito When We Cannot Pass a Mock Object to an Instance of a Class

Using Mockito, how can I mock a return value of an instance method without having an instance of that class?

You can't do this purely in Mockito as you described. Mockito doesn't control all objects of type MyClass, but instead makes it very easy to create and instantiate a proxy subclass it can control. This is why Mockito counts on classes and methods being non-final, and why you won't be able to use Mockito to affect behavior of all instances.

See also: How to use Mockito when we cannot pass a mock object to an instance of a class

One workaround is to switch libraries: As Slava describes, you can use PowerMock, which has a custom classloader that modifies bytecode, which includes replacing calls to the new MyClass() constructor so they can return your mocks instead.

As an alternative, you can restructure your class to make it easier to test. You could fully structure the class for dependency injection and inject a Provider<MyClass>, but there's a more-local solution with most of the benefits:

public class ClassThatConsumesMyClass {
/** Visible for testing. Override to customize MyClass instances. */
MyClass createMyClass() {
return new MyClass();
}

public ReturnValue getSpecialObjects(int n) {
for (int i = 0; i < n; i++) {
// Instead of calling "new MyClass()", call your method.
MyClass myClassInstance = createMyClass();
// ...
}
// ...
}
}

public class TestForClassThatConsumesMyClass {
ClassThatConsumesMyClass consumer;

@Before public void createMyClass() {
consumer = new ClassThatConsumesMyClass() {
@Override MyClass createMyClass() {
MyClass myInstance = Mockito.mock(MyClass.class);
Mockito.when(myInstance.getSpacing()).thenReturn(0);
return myInstance;
}
}
}

// Test as usual, now that you've mocked every newly-created
// instance of MyClass in your consumer.
}

Mocking member variables of a class using Mockito

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

Use Mockito to mock some methods but not others

To directly answer your question, yes, you can mock some methods without mocking others. This is called a partial mock. See the Mockito documentation on partial mocks for more information.

For your example, you can do something like the following, in your test:

Stock stock = mock(Stock.class);
when(stock.getPrice()).thenReturn(100.00); // Mock implementation
when(stock.getQuantity()).thenReturn(200); // Mock implementation
when(stock.getValue()).thenCallRealMethod(); // Real implementation

In that case, each method implementation is mocked, unless specify thenCallRealMethod() in the when(..) clause.

There is also a possibility the other way around with spy instead of mock:

Stock stock = spy(Stock.class);
when(stock.getPrice()).thenReturn(100.00); // Mock implementation
when(stock.getQuantity()).thenReturn(200); // Mock implementation
// All other method call will use the real implementations

In that case, all method implementation are the real one, except if you have defined a mocked behaviour with when(..).

There is one important pitfall when you use when(Object) with spy like in the previous example. The real method will be called (because stock.getPrice() is evaluated before when(..) at runtime). This can be a problem if your method contains logic that should not be called. You can write the previous example like this:

Stock stock = spy(Stock.class);
doReturn(100.00).when(stock).getPrice(); // Mock implementation
doReturn(200).when(stock).getQuantity(); // Mock implementation
// All other method call will use the real implementations

Another possibility may be to use org.mockito.Mockito.CALLS_REAL_METHODS, such as:

Stock MOCK_STOCK = Mockito.mock( Stock.class, CALLS_REAL_METHODS );

This delegates unstubbed calls to real implementations.


However, with your example, I believe it will still fail, since the implementation of getValue() relies on quantity and price, rather than getQuantity() and getPrice(), which is what you've mocked.

Another possibility is to avoid mocks altogether:

@Test
public void getValueTest() {
Stock stock = new Stock(100.00, 200);
double value = stock.getValue();
assertEquals("Stock value not correct", 100.00*200, value, .00001);
}

Can't return Class Object with Mockito

Also, a slightly more terse way to get around this is to use the do syntax instead of when.

doReturn(User.class).when(methodParameter).getParameterType();

How to mock a final class with mockito

Mocking final/static classes/methods is possible with Mockito v2 only.

add this in your gradle file:

testImplementation 'org.mockito:mockito-inline:2.13.0'

This is not possible with Mockito v1, from the Mockito FAQ:

What are the limitations of Mockito

  • Needs java 1.5+

  • Cannot mock final classes


...

Test class with a new() call in it with Mockito

For the future I would recommend Eran Harel's answer (refactoring moving new to factory that can be mocked). But if you don't want to change the original source code, use very handy and unique feature: spies. From the documentation:

You can create spies of real objects. When you use the spy then the real methods are called (unless a method was stubbed).

Real spies should be used carefully and occasionally, for example when dealing with legacy code.

In your case you should write:

TestedClass tc = spy(new TestedClass());
LoginContext lcMock = mock(LoginContext.class);
when(tc.login(anyString(), anyString())).thenReturn(lcMock);

Mockito: Mock private field initialization

I already found the solution to this problem which I forgot to post here.

@RunWith(PowerMockRunner.class)
@PrepareForTest({ Test.class })
public class SampleTest {

@Mock
Person person;

@Test
public void testPrintName() throws Exception {
PowerMockito.whenNew(Person.class).withNoArguments().thenReturn(person);
Test test= new Test();
test.testMethod();
}
}

Key points to this solution are:

  1. Running my test cases with PowerMockRunner: @RunWith(PowerMockRunner.class)

  2. Instruct Powermock to prepare Test.class for manipulation of private fields: @PrepareForTest({ Test.class })

  3. And finally mock the constructor for Person class:

    PowerMockito.mockStatic(Person.class);
    PowerMockito.whenNew(Person.class).withNoArguments().thenReturn(person);



Related Topics



Leave a reply



Submit