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

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

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&#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");
......
......

// 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) -&gt; 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 &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);

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

  ......
  ......

}


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. 

And here comes my question. 
How can I write something that is equivalent to ```&quot;when, thenReturn&quot;``` when I&#39;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&lt;User&gt; 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) &amp;&amp; userPassword.equals(inputPassword)) {
			userId = user.getId();
		}
	}	
	
	if(userId &gt; 0) {
		Integer userIdObj = Integer.valueOf(userId);
		return new ResponseEntity&lt;&gt;(userIdObj, HttpStatus.OK);
	} else {
		return new ResponseEntity&lt;&gt;(
				new Error(&quot;The email address and the password don&#39;t match&quot;),  
				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&#39;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&#39;s the repository method that you are calling inside the ```checkIfValidUser``` method in your controller. 

So your &quot;when, thenReturn&quot; 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&#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```. 

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>



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:

确定