Home > database >  How to make Mockito call different method but return same object instance
How to make Mockito call different method but return same object instance

Time:07-05

I have the following methods

//my method
public Car polishCar(Car car){
    car.setPolish(true);
    car.setPolishDate(new Date());
    
    return car;
}

//myMethod
public Car washCar(Car car){
    car.setWash(true);
    car.setWashDate(new Date());
    
    return car;
}

// mainMethod
public Car serviceCar(){
    Car car = new Car();
    myMethod.polishCar(car);
    myMethod.washCar(car);
    ......

    return Car;
}

This is what I have code for my unit test with mockito, I need to create 2 different Car objects which look redundant and keep adding new value to it.

Car pCar = new Car();
pCar.setPolish(true);
pCar.setPolishDate(new Date());

//first call
when(myMethod.polishCar(any(Car.class))).thenReturn(pCar);

Car wCar = new Car();
wCar.setPolish(true);
wCar.setPolishDate(new Date());
wCar.setWash(true);
wCar.setWashDate(new Date());

//second call with same car object from previous, and return same car object with additional value
when(myMethod.washCar(eq(pCar))).thenReturn(wCar);

Car testCar = mainMethod.serviceCar();

// check if they are same instance
assertEquals(testCar, wCar);
assertEquals("true", wCar.getWash());
assertEquals("true", wCar.polish());

How can I better handle this scenario to make mockito return the same & updated object (same instance) and keep passing to the next method call?

So the main point is I want to have 1 car object and this car object will pass to number of method to update this car object.

// Create 1 car object
Car singleCar = new Car();

//pass to first method, and return singleCar with updated polish & polishDate
when(myMethod.polishCar(eq(singleCar))).thenReturn(singleCar);

//pass to second method, and return singleCar with updated wash and washDate
when(myMethod.washCar(eq(singleCar))).thenReturn(singleCar);

// call main method for testing
Car testCar = mainMethod.serviceCar();

// assert testCar == singleCar
// assert singleCar is updated (wash == true & polish == true) 

Is there any way to reuse the same object here ?

Thank you.

CodePudding user response:

There is no sense to use mock in this case. Just create a real SUT and the real car. Simply call the test method for the real car instance and directly verify the car 's state.

@Test
public polishAndCarTest(){
    
   Car car = new Car();

   Date now = new Date();
   sut.polishCar(car);
   sut.washCar(car);

   assertThat(car.isPolish()).isTrue();
   assertThat(car.getPolishDate()).isCloseTo(now,2);
   assertThat(car.isWash()).isTrue();
   assertThat(car.getWashDate()).isCloseTo(now,2);

}

Notes :

  • It is better that you can refactor polishCar() and washCar() such that it can accept a given date as the input argument rather than hardcode it as the current time . After that , you can pass a testing date when calling these methods in the test case and can easily verify the polish date and wash date is really updated to this date.

  • If you don't like this , you can use assertj to verify these date is close to within 2 millisecond after the moment when polishCar() / washCar() is called which is fine enough to me.

CodePudding user response:

Use any Matcher like any(ParamClass.class).

In your case when(myMethod.washCar(any(Car.class))).thenReturn(wCar);

CodePudding user response:

You can use an ArgumentCaptor to capture the argument(your mock instance pCar). Then use that value in your test. Below is pseudo code but essentially how argueMent captor works

@Mock
Car pcar;
@Captor
ArgumentCaptor<Car> carCaptor;



when(myMethod.polishCar(
any(Car.class))).thenReturn(pCar);

 Mockito.verify(car)
.polishCar(carCaptor.capture());

The captured argument would be the pCar which you can then use through out the test

  • Related