【问题标题】:How to mock RestTemplate exchange?如何模拟 RestTemplate 交换?
【发布时间】:2021-08-15 16:18:27
【问题描述】:

我想对assignApplicationsToUser 进行单元测试。给出了 applicationId 和用户 ID 的列表,并且我有一个 URL 来验证用户。我遇到了下面提到的异常。

java.lang.NullPointerException: Cannot invoke "com.fasterxml.jackson.databind.JsonNode.asText()" because the return value of "com.fasterxml.jackson.databind.JsonNode.get(String)" is null

AssignmentService.java

public class AssignmentService implements AssignemetServiceInterface {
    @Autowired
    AppInterceptor appInterceptor;
    @Autowired
    AssignmentRepository assignmentRepository;
    @Autowired
    private RestTemplate restTemplate;
    private String uri="http://localhost:8080/api/users/"+uuid;

    @Override
    public List<String> assignApplicationsToUser(List<String> applications, String uuid)
            throws SQLException, RequestEntityNotFoundException {

        HttpHeaders headers = new HttpHeaders();
        headers.set("token", appInterceptor.getLoggedInUser());
        HttpEntity<String> requestEntity = new HttpEntity<>(null, headers);
        String result = restTemplate.exchange(uri, HttpMethod.GET, requestEntity, String.class).getBody();

        ObjectMapper objectMapper = new ObjectMapper();
        List<String> response = new ArrayList<>();
        JsonNode responseJson = objectMapper.readTree(result);
        if (responseJson.get("data") == null
                    && responseJson.get("error").get("status").asText().equals("NOT_FOUND")) {
                throw new RequestEntityNotFoundException(uuid, "find");
        }

        for (String application : applications) {
        ApplicationAssignment isAssigned = assignmentRepository.findOneByApplicationId(application);
            if (isAssigned != null) {
                isAssigned.setIsUser(true);
                response.add(application + " reassigned to "+ uuid);
                isAssigned.setAssignedTo(uuid);
                this.assignmentRepository.save(isAssigned);
                } 
           else {
          ApplicationAssignment newApplication = new ApplicationAssignment(application, uuid, true);
    
            this.assignmentRepository.save(newApplication);
            response.add(application + " assigned successfully");
        }
       }
    return response;
}
}

样本输入: { 应用程序:["APP001","APP002","APP003","APP004"], uuid:“用户1” }

已编辑: 我已经添加了相同的示例响应。

{
    "data": {
        "id": "fa727274-5a74-428a-b0f6-501eebafd8e8",
        "name": "Akash",
        "email": "AkashTyagi@fico.com",
        "phone": 8799190991,
        "isActive": true,
        "createdBy": "abhishekjaiswal@fico.com",
        "updatedBy": null,
        "creationTimeStamp": "2021-08-11T11:23:05.356+00:00",
        "updationTimeStamp": null
    },
    "error": null,
    "timeStamp": "2021-08-16T05:02:04.866+00:00",
    "success": true
}

AssignmentServiceTest.java

@RunWith(MockitoJUnitRunner.class)
@SpringBootTest
public class AssignmentServiceTest {

    private static final Logger LOGGER = LoggerFactory.getLogger(AssignmentServiceTest.class);
    @InjectMocks
    private AssignmentService assignmentService;
    
    @Mock
    private AssignmentRepository assignmentRepo;
    
    @Mock
    private AppInterceptor appInterceptor;
    
    @Mock
    private RestTemplate restTemplate;
    
    ObjectMapper  objectMapper;
    String jsonString="{\r\n"
            + "    \"data\": {\r\n"
            + "        \"id\": \"fa727274-5a74-428a-b0f6-501eebafd8e8\",\r\n"
            + "        \"name\": \"Akash\",\r\n"
            + "        \"email\": \"AkashTyagi@fico.com\",\r\n"
            + "        \"phone\": 8799190991,\r\n"
            + "        \"isActive\": true,\r\n"
            + "        \"createdBy\": \"abhishekjaiswal@fico.com\",\r\n"
            + "        \"updatedBy\": null,\r\n"
            + "        \"creationTimeStamp\": \"2021-08-11T11:23:05.356+00:00\",\r\n"
            + "        \"updationTimeStamp\": null\r\n"
            + "    },\r\n"
            + "    \"error\": null,\r\n"
            + "    \"timeStamp\": \"2021-08-16T05:02:04.866+00:00\",\r\n"
            + "    \"success\": true\r\n"
            + "}";;
            
    JsonNode mock;
    
    @BeforeEach
    private void setUp() throws JsonMappingException, JsonProcessingException {
    
         objectMapper = new ObjectMapper();
         mock = org.mockito.Mockito.mock(JsonNode.class);
    }
    @Test
    public void test_assignApplicationsToUser() throws NullPointerException, SQLException, RequestEntityNotFoundException, IndexOutOfBoundsException,JsonProcessingException, URISyntaxException{

        LOGGER.info("Begin of test_assignApplicationToUser method");

        
        ApplicationAssignment applicationAssignment = new ApplicationAssignment("1","u1",true);
        ApplicationAssignment savedApplicationAssignment = new ApplicationAssignment("1","u2",true);
    

        ResponseEntity<String> myEntity = new ResponseEntity<String>("{\r\"error\":null\r}",HttpStatus.ACCEPTED);
        
        
        Mockito.when(restTemplate.exchange(
                ArgumentMatchers.anyString(),
                ArgumentMatchers.any(HttpMethod.class),
                ArgumentMatchers.any(),
                ArgumentMatchers.<Class<String>>any())).thenReturn(myEntity);
        
        Mockito.when(appInterceptor.getLoggedInUser()).thenReturn("abhishek@fico.com");

     
        Mockito.when(mock.get("data")).thenReturn(mock);
        Mockito.when(mock.get("error")).thenReturn(mock);
        Mockito.when(mock.get("error").asText()).thenReturn("FOUND");
        LOGGER.info("Node value "+ mock.asText());
        
        Mockito.when(assignmentRepo.findOneByApplicationId("1")).thenReturn(applicationAssignment);
        Mockito.when(assignmentRepo.save(ArgumentMatchers.any())).thenReturn(savedApplicationAssignment);
 
        // Mock the input List of applications
        List<String> listOfApplication = new ArrayList<>();
        listOfApplication.add("app1");
        listOfApplication.add("app2");
        List<String> response = assignmentService.assignApplicationsToUser(listOfApplication,"u1");
        assertEquals(response.get(0),"app1 assigned successfully");
    }
}

【问题讨论】:

    标签: spring unit-testing junit


    【解决方案1】:

    在主代码中,创建两个 pojo,如下所示,代表其余调用的响应。

    @JsonIgnoreProperties(ignoreUnknown = true)
    public class UserDataResponse {
    
        @JsonProperty("data")
        UserInfo userInfo;
    
        @JsonProperty("error")
        String error;
    
        @JsonProperty("success")
        boolean success;
    
    
    }
    
    @JsonIgnoreProperties(ignoreUnknown = true)
    public class UserInfo {
    
        @JsonProperty("id")
        String id;
    
        @JsonProperty("name")
        String name;
    
        @JsonProperty("email")
        String email;
    
        @JsonProperty("phone")
        String phone;
    
        @JsonProperty("isActive")
        boolean isActive;
    
        @JsonProperty("createdBy")
        String createdBy;
    
        @JsonProperty("updatedBy")
        String updatedBy;
    }
    

    在 AssignmentService 类中更改您的代码以获取如下响应,因为这是基于 HttpStatus 代码处理其余响应的更好方法,而不是尝试在 json 中添加元素。所以你的主要休息电话如下所示:

            String result = restTemplate.exchange(uri, HttpMethod.GET, requestEntity, String.class);
    
            if(result.getStatusCode()==HttpStatus.OK){
                UserDataResponse userDataResponse = readResponse(result,UserDataResponse.class);
                
            }else if(result.getStatusCode()==HttpStatus.NOT_FOUND){
                throw new RequestEntityNotFoundException(uuid, "find");
            }
    
    // You can make below code as part of the helper class and use it anywhere you want.
          private <T> T readResponse(ResponseEntity<String> responseEntity,Class<T> classType){
            T object;
            try {
                    object = objectMapper.readValue(responseEntity.getBody(),classType);
            } catch (JsonProcessingException e) {
                e.printStackTrace();
            }
            return null;
        }
    

    测试代码: 将 AppInterceptor、repository 和 resttemplate 等服务的所有变量模拟为注入到 AssignmentService。 将以下所有代码放在 AssingmentServiceTest 等测试类中

        @Mock
        AppInterceptor appInterceptor;
    
        @Mock
        AssignmentRepository assignmentRepository;
    
        @Mock
        RestTemplate restTemplate;
    
        @InjectMocks
        AssignmentService assignmentService;
    
        ObjectMapper objectMapper = new ObjectMapper();
    
        @Test
        public void test_assignApplicationsToUser(){
    
            UserDataResponse restResponse = new UserDataResponse();
            String jsonString="{\r\n"
                    + "    \"data\": {\r\n"
                    + "        \"id\": \"fa727274-5a74-428a-b0f6-501eebafd8e8\",\r\n"
                    + "        \"name\": \"Akash\",\r\n"
                    + "        \"email\": \"AkashTyagi@fico.com\",\r\n"
                    + "        \"phone\": 8799190991,\r\n"
                    + "        \"isActive\": true,\r\n"
                    + "        \"createdBy\": \"abhishekjaiswal@fico.com\",\r\n"
                    + "        \"updatedBy\": null,\r\n"
                    + "        \"creationTimeStamp\": \"2021-08-11T11:23:05.356+00:00\",\r\n"
                    + "        \"updationTimeStamp\": null\r\n"
                    + "    },\r\n"
                    + "    \"error\": null,\r\n"
                    + "    \"timeStamp\": \"2021-08-16T05:02:04.866+00:00\",\r\n"
                    + "    \"success\": true\r\n"
                    + "}";
    
            restResponse = readResponse(jsonString, UserDataResponse.class);
            ResponseEntity<String> myEntity = new ResponseEntity<String>(restResponse.toString(),HttpStatus.OK);
            Mockito.when(resttemplate.exchange(
                    ArgumentMatchers.anyString(),
                    ArgumentMatchers.any(HttpMethod.class),
                    ArgumentMatchers.any(),
                    ArgumentMatchers.<Class<String>>any())
        ).thenReturn(myEntity);
    
            // You can create a ApplicationAssignment pojo and pass it in thenReturn
            ApplicationAssignment applicationAssignment = new ApplicationAssingment(1,"sd");
            Mockito.when(assignmentRepository.findOneByApplicationId(ArgumentMatchers.any())).thenReturn(assignmentRepository);
    
            Mockito.when(appInterceptor.getLoggedInUser).thenReturn("token");
    
            // You can mock the response here but looks like that response is not used anywhere
            ApplicationAssignment savedApplicationAssignment = new ApplicationAssignment(1,"ss");
            Mockito.when(assignmentRepository.save(ArgumentMatchers.any())).thenReturn(savedApplicationAssignment);
    
            // Mock the input List of applications
            List<String> listOfApplication = new ArrayList<>();
            listOfApplication.add("app1");
            listOfApplication.add("app2");
            List<String> response = assignmentService.assignApplicationsToUser(listOfApplication,"1212");
            assertEquals(response[0],"app1 reassigned to 1212");
      }
    

    【讨论】:

    • 获取异常 com.fasterxml.jackson.core.JsonParseException: Unexpected character ('e' (code 101)): 期待双引号在 [Source: (String)" 处开始字段名称{存在:真}";行:1,列:3]
    • 我根据给定的异常更改为 "{\r\"exists\":true\r}",现在低于异常。无法调用“com.fasterxml.jackson.databind.JsonNode.get(String)”,因为“com.fasterxml.jackson.databind.JsonNode.get(String)”的返回值为 null
    • 你可以创建一个模型类来响应 rest 调用。模拟该对象并期望它作为响应。你能提供一个来自 rest 调用的示例响应吗?
    • 请在上面找到更新的代码。我也相信 Mockito.when(mock.get("data")).thenReturn(mock); Mockito.when(mock.get("error")).thenReturn(mock); Mockito.when(mock.get("error").asText()).thenReturn("FOUND"); LOGGER.info("节点值"+ mock.asText());这些行不是必需的
    • 我正在尝试测试下面提到的分支。 if (responseJson.get("data") == null && responseJson.get("error") != null){ throw new RequestEntityNotFoundException(uuid, "find"); }
    猜你喜欢
    • 2018-10-20
    • 1970-01-01
    • 2019-08-30
    • 2019-12-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多