简体   繁体   中英

How to mock required related entity?

In my problem I want to test PostService class. Every new added post have to have Book (entity) assigned to it first. There is relation OneToMany (one book can have many posts). So if you want to add new post it will be like:

post: {
   "title": "postTitle1",
   "content": "Content of post",
   "bookId": "1"
}

So my savePost method is:

@Transactional
public Post savePost(Post post) {
    Optional<Book> bookOpt = Optional.ofNullable(post.getBook());
    Book bookInPost = bookOpt.orElseThrow(() -> new IllegalArgumentException("Book id for new Post cannot be null!"));
    Book book = bookService.getBook(bookInPost.getId());
    book.addPost(post);
    return postRepository.save(post);
}

Now I want to test this method. How can I mock Book inside Post without inject bookRepository/bookService and without really saving it? Book is also nested (have categories, authors etc.) so it will be hard to make if from 0 every time. I tried something like this:

    @Test
    void should_save_post_assigned_to_book() {
        //given
        Post post = new Post("Title 1", "Content 1");
        Book bookToReturnFromRepository = new Book();
        bookToReturnFromRepository.setId(2);
        //when
        when(mockedBookRepository.findById(any())).thenReturn(Optional.of(bookToReturnFromRepository));
        postService.savePost(post);
        //then
        Optional<Post> loaded = postRepository.findById(post.getId());
        assertAll(() -> {
            assertThat(loaded).isPresent(); ...

But it is obviously wrong. What should I do?

You must first identify what you need to test. While testing savePost there are two branches: One where the post doesn't have a book and the method trows exception and the other where the information is all there and the post is saved.

With that in mind, the first test would be to verify that an exception is thrown when an incomplete post is received. Here would be nice to also verify nothing is saved ( postRepository.save() is never called)

For the second test you'll want to verify that book.addPost() was called and that postRepository.save(post) was called with the initial post.

For all that, having test double (mock) for postRepository is essential. Assuming you are using Mockito or something similar:

@Test(expected = IllegalArgumentException.class)
public void postWithoutBook() {

    // Given a post that doesn't have a book
    final Post post = mock(Post.class);
    when(post.getBook()).thenReturn(null);

    // When trying to save it, we expect an exception to be thrown.
    realPostRepository.savePost(post);

    verify(mockedPostRepository, never()).save(any());
}

@Test
public void postWithBook() {
    // Given a post that does have a book
    final Book book = mock(Book);
    final Post post = mock(Post.class);
    when(post.getBook()).thenReturn(book);

    // When trying to save it...
    realPostRepository.savePost(post);

    // We expect it to be saved.
    verify(mockedPostRepository, times(1)).save(post);
    // And assigned  to a book
    verify(book, times(1)).addPost(post);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM