I'm trying to Unit test my MongoDB-based Spring Boot application, and I'm getting a java.lang.NullPointerException: Cannot invoke "blueprint.model.Garage.getCarModel()" because "savedGarage" is null
error. The code is down below:
My Service:
@Service
public class GarageService {
private final GarageRepository garageRepository;
@Autowired
public GarageService(GarageRepository garageRepository) {
this.garageRepository = garageRepository;
}
public void addToGarage(Garage garage) {
Optional<Garage> garageOptional = garageRepository.findByCarModel(garage.getCarModel());
if(garageOptional.isPresent()) {
throw new IllegalStateException("This Car model is already in our garage!");
}
garageRepository.save(garage);
}
public List<Garage> showOurGarage() {
return garageRepository.findAll();
}
public void deleteFromGarage(String id) {
boolean exists = garageRepository.existsById(id);
if(!exists) {
throw new IllegalStateException("A car with id " id " is not in our Garage.");
}
garageRepository.deleteById(id);
}
public void updateCar(String id, String carModel, Integer HP, Integer year, String designer) {
Garage garage = garageRepository.findById(id)
.orElseThrow(() -> new IllegalStateException(
"A car with the id " id " is not in our Garage."));
if(carModel != null && carModel.length() > 0 && !Objects.equals(garage.getCarModel(), carModel)) {
garage.setCarModel(carModel);
}
if(HP != null && !Objects.equals(garage.getHP(), HP)) {
garage.setHP(HP);
}
if(year != null && !Objects.equals(garage.getYear(), year)) {
garage.setYear(year);
}
if(designer != null && designer.length() > 0 && !Objects.equals(garage.getDesigner(), designer)) {
garage.setDesigner(designer);
}
garageRepository.save(garage);
}
}
My Repository:
@org.springframework.stereotype.Repository
public interface GarageRepository extends MongoRepository<Garage, String> {
Optional<Garage> findByCarModel(String carModel);
And here's a test:
@DisplayName("Garage Service Test")
@DataMongoTest
class GarageServiceTest {
@Mock
private GarageRepository garageRepository;
private GarageService garageService;
@BeforeEach
void setUp() {
MockitoAnnotations.openMocks(this);
garageService = new GarageService(garageRepository);
}
@Test
@DisplayName("Add Car To Garage Test")
void testAddToGarage() {
Garage testGarage = new Garage();
testGarage.setId("630ca281f12905d5f5249f08");
testGarage.setCarModel("Shelby Cobra");
testGarage.setHP(485);
testGarage.setYear(1965);
testGarage.setDesigner("Carroll Shelby");
Garage savedGarage = garageRepository.save(testGarage);
String testedGarage = savedGarage.getCarModel();
Optional<Garage> garages = garageRepository.findByCarModel("Shelby Cobra");
assertEquals(testedGarage, garages.toString());
}
I'm pretty much green with Unit testing and MongoDB, so I'm not sure where and why this error happens. I've skipped adding the Controller because I think the rest of the code will be sufficient.
CodePudding user response:
From what I see, garageRepository is instantiated as a mock, so the save method doesn't know at all what to return and returns null by default.
You have to tell Mockito what to return if the save()
method is called.
This can be achieved by Mockito.when(...).thenReturn(...)
.
Your test should test the service, but you don't call a single method of the service!
On the other hand, it doesn't make sense to query the repository for the saved object as the object is actually not saved (again, the repository is a mock, you want to test the service so you want to make sure that the save method of the repository is called correctly). Instead, you should verify that the save() method is called like
Mockito.verify(garageRepository).save(testGarage);
...or if there is an exception due to an existing object with the same id. Try the following code (not tested!):
@DisplayName("Garage Service Test")
@DataMongoTest
class GarageServiceTest {
@Mock
private GarageRepository garageRepository;
private GarageService garageService;
@BeforeEach
void setUp() {
MockitoAnnotations.openMocks(this);
//This would be the way to make the mock return sth.,
//but not really needed
//Garage returnObject = new Garage();
//returnObject.setId("630ca281f12905d5f5249f08");
//returnObject.set...(...);
//Mockito.when(garageRepository.save(Mockito.ANY))
// .thenReturn(returnObject);
garageService = new GarageService(garageRepository);
}
@Test
@DisplayName("Add Car To Garage Test")
//This is the 'positive test, object isn't persisted yet
void testAddToGarage() {
Garage testGarage = new Garage();
testGarage.setId("630ca281f12905d5f5249f08");
testGarage.setCarModel("Shelby Cobra");
testGarage.setHP(485);
testGarage.setYear(1965);
testGarage.setDesigner("Carroll Shelby");
Mockito.when(garageRepository
.findByCarModel(testGarage.getCarModel()))
.thenReturn(Optional.empty());
garageService.addToGarage(testGarage);
Mockito.verify(garageRepository).save(testGarage);
}
@Test
@DisplayName("Add Car To Garage Test")
//This is the 'negative' test, the repository indicates that the object
//is already persisted
@Expected(IllegalStateException.class)
void testAddToGarageAlreadyPersisted() {
Garage testGarage = new Garage();
testGarage.setId("630ca281f12905d5f5249f08");
testGarage.setCarModel("Shelby Cobra");
testGarage.setHP(485);
testGarage.setYear(1965);
testGarage.setDesigner("Carroll Shelby");
Mockito.when(garageRepository
.findByCarModel(testGarage.getCarModel()))
.thenReturn(Optional.of(testGarage));
garageService.addToGarage(testGarage);
Mockito.verify(garageRepository, Mockito.never()).save(testGarage);
}
}