【问题标题】:Mocked method returning NullPointerException返回 NullPointerException 的模拟方法
【发布时间】:2019-09-17 11:42:00
【问题描述】:

我想测试一种方法,该方法将向我的 GET 请求添加标头,以便与公共 API 建立连接。 我的业务逻辑服务如下:

@Service
public class RestTemplateFacade {

  private NutritionixHeader nutritionHeaderParam;
  private RestTemplate restTemplate;

  public RestTemplateFacade(
      NutritionixHeader nutritionHeaderParam,
      RestTemplate restTemplate) {
    this.nutritionHeaderParam = nutritionHeaderParam;
    this.restTemplate = restTemplate;
  }

  public ResponseEntity<Products> addHeaderToRequest(String queryParam) {

    HttpHeaders headers = new HttpHeaders();
    headers.set("x-app-id", nutritionHeaderParam.getNutritionixAppId());
    headers.set("x-app-key", nutritionHeaderParam.getNutritionixAppKey());

    UriComponentsBuilder uriBuilder = UriComponentsBuilder
        .fromHttpUrl("https://trackapi.nutritionix.com/v2/search/instant")
        .queryParam("query", queryParam);

    HttpEntity httpEntity = new HttpEntity(headers);

    return
        restTemplate
            .exchange(
                uriBuilder.toUriString(),
                HttpMethod.GET,
                httpEntity,
                Products.class);
  }
}

NutritionixHeader.class 看起来像:

@PropertySource("classpath:nutritionix.properties")
@Configuration
@Getter
@NoArgsConstructor
public class NutritionixHeader {

    @Value("${nutritionix-app-id}")
    private String NutritionixAppId;

    @Value("${nutritionix-app-key}")
    private String NutritionixAppKey;
}

我的测试类看起来像:

@ExtendWith(MockitoExtension.class)
@MockitoSettings(strictness = Strictness.LENIENT)
class RestTemplateFacadeTest {

  @Mock
  private NutritionixHeader nutritionixHeader;

  @Mock
  private RestTemplate restTemplate;

  @InjectMocks
  private RestTemplateFacade restTemplateFacade;

  @Mock
  private UriComponentsBuilder uriComponentsBuilder;

  @BeforeEach
  void setUp() {
    restTemplateFacade = new RestTemplateFacade(nutritionixHeader, restTemplate);
  }

  @Test
  void addHeaderToRequest() {

    //given
    var query = "query";
    given(nutritionixHeader.getNutritionixAppId()).willReturn("x-app-id");
    given(nutritionixHeader.getNutritionixAppKey()).willReturn("x-app-key");

    //when
    ResponseEntity<Products> productsResponse = restTemplateFacade.addHeaderToRequest(query);
    HttpHeaders headers = productsResponse.getHeaders();

    //then
    assertEquals("x-app-id",
        headers.entrySet()
            .stream()
            .filter(entry -> entry.getKey().equals("x-app-id"))
            .map(Entry::getValue)
            .flatMap(Collection::stream)
            .findFirst()
            .orElse(""));

    then(nutritionixHeader).should(times(1)).getNutritionixAppId();
  }

问题出现在该行:

HttpHeaders headers = productsResponse.getHeaders();

出现 NullPointerException。

我所需要的只是测试我的方法是否已将标题正确添加到我的 GET 查询中。

我有点困惑,因为我似乎正确地模拟了所有内容,但仍然收到空指针。也许我的 NPE 与我的 RestTemplate 对象没有模拟的事实有关。如果这是真的,我将不胜感激有关如何解决此问题的建议。

【问题讨论】:

    标签: java unit-testing junit mockito junit5


    【解决方案1】:

    您需要使用 ArgumentCaptor 捕获 HttpEntity 然后获取标头。看看代码:

    @Test
    void addHeaderToRequest() {
    
        //given
        var query = "query";
        given(nutritionixHeader.getNutritionixAppId()).willReturn("x-app-id");
        given(nutritionixHeader.getNutritionixAppKey()).willReturn("x-app-key");
    
        //when
        restTemplateFacade.addHeaderToRequest(query);
    
        //then
    
        ArgumentCaptor<HttpEntity> httpEntityCapture = ArgumentCaptor.forClass(HttpEntity.class);
        String url = "https://trackapi.nutritionix.com/v2/search/instant?query=query";
        verify(restTemplate).exchange(eq(url), eq(HttpMethod.GET), httpEntityCapture.capture(), eq(Products.class));
    
        HttpHeaders headers = httpEntityCapture.getValue().getHeaders();
    
        assertEquals("x-app-id",
                headers.entrySet()
                        .stream()
                        .filter(entry -> entry.getKey().equals("x-app-id"))
                        .map(Map.Entry::getValue)
                        .flatMap(Collection::stream)
                        .findFirst()
                        .orElse(""));
    
        then(nutritionixHeader).should(times(1)).getNutritionixAppId();
    }
    

    【讨论】:

    • 非常感谢您的快速回复。效果很好。
    • 我很乐意提供帮助!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-09-30
    • 1970-01-01
    • 2013-10-15
    • 2020-07-07
    • 1970-01-01
    相关资源
    最近更新 更多