0

I am currently trying to test a method using Mockito and I am stuck on a certain method.

For example, there are classes called Car and CarSystem.

The car class contains two parameters, Name(String) and its License Plate(String) and their getter/setter methods.

The method I am trying to test using Mockito is a method called, addCar which is in the CarSystem class.

CarSystem class:

private final List<Car> cars= new ArrayList<>();

public Car addCar(String name, String plateNumber) throws IllegalStateException {
        Car clash = getCar(plateNumber);
        if (null != clash) throw new IllegalStateException("Car with that Number already exists");

        Car car = new Car(name, plateNumber);
        cars.add(car);

        return car;
    }


public Car getCar(String match) {
        for (Car car: cars) {
            if (car.getPlateNumber().equals(match)) return car;
        }

        return null;
    }

What I am trying to test is the two things:

  1. It successfully throws an IllegalStateException exception when there is a car with the plate number that already exists in the car list.

  2. It successfully adds a car to the list.

What I did solely using JUnit is:

@Test
    public void testAddCar_ThrowingException() {

        try {

            CarSystem sys = new CarSystem();
            Car car = sys.addCar("1234", "Toyota123");
            Car car1= sys.addCar("1234", "Honda123");
            Assert.fail();
        } catch(IllegalStateException e) {
            System.out.println(e);
        }
    }

@Test
    public void testAddCar() {

            CarSystem sys = new CarSystem();
            Car car = sys.addCar("1234", "Toyota123");
            Assert.assertEquals(sys.getCar(1234).getName(), "Toyota123");
    }

But I have no idea how to test them using Mockito... Can anyone please help me or give me a hint about this?

P.S) I can freely change the content of the classes for Mockito test.

11
  • 1
    Your test looks perfectly fine. I don't understand why you would want to use Mockito for this. What are you hoping to achieve? Commented Apr 12, 2021 at 12:39
  • 1
    You dont use mocking frameworks because you can. You use them when you need them. And: if you can fully test your code without using a mocking framework, then you always prefer that! Mocking frameworks add another dependency, and complexity to your code base. If you want to understand WHY you have to use them sometimes, dont start with your current code. Pick up a good tutorial on Mockito, and read how it is SUPPOSED to work. Commented Apr 12, 2021 at 12:40
  • BTW, your first test can be cleaned up with @Test(expected=IllegalStateException.class). Then you don't need the manual Assert.fail(), or the try-catch. Commented Apr 12, 2021 at 12:40
  • 1
    Why are you returning null in both getCar() and addCar()? Commented Apr 12, 2021 at 12:40
  • I just want to test them independently. The test suite I wrote is for the CarSystem class, and what I am trying is to isolate the test, not making it dependent on Car class. Commented Apr 12, 2021 at 12:43

1 Answer 1

1

I would not create the Car to be added inside the method but have it passed in as a parameter, ie

interface CarService {
  Car addCar(Car toAdd);
  Car getCar(String plateNumber);
}

Since you are testing the service itself, you cannot mock it - only the car. Since the Car's only data you access is getPlateNumber, a test would look something like this:

class ScratchTest {
    private CarService sut;

    @Before
    public void before() {
        sut = new CarService();
    }

    @Test
    public void addSucceeds() {
        String plateNumber = "new";
        Car toAdd = mock(Car.class);
        when(toAdd.getPlateNumber()).thenReturn(plateNumber);
        Car added = sut.addCar(toAdd);

        // assert return value
        assertSame(toAdd, added);
        // assert car now available via get
        Car stored = sut.getCar(plateNumber);
        assertSame(toAdd, stored);
    }

    @Test(expected = IllegalArgumentException.class)
    public void addFails() {
        Car existing = mock(Car.class);
        String plateNumber = "existing";
        when(existing.getPlateNumber()).thenReturn(plateNumber);
        sut.addCar(existing);
        Car toAdd = mock(Car.class);
        when(toAdd.getPlateNumber()).thenReturn(plateNumber);

        sut.addCar(toAdd);
    }
}

You cannot really test add and get independently, because they are invariants of each other: 1. If add is called with a car with a new plate number, a subsequent get given that plate number will retrieve it, and 2. If get is called with the plate number of a Car for which add has been called before, it will be returned.

Sign up to request clarification or add additional context in comments.

3 Comments

Thank you so much Daniu!! BTW should I add verify() test within public void addSucceeds()? or should I test for verification seperately? Also, is there anyway that I can use dependency injection for making the code more testable?
@seung You could use verify but I don't see the point; you could inject the list, but that's really a different question. If you were to do so, it would make more sense to use verify (on the injected list to make sure that add has been called).
What do you mean by injecting the list? Could you please give me more details?

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.