Home > Mobile >  Error in tests using mock: Wanted but not invoked:
Error in tests using mock: Wanted but not invoked:

Time:06-22

I try to write integration tests for my Spring boot controller. Here is my controller method which I want to test:

    @GetMapping("/")
    public String getAllBooks(Model model, @RequestParam("page") Optional<Integer> page, @RequestParam("size") Optional<Integer> size) {
        int currentPage = page.orElse(FIRST_PAGE);
        int pageSize = size.orElse(DEFAULT_PAGE_SIZE);

        Page<Book> bookPage = bookService.getAllBooks(PageRequest.of(currentPage - 1, pageSize));
        Set<String> categories = bookService.findAllCategories();

        model.addAttribute("categories", categories);
        model.addAttribute("currentPage", currentPage);
        model.addAttribute("books", bookPage);

        int totalPages = bookPage.getTotalPages();
        if (totalPages > 0) {
            List<Integer> pageNumbers = IntStream.rangeClosed(1, totalPages)
                    .boxed()
                    .collect(Collectors.toList());
            model.addAttribute("pageNumbers", pageNumbers);
        }

        return "library";
    }

And here is my class with tests:

@SpringBootTest
@AutoConfigureMockMvc
public class BookControllerTests {

    @Autowired
    private MockMvc mockMvc;
    @Mock
    BookService bookService;
    @Mock
    BookRepository bookRepository;

    private final String category = "forTest";

    final Book book = new Book("test1", 9999, "testAuthor1", 1, "test1");

    @BeforeEach
    public void initEach() {
        bookService.addNewBook(book, category, "not used");
    }

    @AfterEach
    public void deleteEach() {
        bookRepository.deleteAll();
    }

    @Test
    void getAllBooksTest() throws Exception {
        mockMvc.perform(get("/")
                        .param("page", "1")
                        .param("size", "20"))
                .andExpect(status().isOk());

        ArgumentCaptor<Pageable> pageableCaptor = ArgumentCaptor.forClass(Pageable.class);

        verify(bookRepository).findAll(pageableCaptor.capture());
        PageRequest pageable = (PageRequest) pageableCaptor.getValue();

        assertThat(pageable).hasPageNumber(1);
        assertThat(pageable).hasPageSize(20);
    }
}

I wrote custom assertThat methods, which looks like:

class PageableAssert extends AbstractAssert<PageableAssert, Pageable> {

    PageableAssert(Pageable pageable) {
        super(pageable, PageableAssert.class);
    }

    static PageableAssert assertThat(Pageable actual) {
        return new PageableAssert(actual);
    }

    void hasPageSize(int expectedPageSize) {
        if (!Objects.equals(actual.getPageSize(), expectedPageSize)) {
            failWithMessage("expected page size to be <%s> but was <%s>", expectedPageSize, actual.getPageSize());
        }
    }

    void hasPageNumber(int expectedPageNumber) {
        if (!Objects.equals(actual.getPageNumber(), expectedPageNumber)) {
            failWithMessage("expected page number to be <%s> but was <%s>", expectedPageNumber, actual.getPageNumber());
        }
    }

    PageableAssert hasSort(String field, Sort.Direction direction) {

        Sort.Order actualOrder = actual.getSort().getOrderFor(field);

        if (actualOrder == null) {
            failWithMessage("expected sort for field <%s> to be <%s> but was null", field, direction);
        } else if (actualOrder.getDirection() != direction) {
            failWithMessage("expected sort for field <%s> to be <%s> but was <%s>", field, direction, actualOrder.getDirection());
        }

        return this;
    }
}

But as I correctly understand my problem appears in this line: verify(bookRepository).findAll(pageableCaptor.capture()); so my custom asserts cannot influence the occurrence of this error which looks like: Wanted but not invoked: bookRepository.findAll(); -> at kpi.diploma.ovcharenko.controller.BookControllerTests.getAllBooksTest(BookControllerTests.java:58) Actually, there were zero interactions with this mock. Can you help me with this problem, because I totally don't know how I can fix it.

CodePudding user response:

In your test, you're verifying that bookRepository.findAll was invoked, but in the method you're testing, bookRepository isn't even referenced.

What you want to verify is that bookService.getAllBooks and bookService.findAllCategories are being invoked.

If you want to verify that your bookRepository invocations are being called correctly, write a test around the bookService methods, as I assume this is where those calls are made.

Also, I should add that since your method under test uses a response from those mocks, you need to create a when()->then() statement to ensure that those calls return some data, otherwise you're going to get a null pointer exception when it reaches this line: int totalPages = bookPage.getTotalPages();

Something like,

Page<Book> bookPage =  Mockito.mock(Page.class);
when(bookService.getAllBooks(any()).thenReturn(bookPage);
  • Related