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

Time:03-24

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
}

CodePudding user response:

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.

  • Related