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:
Running my test cases with PowerMockRunner:
@RunWith(PowerMockRunner.class)
Instruct Powermock to prepare
Test.class
for manipulation of private fields:@PrepareForTest({ Test.class })
And finally mock the constructor for Person class:
PowerMockito.mockStatic(Person.class);
PowerMockito.whenNew(Person.class).withNoArguments().thenReturn(person);
Related Topics
Package a Non-Modular Javafx Application
Best Way to Build a Plugin System with Java
Parsing JSON with Gson, Object Sometimes Contains List Sometimes Contains Object
Deserialize JSON to Arraylist<Pojo> Using Jackson
Java 8 Lambdas Group List into Map
Is There a Newline Constant Defined in Java Like Environment.Newline in C#
Performance of Stringtokenizer Class VS. String.Split Method in Java
Find Place for Dedicated Application Folder
Java: Get Current Date and Time from Server Not System Clock
Printing My MAC's Serial Number in Java Using Unix Commands
Set Private Field Value with Reflection
Java Regular Expressions and Dollar Sign
Get Cell Value from Excel Sheet with Apache Poi
How to Dynamically Add Items to a Java Array
How to Correctly Decode Unicode Parameters Passed to a Servlet