英文:
How to do something equivalent to "when, thenReturn" when you are testing a non-repository method
问题
在Spring Boot的JUnit测试中,我成功地使用了```"when, thenReturn"```来测试一个使用存储库方法的测试用例,代码如下:
```java
@ExtendWith(SpringExtension.class)
@WebMvcTest
public class PictionarizerapiUserControllerTests {
@MockBean
private UserRepository userRepository;
@MockBean
private UserController userController;
@Autowired
private MockMvc mockMvc;
@Test
@DisplayName("当更新请求发送时,用户数据得到正确更新,并且更新后的用户数据以JSON形式返回")
public void testUpdateUser() throws Exception {
// 更新前的用户
User existingUser = new User();
existingUser.setId(28);
existingUser.setName("Alex");
......
......
// 使用ID=28调用UserRepository#findById()返回更新前的用户
when(userRepository.findById(28)).thenReturn(Optional.of(existingUser));
// UserRepository#save()返回传入的实体
when(userRepository.save(any())).thenAnswer((invocation) -> invocation.getArguments()[0]);
......
......
我尝试为自己编写的控制器方法编写测试用例,使用了"when, thenReturn"
,代码如下:
@Test
@DisplayName("当提供正确的登录信息并且匹配的用户被获取时")
public void testCheckIfValidUserFound() throws Exception {
Integer userIdObj = Integer.valueOf(28);
String requestEmail = "alex.armstrong@example.com";
String requestPassword = "MajorStateAlchemist";
when(userController.checkIfValidUser(requestEmail, requestPassword)).thenReturn(Optional.of(userIdObj));
......
......
}
然而,我得到了一个错误消息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"
的代码?
更新
这是我自己编写的方法的定义。
@RequestMapping(value = "/login", method = RequestMethod.GET)
public ResponseEntity<?> checkIfValidUser(
@RequestParam("email") String email,
@RequestParam("password") String password) {
int userId = 0;
List<User> userList = repository.findAll();
for(User user: userList) {
String userEmail = user.getEmail();
String userPassword = user.getPassword();
String inputEmail = email;
String inputPassword = password;
if(userEmail.equals(inputEmail) && userPassword.equals(inputPassword)) {
userId = user.getId();
}
}
if(userId > 0) {
Integer userIdObj = Integer.valueOf(userId);
return new ResponseEntity<>(userIdObj, HttpStatus.OK);
} else {
return new ResponseEntity<>(
new Error("The email address and the password don't match"),
HttpStatus.NOT_FOUND
);
}
}
<details>
<summary>英文:</summary>
I'm writing some test code in Spring Boot JUnit, and was successful in a testing case with a repository method, using ```"when, thenReturn"``` 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");
......
......
// return the User (before update) that is fetched by UserRepository#findById() with ID=28
when(userRepository.findById(28)).thenReturn(Optional.of(existingUser));
// UserRepository#save() returns the fetched entity as it is
when(userRepository.save(any())).thenAnswer((invocation) -> invocation.getArguments()[0]);
......
......
I thought I could also write a test case for a controller method which I wrote myself, and I tried to do "when, thenReturn" 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);
String requestEmail = "alex.armstrong@example.com";
String requestPassword = "MajorStateAlchemist";
when(userController.checkIfValidUser(requestEmail, requestPassword)).thenReturn(Optional.of(userIdObj));
......
......
}
However, I got an error message ```The method thenReturn(ResponseEntity<capture#1-of ?>) in the type OngoingStubbing<ResponseEntity<capture#1-of ?>> is not applicable for the arguments (Optional<Integer>)```. I did some research and came to the understanding that you can use ```"when, thenReturn"``` syntax only when you are testing repository methods, which are built in methods in JPA, like ```findById()``` etc (unless I'm mistaken), and in my case it doesn't work because what I'm trying to test is a method that I created on my own, not a JPA's built in repository method.
And here comes my question.
How can I write something that is equivalent to ```"when, thenReturn"``` when I'm testing something other than a repository method?
**UPDATE**
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;
List<User> userList = repository.findAll();
for(User user: userList) {
String userEmail = user.getEmail();
String userPassword = user.getPassword();
String inputEmail = email;
String inputPassword = password;
if(userEmail.equals(inputEmail) && userPassword.equals(inputPassword)) {
userId = user.getId();
}
}
if(userId > 0) {
Integer userIdObj = Integer.valueOf(userId);
return new ResponseEntity<>(userIdObj, HttpStatus.OK);
} else {
return new ResponseEntity<>(
new Error("The email address and the password don't match"),
HttpStatus.NOT_FOUND
);
}
}
</details>
# 答案1
**得分**: 1
似乎您想要测试的方法是```testCheckIfValidUserFound()```,您不应该像这样模拟方法本身。
when(userController.checkIfValidUser(requestEmail, requestPassword)).thenReturn(Optional.of(userIdObj));
相反,您应该模拟的是```userRepository.findAll()```方法,因为这是您在控制器的```checkIfValidUser```方法中调用的存储库方法。
因此,您的“when, thenReturn”部分应该如下所示。
when(userRepository.findAll()).thenReturn(Collections.singletonList(existingUser));
当您想要检查测试是否返回正确的值时,通常需要指定要检查的键的值,但在这种情况下,根据您的```checkIfValidUser```方法,如果搜索成功,它只返回一个整数,因此在使用```jsonPath```进行断言时,不应该使用美元符号附加任何规范。
因此,在模拟存储库之后,您可以执行以下获取请求。
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));
<details>
<summary>英文:</summary>
As it seems that the method that you want to test is ```testCheckIfValidUserFound()```, you shouldn't mock the method itself like so.
when(userController.checkIfValidUser(requestEmail, requestPassword)).thenReturn(Optional.of(userIdObj));
Instead, the method that you are supposed to mock is ```userRepository.findAll()``` because that's the repository method that you are calling inside the ```checkIfValidUser``` method in your controller.
So your "when, thenReturn" part should look like this.
when(userRepository.findAll()).thenReturn(Collections.singletonList(esixtingUser));
and when you want to check if the test returns the correct value, ordinarily you need to specify which key'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't be any specification along with the dollar sign when asserting with ```jsonPath```.
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));
</details>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论