A good read about mocking from another answer.
The point of using the mock with its methods is to isolate it from its dependencies. Mocking arguments may seem dirty or un-fancy, but it's designed to allow you to test the expectations of the mocked class. In this case, you assume that whatever your service ClassB is, works as intended (or not, if you expect exceptions as well). In this case, we are testing a single unit ClassA and assume that ClassB is correct.
I'll go ahead and make a more concrete example, similar to the waiter/chef example above.
Let's say your ClassA is a CookingPot and your ClassB is a Pantry. Any time you interact with the Pantry to getPasta, it tries to send you Pasta. If it has no Pasta, it returns null.
Pantry is a fancy Pantry. It has a table storing its contents, so you don't know if it has or doesn't have Pasta. This is "complex" in the real world, but this isn't our Pantry. We have no idea how it works, just that it either returns Pasta or it doesn't. We don't know the logic, nor do we have to. We know what it does, so if we want to make sure we can add pasta to our pot, we can use a mock Pantry that acts the same way, without having the fancy bells whistles the real one does.
Pantry also has a bunch of other methods, such as allowing you to add your leftover Pasta back to the shelf. You have no idea how the smart Pantry puts the pasta back in, but you don't need to model that, because Mockito is fine with void calls regardless.
If ClassB were to change, these tests might still pass, but you'd catch the issue in integration tests and say "hmm, my class still works, but something is up with the new Pantry so I need to redo the class, and rewire the tests."
CookingPot {
void addPasta(Pantry pantry) {
Pasta p = pantry.getPasta();
System.out.println("Added " + p.toString() + " to the pot!");
//fancy actions that use up some of the Pasta
pantry.add(p); //return the pasta
}
}
Pantry {
Pasta getPasta(); //we have no idea what this does in actuality
}
@Test(expected = NullPointerException.class)
public void testAddPastaButNoPastaLeft() {
Pantry mockPantry = mock(Pantry.class);
CookingPot cookingPot = new CookingPot();
cookingPot.addPasta(mockPantry);
//we should probably have checked if there was pasta before
//attempting to add imaginary pasta
}
@Test
public void testAddPasta() {
Pantry mockPantry = mock(Pantry.class);
when(mockPantry.getBaz()).thenReturn(new Pasta());
CookingPot cookingPot = new CookingPot();
cookingPot.addPasta(mockPantry);
}