Skip to content
Advertisement

How do I write a test for a service that uses other services in Springboot?

I have a BookManagementService that uses the @Autowired implementation of three other services, like so

@Service
public class BookManagementService {

    private final BookRepo repo;
    private final BookItemRepo itemRepo;
    private final BookEditionRepo editionRepo;
    // the below services are the ones I want to mock in the test.
    @Autowired AuthorService authorService;
    @Autowired YearService yearService;
    @Autowired GenreService genreService;


    static String position = "1000";

    public BookManagementService(BookRepo repo, BookItemRepo itemRepo, BookEditionRepo editionRepo,
    YearService yearService) {
        this.repo = repo;
        this.itemRepo = itemRepo;
        this.editionRepo = editionRepo;
    }
    // Rest of the methods that perform the business logic.
}

So how do I mock the aforementioned services and their repo in the BookManagementServiceTest? When the test start running and it gets to the yearService layer, it throws a NullPointerEXception cause the year it receives is null The BookManagementServiceTest

@SpringBootTest
@ExtendWith(MockitoExtension.class)
class BookManagementServiceTest {

    // Injects the needed services
    @InjectMocks private BookManagementService service;
    @InjectMocks private YearService yearService;
    @InjectMocks private GenreService genreService;
    @InjectMocks private AuthorService authorService;

    // Mock the needed repos
    @Mock private BookItemRepo repoItem;
    @Mock private BookEditionRepo repoEdition;
    @Mock private BookRepo repo;
    // External repo
    @Mock private BookYearRepo yearRepo;
    @Mock private GenreRepo genreRepo;
    @Mock private AuthorRepo authorRepo;

    @BeforeEach
    void setUp() {
        // instantiate the injected services
        service = new BookManagementService(repo, repoItem, repoEdition, yearService);
        yearService = new YearService(yearRepo);
        genreService = new GenreService(genreRepo);
        authorService = new AuthorService(authorRepo);
       // setting the needed variables

       // calling when.thenReturn for all the repos like I would in a normal single class test.

        lenient().when(yearRepo.findByYear("2006")).thenReturn(year);
        lenient().when(yearRepo.save(year)).thenReturn(year);
        lenient().when(repoItem.save(item)).thenReturn(item);
        lenient().when(yearService.create("2006", edition)).thenReturn(year);

        lenient().when(repoEdition.save(edition)).thenReturn(edition);
        lenient().when(repo.save(book)).thenReturn(book);
    }
    // Tests
}

Advertisement

Answer

You just have to mock all the related services aswell.

Take this as an example, imagine you want to test an ItemService class, that has ItemRepositoy and AuthService autowired.

ItemService.java

@Service
public class ItemService {

    @Autowired
    private ItemRepository itemRepository;

    @Autowired
    private AuthService authService;

    public Item fetchItem() {
        return new Item(1, "name", 10, 100);
    }

    public List<Item> fetchItems() {
        List<Item> items = itemRepository.findAll();
        Boolean isValidItems = authService.checkItems();
        if (isValidItems) items.forEach((item) -> item.setValue(item.getPrice() * item.getQuantity()));
        return items;
    }
}

Item.java

@Entity
public class Item {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    private String name;
    private Integer price;
    private Integer quantity;

    // getters, setters and constructors...
   
}

AuthService.java

@Service
public class AuthService {

    public Boolean checkItems() {
        return true;
    }
}

ItemRepository.java

public interface ItemRepository extends JpaRepository<Item, Integer> {
}

ItemServiceTest.java

@ExtendWith(MockitoExtension.class)
public class ItemServiceTest {

    @InjectMocks
    private ItemService itemServiceMock;

    @Mock
    private ItemRepository itemRepositoryMock;

    @Mock
    private AuthService authServiceMock;

    @Test
    public void fetchItems_basic() {
        // arrange
        List<Item> items = Arrays.asList(new Item(1, "first", 10, 100), new Item(2, "second", 20, 200));
        Integer expectedResultFirst = 1000;
        Integer expectedResultSecond = 4000;
        when(itemRepositoryMock.findAll()).thenReturn(items);
        when(authServiceMock.checkItems()).thenReturn(true);

        // act
        List<Item> actualResult = itemServiceMock.fetchItems();

        // assert
        assertEquals(expectedResultFirst, actualResult.get(0).getValue());
        assertEquals(expectedResultSecond, actualResult.get(1).getValue());
    }

}

If you don’t mock the autowired classes and set the when().thenReturn() as you expect you will always get a NullPointException.

5 People found this is helpful
Advertisement