如何在测试非存储库方法时执行类似于 “when, thenReturn” 的等效操作。

huangapple go评论84阅读模式
英文:

How to do something equivalent to "when, thenReturn" when you are testing a non-repository method

问题

  1. Spring BootJUnit测试中,我成功地使用了```"when, thenReturn"```来测试一个使用存储库方法的测试用例,代码如下:
  2. ```java
  3. @ExtendWith(SpringExtension.class)
  4. @WebMvcTest
  5. public class PictionarizerapiUserControllerTests {
  6. @MockBean
  7. private UserRepository userRepository;
  8. @MockBean
  9. private UserController userController;
  10. @Autowired
  11. private MockMvc mockMvc;
  12. @Test
  13. @DisplayName("当更新请求发送时,用户数据得到正确更新,并且更新后的用户数据以JSON形式返回")
  14. public void testUpdateUser() throws Exception {
  15. // 更新前的用户
  16. User existingUser = new User();
  17. existingUser.setId(28);
  18. existingUser.setName("Alex");
  19. ......
  20. ......
  21. // 使用ID=28调用UserRepository#findById()返回更新前的用户
  22. when(userRepository.findById(28)).thenReturn(Optional.of(existingUser));
  23. // UserRepository#save()返回传入的实体
  24. when(userRepository.save(any())).thenAnswer((invocation) -> invocation.getArguments()[0]);
  25. ......
  26. ......

我尝试为自己编写的控制器方法编写测试用例,使用了"when, thenReturn",代码如下:

  1. @Test
  2. @DisplayName("当提供正确的登录信息并且匹配的用户被获取时")
  3. public void testCheckIfValidUserFound() throws Exception {
  4. Integer userIdObj = Integer.valueOf(28);
  5. String requestEmail = "alex.armstrong@example.com";
  6. String requestPassword = "MajorStateAlchemist";
  7. when(userController.checkIfValidUser(requestEmail, requestPassword)).thenReturn(Optional.of(userIdObj));
  8. ......
  9. ......
  10. }

然而,我得到了一个错误消息The method thenReturn(ResponseEntity<capture#1-of ?>) in the type OngoingStubbing<ResponseEntity<capture#1-of ?>> is not applicable for the arguments (Optional<Integer>)。我进行了一些研究,并理解了"when, thenReturn"语法仅适用于测试存储库方法,这些方法是JPA中的内置方法,例如findById()等(除非我理解错误)。在我的情况下,它不起作用,因为我要测试的是我自己创建的方法,而不是JPA的内置存储库方法。

接下来是我的问题。
当我测试除存储库方法以外的内容时,如何编写等效于"when, thenReturn"的代码?

更新

这是我自己编写的方法的定义。

  1. @RequestMapping(value = "/login", method = RequestMethod.GET)
  2. public ResponseEntity<?> checkIfValidUser(
  3. @RequestParam("email") String email,
  4. @RequestParam("password") String password) {
  5. int userId = 0;
  6. List<User> userList = repository.findAll();
  7. for(User user: userList) {
  8. String userEmail = user.getEmail();
  9. String userPassword = user.getPassword();
  10. String inputEmail = email;
  11. String inputPassword = password;
  12. if(userEmail.equals(inputEmail) && userPassword.equals(inputPassword)) {
  13. userId = user.getId();
  14. }
  15. }
  16. if(userId > 0) {
  17. Integer userIdObj = Integer.valueOf(userId);
  18. return new ResponseEntity<>(userIdObj, HttpStatus.OK);
  19. } else {
  20. return new ResponseEntity<>(
  21. new Error("The email address and the password don't match"),
  22. HttpStatus.NOT_FOUND
  23. );
  24. }
  25. }
  1. <details>
  2. <summary>英文:</summary>
  3. I&#39;m writing some test code in Spring Boot JUnit, and was successful in a testing case with a repository method, using ```&quot;when, thenReturn&quot;``` like below.

@ExtendWith(SpringExtension.class)
@WebMvcTest
public class PictionarizerapiUserControllerTests {

@MockBean
private UserRepository userRepository;

@MockBean
private UserController userController;

@Autowired
private MockMvc mockMvc;

@Test
@DisplayName("When an update request is sent, the User data gets updated properly, and the updated User data gets returned in a form of JSON")
public void testUpdateUser() throws Exception {
// User before update
User existingUser = new User();
existingUser.setId(28);
existingUser.setName("Alex");
......
......

  1. // return the User (before update) that is fetched by UserRepository#findById() with ID=28
  2. when(userRepository.findById(28)).thenReturn(Optional.of(existingUser));
  3. // UserRepository#save() returns the fetched entity as it is
  4. when(userRepository.save(any())).thenAnswer((invocation) -&gt; invocation.getArguments()[0]);
  5. ......
  6. ......
  1. I thought I could also write a test case for a controller method which I wrote myself, and I tried to do &quot;when, thenReturn&quot; as the following.

@Test
@DisplayName("When correct login information is given and the matched user is fetched")
public void testCheckIfValidUserFound() throws Exception {
Integer userIdObj = Integer.valueOf(28);

  1. String requestEmail = &quot;alex.armstrong@example.com&quot;;
  2. String requestPassword = &quot;MajorStateAlchemist&quot;;
  3. when(userController.checkIfValidUser(requestEmail, requestPassword)).thenReturn(Optional.of(userIdObj));
  4. ......
  5. ......

}

  1. However, I got an error message ```The method thenReturn(ResponseEntity&lt;capture#1-of ?&gt;) in the type OngoingStubbing&lt;ResponseEntity&lt;capture#1-of ?&gt;&gt; is not applicable for the arguments (Optional&lt;Integer&gt;)```. I did some research and came to the understanding that you can use ```&quot;when, thenReturn&quot;``` syntax only when you are testing repository methods, which are built in methods in JPA, like ```findById()``` etc (unless I&#39;m mistaken), and in my case it doesn&#39;t work because what I&#39;m trying to test is a method that I created on my own, not a JPA&#39;s built in repository method.
  2. And here comes my question.
  3. How can I write something that is equivalent to ```&quot;when, thenReturn&quot;``` when I&#39;m testing something other than a repository method?
  4. **UPDATE**
  5. This is how my own method is defined.

@RequestMapping(value = "/login", method = RequestMethod.GET)
public ResponseEntity<?> checkIfValidUser(
@RequestParam("email") String email,
@RequestParam("password") String password) {
int userId = 0;

  1. List&lt;User&gt; userList = repository.findAll();
  2. for(User user: userList) {
  3. String userEmail = user.getEmail();
  4. String userPassword = user.getPassword();
  5. String inputEmail = email;
  6. String inputPassword = password;
  7. if(userEmail.equals(inputEmail) &amp;&amp; userPassword.equals(inputPassword)) {
  8. userId = user.getId();
  9. }
  10. }
  11. if(userId &gt; 0) {
  12. Integer userIdObj = Integer.valueOf(userId);
  13. return new ResponseEntity&lt;&gt;(userIdObj, HttpStatus.OK);
  14. } else {
  15. return new ResponseEntity&lt;&gt;(
  16. new Error(&quot;The email address and the password don&#39;t match&quot;),
  17. HttpStatus.NOT_FOUND
  18. );
  19. }
  20. }
  1. </details>
  2. # 答案1
  3. **得分**: 1
  4. 似乎您想要测试的方法是```testCheckIfValidUserFound()```,您不应该像这样模拟方法本身。

when(userController.checkIfValidUser(requestEmail, requestPassword)).thenReturn(Optional.of(userIdObj));

  1. 相反,您应该模拟的是```userRepository.findAll()```方法,因为这是您在控制器的```checkIfValidUser```方法中调用的存储库方法。
  2. 因此,您的“when, thenReturn”部分应该如下所示。

when(userRepository.findAll()).thenReturn(Collections.singletonList(existingUser));

  1. 当您想要检查测试是否返回正确的值时,通常需要指定要检查的键的值,但在这种情况下,根据您的```checkIfValidUser```方法,如果搜索成功,它只返回一个整数,因此在使用```jsonPath```进行断言时,不应该使用美元符号附加任何规范。
  2. 因此,在模拟存储库之后,您可以执行以下获取请求。

mockMvc.perform(MockMvcRequestBuilders.get("/login")
.param("email", requestEmail)
.param("password", requestPassword)
.with(request -> {
request.setMethod("GET");
return request;
}))
.andExpect(MockMvcResultMatchers.status().is(HttpStatus.OK.value()))
.andExpect(jsonPath("$").value(28));

  1. <details>
  2. <summary>英文:</summary>
  3. As it seems that the method that you want to test is ```testCheckIfValidUserFound()```, you shouldn&#39;t mock the method itself like so.

when(userController.checkIfValidUser(requestEmail, requestPassword)).thenReturn(Optional.of(userIdObj));

  1. Instead, the method that you are supposed to mock is ```userRepository.findAll()``` because that&#39;s the repository method that you are calling inside the ```checkIfValidUser``` method in your controller.
  2. So your &quot;when, thenReturn&quot; part should look like this.

when(userRepository.findAll()).thenReturn(Collections.singletonList(esixtingUser));

  1. and when you want to check if the test returns the correct value, ordinarily you need to specify which key&#39;s value you want to check, but in this case, according to your ```checkIfValidUser``` method it just returns an integer if the search is successful, so there shouldn&#39;t be any specification along with the dollar sign when asserting with ```jsonPath```.
  2. So after you mock the repository you can perform a get request like so.

mockMvc.perform(MockMvcRequestBuilders.get("/login")
.param("email", requestEmail)
.param("password", requestPassword)
.with(request -> {
request.setMethod("GET")<
return request;
}))
.andExpect(MockMvcResultMatchers.status().is(HttpStatus.OK.value()))
.andExpect(jsonPath("$").value(28));

  1. </details>

huangapple
  • 本文由 发表于 2020年8月23日 01:43:37
  • 转载请务必保留本文链接:https://go.coder-hub.com/63539265.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定