I'm trying to test a service class (responsible for calling the repository layer and do some operations if needed), basically, this is the class I'm trying to test
class CarServiceImpl{
public Car findById(String id){
//call repository layer to find a car
}
public void deleteById(String id){
Car car = this.findById(id);
if(car != null){
//Call repository layer to update the car
}else{
Throw NotFOundException();
}
}
}
As you can see I call the findById method on the deleteById method, so my questions are.
is it really code smell to call a method on the same class? I don think I should create a separate class to find a car by id.
how can I mock the call to "findById" on the "deleteById" method, if I use Mockito.when(carServiceImpl.findById("car1")).thenReturn(carModel);
it stills call the method so i'll need to mock the call to respository for finding by id too, even when i already tested the method findById.
We can mock runInGround(String location) method inside the PersonTest class as shown below. Instead of using mock(class) here we need to use Mockito. spy() to mock the same class we are testing. Then we can mock the method we want as follows.
Mock Objects are a Code Smell But mock objects are more often smelly because they are telling you something about the system under test. A Code Smell is defined as “a hint that something has gone wrong somewhere in your code”.
Mocking is a very common testing mechanism, and it is a bad idea. This post details why you should not use mocking, and why and how you should write integration tests instead. TL;DR: Mocking provides false confidence by hiding real failures.
You can either mock the external calls that happen within the getTyreSpecification method or you can pull that method out into its own class, wrapped in an interface, and inject the interface into your Selecter class. That would allow you to mock it.
This isn't necessarily a smell and you can partially mock Car
like so:
String carId = "...";
Car car = ...;
CarServiceImpl car = mock(CarServiceImpl.class);
when(car.findById(carId)).thenReturn(car);
when(car.deleteById(carId)).thenCallRealMethod();
But if you can allow deleteById()
to execute the 'real method' then your test must already have a repository in which case letting findById()
be a 'real call' is simple and improves the quality of your test coverage at ~no additional cost. The fact that you have already tested findById()
doesn't mean that you shouldn't test it again, indirectly, as part of deleteById()
.
I would suggest that you do either or both of the following:
Car
by giving it a mocked repository
and using mocked expectations and verifications to test all of its methodsCar
by giving it a real repository and using real invocations on the underlying store to assert actual results for each of its methodsOn a separate note, I'm guessing the idea of injecting a repository into a domain object is a deliberate use of the "active record" pattern where your entities know how to CRUD themselves. This could be considered a code smell; it breaches the SRP and could be held to be a poor separation of concerns because the domain object knows about two things: its own state and how to persist itself.
You want your test setup and test code to be as "minimalistic" as possible. In that sense, the other answer is correct in saying: if you can test deleteById()
without a special setup then go for that.
And would it turn out that findById()
is by itself a "huge" thing that requires a lot of test setup - then I would rather step back and consider putting the content of this method into a distinct class - some kind of CarIdentityService
.
Meaning: very often when we start making test code more complex - the better answer is to step back and change the design of our production code. In your case, you might want to push the whole findById()
code into a distinct class - and now you can simply mock that finder object within your CarService
class.
And just for the record: CarIdentityService
could be a local or inner class of CarService
. But introducing it might allow you to streamline you implementation and avoid getting into the spy business.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With