如何使用@MockBean 和 MockMvc 通过 HTTP POST 将一些数据附加到模拟存储库

How to use @MockBean and MockMvc to append some data with HTTP POST to a mocked repository

我有一个带有 POST 动作的控制器,它创建了一个新的 BLOGPOST 和 return 所有 BLOGPOSTS 包括新创建的:

@Autowired
private BlogPostInMemRepository bpr;

@RequestMapping(method = RequestMethod.POST, path="/post",
        consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
public @ResponseBody List<BlogPost> addPost(BlogPost post) {
    bpr.addPost(post);
    return bpr.getAllPosts();
}

BlogPostInMemRepository 代码如下所示:

@Repository
public class BlogPostInMemRepository {

    private List<BlogPost> posts = new ArrayList<BlogPost>(){{
        add(new BlogPost(1, "Author1", "Post 1"));
        add(new BlogPost(2, "Author2", "Post 2"));
    }};

    public List<BlogPost> getAllPosts(){
        return posts;
    }

    public void addPost(BlogPost post){
        posts.add(post);
    }
}

我的目标是使用@MockBean 和 MockMvc 对控制器进行单元测试。步骤为:

我目前的测试:

@Test
public void post_whenValidParametersPassed_addsAndReturnsAllBlogPostsSuccessfully() throws Exception {
    // given
    BlogPost bp1 = new BlogPost(1, "John", "Post 1");
    BlogPost bp2 = new BlogPost(2, "Jack", "Post 2");
    List<BlogPost> posts = new ArrayList<BlogPost>(){{ add(bp1); add(bp2); }};
    given(repo.getAllPosts()).willReturn(posts);

    mockMvc.perform(post("/api/v1/post")
        .contentType(APPLICATION_FORM_URLENCODED)
        .param("id", "33")
        .param("author", "Gibraltar")
        .param("post", "There is now way!"))
        .andExpect(status().isOk())
        .andExpect(content().string("{... the whole string ...}"))
        .andDo(print());
}

我得到的只是传入的 posts:given(repo.getAllPosts()).willReturn(posts); - 这当然是预期的。

问题:如何实际注入 BLOGPOSTS 的初始集,使用 POST 添加一个并从模拟存储库中取回所有这些?

如果您打算模拟存储库,那么遵循您的方法并没有多大意义,因为 addPost 不会有任何效果,而 getAllPosts 只会假设它已被添加。这似乎有点做作,并没有带来任何实际价值testing-wise。

我在这里要做的是使用一个简单的in-order验证:

InOrder inOrder = Mockito.inOrder(brp);
inOrder.verify(brp).addPost(any(Post.class));
inOrder.verify(brp).getAllPosts();

所以要确保 post 在从存储库中获取所有这些之前添加。

通过使用 doCallRealMethod()when().thenCallRealMethod() 解决了它 - 这似乎是使用 Mockito 从 "down-below (bottom-up)" 注入控制器数据的唯一方法,因为直接设置器不适用于 @MockBean的。

代码:

@Test
public void post_whenValidParametersPassedAndPreExistingBlogsPresent_addsAndReturnsAllBlogPostSuccessfully() throws Exception {
    // given : initialize mock data
    BlogPost bp1 = new BlogPost(1, "John", "Post 1");
    BlogPost bp2 = new BlogPost(2, "Jack", "Post 2");
    List<BlogPost> posts = new ArrayList<BlogPost>(){{ add(bp1); add(bp2); }};

    // given : prep the mocked object
    doCallRealMethod().when(repo).setPosts(posts);
    doCallRealMethod().when(repo).addPost(any(BlogPost.class));
    repo.setPosts(posts);
    when(repo.getAllPosts()).thenCallRealMethod();

    mockMvc.perform(post("/api/v1/post")
            .contentType(APPLICATION_FORM_URLENCODED) //from MediaType
            .param("id", "33")
            .param("author", "Gibraltar")
            .param("post", "There is now way!"))
            .andExpect(status().isOk())
            .andExpect(content().string("[" +
                    "{\"id\":1,\"author\":\"John\",\"post\":\"Post 1\"}," +
                    "{\"id\":2,\"author\":\"Jack\",\"post\":\"Post 2\"}," +
                    "{\"id\":33,\"author\":\"Gibraltar\",\"post\":\"There is now way!\"}" +
                "]"))
            .andDo(print());
}