1

I'm trying to use the when....then functions of mockito. And I'm wondering if it's possible to put in a list for the then portion but for it to iterate through the list and return a different object. So if I had a list with 3 objects: A, B, C

I want to be able to iterate through the list and return the next object in the list. So if the method inside of when gets called 3 times, the first time it would return A, the second time B, and the third time C. Here's my current code:

public class fooTest  {
    private Foo foo;
    private List<Car> cars;
    private TestHelper helper;
    @Mock
    private DomainService service;
    private Car car;

    @Before
    public void setUp() throws Exception {
        MockitoAnnotations.initMocks(this);     
        foo = new Foo();
        helper = new TestHelper();
        cars = new ArrayList<Car>();
        Mockito.when(service.findObjectByID(any())).thenAnswer( AdditionalAnswers.returnsElementsOf(cars) );
    }

    @Test
    public void testVin() {
        cars = helper.generateCars(5, /*random number*/);
        Fleet result = foo.setFleet(Arrays.asList( helper.makeCars(3) );
        assertEquals(/*list of numbers*/, result.getCars().getVINs());
    }
}

public class TestHelper {
    public Fleet makeCars(int numCars) {
        Fleet fleet = new Fleet();
        Car car;
        for(int i=0; i<numCars; i++) {
            car = new Car(i, /*some vin number*/);
            fleet.add(car);
        }       

        return fleet;
    }
    public List<Car> generateCars(int numCars, int vinNum) {
        //implementation
    }
}

public class Foo {
    private DomainService domainService;

    // Constructor to initialize 

    public Fleet setFleet(List<Car> cars) {
        ....
        Optional<Vehicle> vehicle = domainService.findObjectByID(/*Some number*/);
        ....
    }
}

public class DomainService {
    Optional<Vehicle> findObjectByID(long id) {
        //implementation
    }
}

Vehicle is the class Car extends. My problem is every time findObjectByID gets called the vehicle object is null. That makes sense because the cars object is initialized as an empty list and the mockito whenfunction uses that as the return list instead of the populated one made in the test function. I also get this runtime error:

java.lang.ClassCastException: com.jer.test.model.Car cannot be cast to com.google.common.base.Optional

But I've changed my code and tried it this way as well:

@Before
public void setUp() throws Exception {
}

@Test
public void testVin() {
    cars = helper.generateCars(5, /*random number*/);
    Mockito.when(service.findObjectByID(any())).thenAnswer( AdditionalAnswers.returnsElementsOf(cars) );
    Fleet result = foo.setFleet(Arrays.asList( helper.makeCars(3) );
    assertEquals(/*list of numbers*/, result.getCars().getVINs());
}

But the vehicle object is still null. I have no idea why. Any place I've gone wrong?

Re-attempt 1: I've tried the following with no luck:

@Before
public void setUp() throws Exception {
    MockitoAnnotations.initMocks(this);     
    foo = new Foo();
    helper = new TestHelper();        
}

@Test
public void testVin() {
    cars = helper.generateCars(5, /*random number*/);
    Mockito.when(service.findObjectByID(any())).then( new ReturnsElementsOf(bFIs) );
    Fleet result = foo.setFleet(Arrays.asList( helper.makeCars(3) );
    assertEquals(/*list of numbers*/, result.getCars().getVINs());
}

Re-attempt 2: I've tried the following with some luck:

@Before
public void setUp() throws Exception {
    MockitoAnnotations.initMocks(this);     
    foo = new Foo();
    helper = new TestHelper();
    cars = new ArrayList<Car>();
    cars.add(new Car(1, 2));
    Mockito.when(service.findObjectByID(any())).thenAnswer( AdditionalAnswers.returnsElementsOf(cars) );
}

@Test
public void testVin() {
    cars = helper.generateCars(5, /*random number*/);
    Fleet result = foo.setFleet(Arrays.asList( helper.makeCars(3) );
    assertEquals(/*list of numbers*/, result.getCars().getVINs());
}

And this seems to work. The vehicle object returned is the cars object made in the setUp class but this isn't really what I want because I want the return to be from a list or different car objects.

Re-attempt 3: I've tried Florian Schaetz's suggestion but I'm still getting null returned when findObjectByID is called:

@Before
public void setUp() throws Exception {
    MockitoAnnotations.initMocks(this);     
    foo = new Foo();
    helper = new TestHelper();

}

@Test
public void testVin() {
    cars = helper.generateCars(5, /*random number*/);
    for(Car car : cars) {
        Mockito.when(service.findObjectByID(any())).thenReturn( Optional.of(car) );
    }
    Fleet result = foo.setFleet(Arrays.asList( helper.makeCars(3) );
    assertEquals(/*list of numbers*/, result.getCars().getVINs());
}

1 Answer 1

3

It's actually quite simple to return different things for sequentiel invocations...

Mockito.when( <some mock invocation>).thenReturn( a ).thenReturn( b).thenReturn( c );

This will return a for the first invocation, b for the second and c for all the others that follow.

If you don't know how many, you could either use an Answer like you tried (simple enough), but could probably also wrap it like this...

OngoingStubbing<T> stubbing = Mockito.when ( <something> );
for(Object obj : array) {
 stubbing = stubbing.then( obj );
}

On the other hand, I don't really see why you need to write a UnitTest where you don't know what the return values will be... There's a certain code smell to that, if you ask me.

Random testing might be a good idea for some things, but I wouldn't use it for stubbing, honestly.

Anyway, why do you use Mockito.when(service.findObjectByID(any())) at all? Isn't that the source of your problems? If you have to create random cars (I doubt it, though), it would be easy to write...

List<Car> cars = helper.generateCars(5, /*random number*/);
for(Car car : cars) {
    Mockito.when(service.findObjectByID(car.getId())).thenReturn(car);
}
Sign up to request clarification or add additional context in comments.

7 Comments

right that's the idea but that's not what I want. As you can see in the code above I'm trying to return the objects from an array. I don't know how big the array will be because the test functions set those in the generateCars method.
Why don't you simple write a method that calls the thenReturn method of the previous result? I'll edit the answer...
so would that above studding code go in the setup function? And what would the T in the OngoingStubbing be? Car?
The return value, but that will be implicit as soon as you put something in the then, so you will not have to write it. And no, it doesn't make sense in your before method, since the list of cars is empty there. You would have to do that in each method. But honestly, I still don't see any need for a random amount of cars there. And neither do I see the need to return them car by car. Why can't you give explicit ids instead of any() and then return them via when ( foo( someId ) ).thenReturn(someCar); You can even wrap that into a for loop more easily.
edited the post with changes. Still am unable to return the car from the list. However, I just want to clarify that the number of cars being generated is not random. If you see the method signature for generateCars then you'll see that the first argument is the number of cars which the test functions pass as a known number.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.