模拟方法返回 NullPointerException

Mocked method returning NullPointerException

我想测试一种方法,该方法会将 headers 添加到我的 GET 请求中,以便与 public 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;
}

我的测试 class 看起来像:

@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。

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

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

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

@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();
}