For mocking local variable/method call on local object that is constructed inside method under test, we are using PowerMockito library as of now.
We are trying to evaluate whether we can use mockito-inline (version 3.7.7) to do the same.
In short, we are trying to intercept construction of object using Mockito.mockConstruction, so that we can specify mocked behavior on object created locally.
Here is a scenario which describes our usage. Since this is legacy code, we are not in a position to change it now.(e.g. Change dependency to instance variable or some other refactoring)
In short, execute() method of class MyClass is constructing object of Util class locally. Since we want to unit test core logic of execute() & hence need to mock process() method called on local Util object created inside execute() method of class MyClass.
public class MyClass {
public String execute(){
Util localUtil = new Util();
String data = localUtil.process();
//Core logic of the execute() method that needs to be unit tested...
//Further update data based on logic define in process() method.
data = data + " core logic";
return data;
}
}
public class Util {
public String process(){
String result = "real";
//Use various other objects to arrive at result
return result;
}
}
#Test
public void testLocalVar(){
Util util = new Util();
Assertions.assertEquals("real", util.process());
try (MockedConstruction<Util> mockedConstruction = Mockito.mockConstruction(Util.class);) {
util = new Util();
when(util.process()).thenReturn("mocked method data");
String actualData = util.process();
//This works on local object created here.
Assertions.assertEquals("mocked method data",actualData);
//Object,method under test
MyClass myClass = new MyClass();
actualData = myClass.execute();
//This doesn't not works on local object created inside method under test
Assertions.assertEquals("mocked method data" + " core logic",actualData); //Fails
}
}
Here we are able to define mocked behavior on method on object created locally inside test method. But same thing is not possible with local object created inside actual method under test.
I can see mockedConstruction.constructed() does have every object of class created
But not able to specify mocked behavior on on localUtil.process() inside execute() method ..
Any suggestions..
In your case, you have to stub the behavior when instructing Mockito to mock the constructor. The Mockito.mockConstruction() is overloaded and allows to not only pass the class you want to mock the constructor for:
#Test
void mockObjectConstruction() {
try (MockedConstruction<Util> mocked = Mockito.mockConstruction(Util.class,
(mock, context) -> {
// further stubbings ...
when(mock.process()).thenReturn("mocked method data");
})) {
MyClass myClass = new MyClass();
actualData = myClass.execute();
}
}
You can find further possible stubbing scenarios in this article.
Related
Base class looks like this:
class BaseReport {
constructor(reportName) {
this.reportName = reportName;
}
async generateReport(accountId, request) {
let results = await this.getDataFromDb(request);
results = this.formatResults(results);
return updatedResults;
}
formatResults(data) {
//Some logig here
return result
}
getDataFromDb(request) {
//Logic to get data from database
return errorRequest;
}
}
module.exports = BaseReport;
The I have another class that extends Base class:
const BaseReport = require("./base.service");
class DataReport extends BaseReport {
constructor() {
super('dataReport');
}
formatResults(data) {
//Logic to format results in a different way
return data;
}
}
module.exports = new DataReport();
So far everything works well. DataReport class doesn't need to implement the method getDataFromDB because it is inherited from BaseReport class.
The problem starts when I am writing unit/integration tests.
When I try to stub function getData() it is not working (it is not being stubbed but instead it is calling the method and making the database call.
I understand WHY is happening... In BaseReport class I am exporting the class itself. So when I create an object on my unit tests and try to stub the function, that is not the same method and object which is initialized when the application is running. Every time you use the keyword "new" creates a new object and has its own methods.
If I want to stub a method from DataReport class it works fine because in there I am exporting an object of that class (module.exports = new DataReport();). In this case, it can only exist one copy of the object and so only one copy of the methods as well.
Now I can not do the same for BaseReport class because my understanding is that you CAN NOT extend BaseReport class if I exported a new object (module.exports = new BaseReport();).
What is the proper way to implement this in order to also have the unit tests working??
I am writing test class for my java class. I am using Junit5 with Mockito.
I am using Junit5 which isnt compatible with Power Mockito so I am using Mockito only.
I have class Emp which have function findSalary like below and EmpProfileClient is initialized at constructor.
Class Emp {
......
public void findSalary(empId) {
...
TaxReturn taxReturn = new TaxReturn(EmpProfileClient);
int value = taxReturn.apply(new TaxReturnRequest.withEmpId(empId))
.returnInRupee();
...
}
}
When I am writing the test case, I mocked EmpProfileClient, but since we are creating TaxReturn in a method, How I can mock TaxReturn.apply so I can write the expectation to get the value as per my choice which I set in the test class?
If you want to mock this, the TaxReturn class should be an injected bean in the Emp class. Add an injection framework (like Spring) and inject the TaxReturn class. In the test you write you can inject a Mock instead of the real class. See #InjectMocks annotation of the mockito framework.
If I understood your question correctly(you are looking for mocking taxReturn.apply) I'd suggest next:
First. Refactor your taxReturn instantiation(as it is would be much easier to mock method behavior in comparison for trying to mock local variable)
public class EmpService {
public int findSalary(Integer empId) {
//...
// It's doesn't matter what the actual empProfileClient type is
// as you mocking creation behavior anyway
Object empProfileClient = null;
TaxReturn taxReturn = createClient(empProfileClient);
int value = taxReturn.apply(new TaxReturnRequest().withEmpId(empId))
.returnInRupee();
//...
return value; // or whatever
}
protected TaxReturn createClient(Object empProfileClient) {
return new TaxReturn(empProfileClient);
}
}
Second. Use Mockito.spy() in your test:
class EmpServiceTest {
#Test
void findSalary() {
TaxReturn taxReturn = Mockito.mock(TaxReturn.class);
// this is the main idea, here you using partial EmpService mock instance
// part is mocked(createClient()) and other part(findSalary()) is tested
EmpService service = Mockito.spy(EmpService.class);
when(service.createClient(any())).thenReturn(taxReturn);
when(taxReturn.apply(any(TaxReturnRequest.class))).thenReturn(taxReturn);
int yourExpectedValue = 5;
when(taxReturn.returnInRupee()).thenReturn(yourExpectedValue);
assertEquals(yourExpectedValue, service.findSalary(0));
}
}
Keep in mind that any(), spy(), when() and mock() methods are part of Mockito API. So there is nothing hidden here
I am facing some difficulty in writing a JUnit test for a method with a While loop. My method looks like below:
private void deleteMethod(DeleteRequest dr){
// below statement am calling some service which returns me a object after querying it from Database.
SomeObject ob = db.getdata(dr);
while(ob != null) {
// this method deletes the Data from DB
db.deleteData(ob);
// again calling the same service operation as we did before while loop. I have a situation where my service only returns single record at a time. It is avoidable that I need to do a dirty job.
ob = db.getdata(dr);
}
Below is my JUnit:
#Test
public void testDeleteMethod() throws Exception{
DeleteRequest mockDR = new DeleteRequest();
mockDR.setX(y);
SomeObject mockOB = new SomeObject();
mockOB.setZ(k);
// making a mockcall to the method before I can assert anything
mockClassObject.deleteMethod(mockDR);
}
The above JUnit method is getting struck in execution for ever and I understand that its not getting out of the while loop. How I can approach this issue. Just to mention am using Mockito framework and am not aware if at all there is any way to handle this situation in Mockito.
Using Mockito, you have to mock the db connection and inject it into your service using #InjectMocks, constructor injection, or via a setter before you test your method. This is how I would write your test.
#Test
public void testDeleteMethod() throws Exception{
DeleteRequest deleteRequest = new DeleteRequest();
deleteRequest.setX(y);
SomeObject someObject = new SomeObject();
someObject.setZ(k);
Database db = Mockito.mock(Database.class);
// Notice chain of calls
Mockito.when(db.getdata(deleteRequest))
.thenReturn(someObject).thenReturn(null);
// TODO Inject the Database object into your mockClassObject.
// making a mock call to the method before I can assert anything
mockClassObject.deleteMethod(deleteRequest);
Mockito.verify(db, Mockito.times(2)).getdata(deleteRequest);
Mockito.verify(db).deleteData(someObject);
}
You can see that I chain the calls on the db.getData() method two times, the first time returns someObject and the second time returns null.
In this code, i have mocked the one of the ValidateHandlerSoapClient class method which is instantiated and called this method (soapClientSpy.processSoapRequestRespons) in validateMsisdnHandlerIRSpy.validate().So soapClientSpy.processSoapRequestResponse is not working , instead the real method is called.
#RunWith(PowerMockRunner.class)
#PrepareForTest({ValidateMsisdnHandler.class,ValidateHandlerSoapClient.class})
public class Demo {
MessageControl messageControl=PowerMockito.mock(MessageControl.class);
Validate validate=PowerMockito.mock(Validate.class);
ValidateMsisdnHandlerIR validateMsisdnHandlerIRSpy = PowerMockito.spy(new ValidateMsisdnHandlerIR());
ValidateHandlerSoapClient soapClientSpy = PowerMockito.spy( new ValidateHandlerSoapClient());
#Before
public void initialize() throws Exception
{
PowerMockito.when(validate.getAccountId()).thenReturn("0879221485");
PowerMockito.doReturn(true).when(validateMsisdnHandlerIRSpy, "isPrePaid",anyString());
MemberModifier.field( ValidateMsisdnHandlerIR.class, "endDate").set(
validateMsisdnHandlerIRSpy, "10-FEB-2015");
PowerMockito.when(soapClientSpy.processSoapRequestResponse(anyString())).thenReturn(true);
PowerMockito.whenNew(ValidateHandlerSoapClient.class).withNoArguments().thenReturn(soapClientSpy);
}
#Test
public void testValidateMsisdn_Cr6_Roverprempay_Not_Roverpayg() throws Exception{
Response response = validateMsisdnHandlerIRSpy.validate(validate,messageControl);
}
Replace
ValidateHandlerSoapClient soapClientSpy = PowerMockito.spy( new ValidateHandlerSoapClient())
with
ValidateHandlerSoapClient soapClientMock = PowerMockito.mock(ValidateHandlerSoapClient.class)
A spy by default just calls the methods of the underlying regular class. What you want to do is (presumably) nothing when the methods of the soap client are called.
Then of course you will need to make also change:
PowerMockito.whenNew(ValidateHandlerSoapClient.class).withNoArguments().thenReturn(soapClientMock);
I need some help with this:
Example:
void method1{
MyObject obj1=new MyObject();
obj1.method1();
}
I want to mock obj1.method1() in my test but to be transparent so I don't want make and change of code.
Is there any way to do this in Mockito?
The answer from #edutesoy points to the documentation of PowerMockito and mentions constructor mocking as a hint but doesn't mention how to apply that to the current problem in the question.
Here is a solution based on that. Taking the code from the question:
public class MyClass {
void method1 {
MyObject obj1 = new MyObject();
obj1.method1();
}
}
The following test will create a mock of the MyObject instance class via preparing the class that instantiates it (in this example I am calling it MyClass) with PowerMock and letting PowerMockito to stub the constructor of MyObject class, then letting you stub the MyObject instance method1() call:
#RunWith(PowerMockRunner.class)
#PrepareForTest(MyClass.class)
public class MyClassTest {
#Test
public void testMethod1() {
MyObject myObjectMock = mock(MyObject.class);
when(myObjectMock.method1()).thenReturn(<whatever you want to return>);
PowerMockito.whenNew(MyObject.class).withNoArguments().thenReturn(myObjectMock);
MyClass objectTested = new MyClass();
objectTested.method1();
... // your assertions or verification here
}
}
With that your internal method1() call will return what you want.
If you like the one-liners you can make the code shorter by creating the mock and the stub inline:
MyObject myObjectMock = when(mock(MyObject.class).method1()).thenReturn(<whatever you want>).getMock();
If you really want to avoid touching this code, you can use Powermockito (PowerMock for Mockito).
With this, amongst many other things, you can mock the construction of new objects in a very easy way.
No way. You'll need some dependency injection, i.e. instead of having the obj1 instantiated it should be provided by some factory.
MyObjectFactory factory;
public void setMyObjectFactory(MyObjectFactory factory)
{
this.factory = factory;
}
void method1()
{
MyObject obj1 = factory.get();
obj1.method();
}
Then your test would look like:
#Test
public void testMethod1() throws Exception
{
MyObjectFactory factory = Mockito.mock(MyObjectFactory.class);
MyObject obj1 = Mockito.mock(MyObject.class);
Mockito.when(factory.get()).thenReturn(obj1);
// mock the method()
Mockito.when(obj1.method()).thenReturn(Boolean.FALSE);
SomeObject someObject = new SomeObject();
someObject.setMyObjectFactory(factory);
someObject.method1();
// do some assertions
}
Both mocking of a new instance creation and static methods is possible without PowerMock in the latest mockito versions and junit5.
Take a look in the methods Mockito.mockConstruction() and Mockito.mockStatic().
In your case:
try (MockedConstruction<MyObject> myobjectMockedConstruction = Mockito.mockConstruction(MyObject.class,
(mock, context) -> {
given(mock.method1()).willReturn("some result"); //any additional mocking
})) {
underTest.method1();
assertThat(myobjectMockedConstruction.constructed()).hasSize(1);
MyObject mock = myobjectMockedConstruction.constructed().get(0);
verify(mock).method1();
}
You could avoid changing the code (although I recommend Boris' answer) and mock the constructor, like in this example for mocking the creation of a File object inside a method. Don't forget to put the class that will create the file in the #PrepareForTest.
package hello.easymock.constructor;
import java.io.File;
import org.easymock.EasyMock;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.api.easymock.PowerMock;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
#RunWith(PowerMockRunner.class)
#PrepareForTest({File.class})
public class ConstructorExampleTest {
#Test
public void testMockFile() throws Exception {
// first, create a mock for File
final File fileMock = EasyMock.createMock(File.class);
EasyMock.expect(fileMock.getAbsolutePath()).andReturn("/my/fake/file/path");
EasyMock.replay(fileMock);
// then return the mocked object if the constructor is invoked
Class<?>[] parameterTypes = new Class[] { String.class };
PowerMock.expectNew(File.class, parameterTypes , EasyMock.isA(String.class)).andReturn(fileMock);
PowerMock.replay(File.class);
// try constructing a real File and check if the mock kicked in
final String mockedFilePath = new File("/real/path/for/file").getAbsolutePath();
Assert.assertEquals("/my/fake/file/path", mockedFilePath);
}
}
If you don't prefer to use PowerMock, you may try the below way:
public class Example{
...
void method1(){
MyObject obj1 = getMyObject();
obj1.doSomething();
}
protected MyObject getMyObject(){
return new MyObject();
}
...
}
Write your test like this:
#Mock
MyObject mockMyObject;
#Test
void testMethod1(){
Example spyExample = spy(new Example());
when(spyExample.getMyObject()).thenReturn(mockMyObject);
//stub if required
doNothing().when(mockMyObject.doSomething());
verify(mockMyObject).doSomething();
}
You can do this by creating a factory method in MyObject:
class MyObject {
public static MyObject create() {
return new MyObject();
}
}
then mock that with PowerMock.
However, by mocking the methods of a local scope object, you are depending on that part of the implementation of the method staying the same. So you lose the ability to refactor that part of the method without breaking the test. In addition, if you are stubbing return values in the mock, then your unit test may pass, but the method may behave unexpectedly when using the real object.
In sum, you should probably not try to do this. Rather, letting the test drive your code (aka TDD), you would arrive at a solution like:
void method1(MyObject obj1) {
obj1.method1();
}
passing in the dependency, which you can easily mock for the unit test.