如何使用@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 对控制器进行单元测试。步骤为:
- 模拟 BlogPostInMemRepository 向其中注入一些初始数据
- 使用
mockMvc.perform(post("/api/v1/post")
发出 post 请求
- 取回初始 post 并成功添加新的 post。
我目前的测试:
@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());
}
我有一个带有 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 对控制器进行单元测试。步骤为:
- 模拟 BlogPostInMemRepository 向其中注入一些初始数据
- 使用
mockMvc.perform(post("/api/v1/post")
发出 post 请求
- 取回初始 post 并成功添加新的 post。
我目前的测试:
@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());
}