Получение org.mockito.exceptions.misusing.PotentialStubbingProblem: Строгое несоответствие аргумента заглушки - PullRequest
0 голосов
/ 01 октября 2019

Я использую общедоступный API Nutritionix в своем приложении для получения подробной информации о пищевых ингредиентах из их базы данных. Я хочу протестировать метод, который отправляет запрос POST в общедоступный API, чтобы получить подробную информацию о питательной ценности продуктов, а затем использовать их в следующем процессе в моем приложении.

Мой тестовый класс выглядит следующим образом:

@ExtendWith(MockitoExtension.class)
class CaloriesServiceTest {

  @InjectMocks
  private CaloriesService caloriesService;

  @Mock
  private RestTemplate restTemplate;

  @Mock
  private NutritionixHeader nutritionixHeader;

  @BeforeEach
  void setUp() {
    caloriesService = new CaloriesService(nutritionixHeader, restTemplate);
  }

  @Test
  void receiveNutritionInformationFromAPI() {

    given(nutritionixHeader.getNutritionixAppId()).willReturn("secretID");
    given(nutritionixHeader.getNutritionixAppKey()).willReturn("secretKey");

    var meal1 = Meal.builder()
        .mealIngredients(List.of(RecipeIngredient.builder()
            .foodName("grilled fish")
            .build()))
        .build();
    var meal2 = Meal.builder()
        .mealIngredients(List.of(RecipeIngredient.builder()
            .foodName("eggs")
            .build()))
        .build();
    var meal3 = Meal.builder()
        .mealIngredients(List.of(RecipeIngredient.builder()
            .foodName("bacon")
            .build()))
        .build();

    var dailyMeals = DailyMeals.builder().dailyMeals(List.of(meal1, meal2, meal3)).build();

    var foodNutritional1 = FoodNutritional.builder().foodName("food name1").calories(11.1).build();
    var foodNutritional2 = FoodNutritional.builder().foodName("food name2").calories(22.2).build();
    var foodNutritional3 = FoodNutritional.builder().foodName("food name3").calories(33.3).build();
    var foodNutritional4 = FoodNutritional.builder().foodName("food name4").calories(44.4).build();

    HttpEntity<NutrientsBodyForRequest> requestBody =
        new HttpEntity<>(NutrientsBodyForRequest.builder()
            .query("grilled fish | eggs | bacon | burger")
            .build());

    given(restTemplate
        .exchange("https://trackapi.nutritionix.com/v2/natural/nutrients",
            HttpMethod.POST,
            requestBody,
            new ParameterizedTypeReference<List<FoodNutritional>>() {
            }))
        .willReturn(
            new ResponseEntity<List<FoodNutritional>>(
                List.of(
                    foodNutritional1,
                    foodNutritional2,
                    foodNutritional3,
                    foodNutritional4),
                HttpStatus.OK));

    //when
    List<FoodNutritional> foodsNutritional =
        caloriesService.receiveNutritionInformationFromAPI(dailyMeals);

    assertThat(foodsNutritional.get(0).getBrandName(), is("grilled fish"));
    assertEquals(3, foodsNutritional.size());

  }

}

К сожалению, когда я запускаю тест выше, я получаю исключение как:

org.mockito.exceptions.misusing.PotentialStubbingProblem: 
Strict stubbing argument mismatch. Please check:
 - this invocation of 'exchange' method:
    restTemplate.exchange(
    "https://trackapi.nutritionix.com/v2/natural/nutrients",
    POST,
    <com.application.meal.NutrientsBodyForRequest@3c486eb1,[]>,
    ParameterizedTypeReference<java.util.List<com.application.meal.FoodNutritional>>
);
    -> at com.application.meal.CaloriesService.receiveNutritionInformationFromAPI(CaloriesService.java:45)
 - has following stubbing(s) with different arguments:
    1. restTemplate.exchange(
    "https://trackapi.nutritionix.com/v2/natural/nutrients",
    POST,
    <com.application.meal.NutrientsBodyForRequest@afd496eb,[]>,
    ParameterizedTypeReference<java.util.List<com.application.meal.FoodNutritional>>
);
      -> at com.application.meal.CaloriesServiceTest.receiveNutritionInformationFromAPI(CaloriesServiceTest.java:69)
Typically, stubbing argument mismatch indicates user mistake when writing tests.
Mockito fails early so that you can debug potential problem easily.

Мой сервис выглядит следующим образом:

@Service
public class CaloriesService {

  private NutritionixHeader nutritionHeaderParam;

  private RestTemplate restTemplate;

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

  private String prepareFoodNamesForRequest(DailyMeals dailyMeals) {
    return dailyMeals.getDailyMeals()
        .stream()
        .map(Meal::getMealIngredients)
        .flatMap(Collection::stream)
        .map(RecipeIngredient::getFoodName)
        .collect(Collectors.joining(" | "));
  }


  public List<FoodNutritional> receiveNutritionInformationFromAPI(DailyMeals dailyMeals) {

    String foodNamesForRequest = prepareFoodNamesForRequest(dailyMeals);

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

    HttpEntity<NutrientsBodyForRequest> request =
        new HttpEntity<>(NutrientsBodyForRequest.builder()
            .query(foodNamesForRequest)
            .build(), headers);

    ResponseEntity<List<FoodNutritional>> response =
        restTemplate
            .exchange(
                "https://trackapi.nutritionix.com/v2/natural/nutrients",
                HttpMethod.POST,
                request,
                new ParameterizedTypeReference<List<FoodNutritional>>() {
                });

    return response.getBody();
  }
}

Мои классы компонентов следующие:

DailyMeals.class

@Getter
@AllArgsConstructor
@EqualsAndHashCode
@Builder
class DailyMeals {

  @Id
  private Long id;
  @OneToMany(cascade = CascadeType.ALL, mappedBy = "dailyMeals")
  List<Meal> dailyMeals;
}

FoodNutritional.class

@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode
public class FoodNutritional {

  @JsonProperty("food_name")
  private String foodName;

  @JsonProperty("brand_name")
  private String brandName;

  @JsonProperty("serving_qty")
  private Integer servingQuantity;

  @JsonProperty("serving_unit")
  private String servingUnit;

  @JsonProperty("serving_weight_grams")
  private String servingWeightGrams;

  @JsonProperty("nf_calories")
  private Double calories;

  @JsonProperty("nf_total_fat")
  private Double totalFat;

  @JsonProperty("nf_saturated_fat")
  private Double saturatedFat;

  @JsonProperty("nf_cholesterol")
  private Double cholesterol;

  @JsonProperty("nf_sodium")
  private Double sodium;

  @JsonProperty("nf_total_carbohydrate")
  private Double totalCarbohydrate;

  @JsonProperty("nf_dietary_fiber")
  private Double dietaryFiber;

  @JsonProperty("nf_sugars")
  private Double sugars;

  @JsonProperty("nf_protein")
  private Double protein;

  @JsonProperty("nf_potassium")
  private Double potassium;
}

Meal.class

@AllArgsConstructor
@EqualsAndHashCode
@Getter
@Builder
@Entity
public class Meal {

  @Id
  @GeneratedValue
  private Long id;

  @OneToMany(cascade = CascadeType.ALL, mappedBy = "meal")
  List<RecipeIngredient> mealIngredients;
}

РЕДАКТИРОВАТЬ:

Для @Abhiram Я прилагаю и IntelliJ жалуются на ваш предложенный кусок кода

error

Edit2: После нескольких размышлений я думаю, что есть проблема с другим HashCode объекта, который я передаю как тело: NutrientsBodyForRequest@3c486eb1,[] и NutrientsBodyForRequest@afd496eb,[], любые предложения, если это возможно, что это источникпроблема?

Все, чего я хочу добиться, это исправить эту ошибку и проверить в тесте, будет ли поддельный сервис возвращать желаемый результат. Буду благодарен за предложение о том, как достичь цели.

Ответы [ 2 ]

1 голос
/ 01 октября 2019

Вместо «new ParameterizedTypeReference> () {}» установите макет для соответствия ЛЮБОМУ параметру этого типа.

Вместо

given(restTemplate
        .exchange("https://trackapi.nutritionix.com/v2/natural/nutrients",
            HttpMethod.POST,
            requestBody,
>>>>>>>>>>  new ParameterizedTypeReference<List<FoodNutritional>>() { 
            }))
        .willReturn(
            new ResponseEntity<List<FoodNutritional>>(
                List.of(
                    foodNutritional1,
                    foodNutritional2,
                    foodNutritional3,
                    foodNutritional4),
                HttpStatus.OK));

Что-то вроде:

 given(restTemplate
      .exchange(eq("https://trackapi.nutritionix.com/v2/natural/nutrients"),
            eq(HttpMethod.POST),
            eq(requestBody),
            Mockito.any())
        .willReturn(
            new ResponseEntity<List<FoodNutritional>>(
                List.of(
                    foodNutritional1,
                    foodNutritional2,
                    foodNutritional3,
                    foodNutritional4),
                HttpStatus.OK));
0 голосов
/ 02 октября 2019

После сотрудничества с @Abhiram, к счастью, я нашел решение.

В моём макете отсутствовал ArgumentMatcher для параметров.

given(restTemplate
        .exchange(eq("https://trackapi.nutritionix.com/v2/natural/nutrients"),
            ArgumentMatchers.eq(HttpMethod.POST),
            ArgumentMatchers.any(),
            ArgumentMatchers.<ParameterizedTypeReference<List<FoodNutritional>>>any()))
        .willReturn(
            new ResponseEntity<List<FoodNutritional>>(
                foodsNutritionalStabData(),
                HttpStatus.OK));

Приведенный ниже подход решил проблему. Большое вам спасибо за приверженность.

...