Я использую общедоступный 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 жалуются на ваш предложенный кусок кода
Edit2: После нескольких размышлений я думаю, что есть проблема с другим HashCode объекта, который я передаю как тело: NutrientsBodyForRequest@3c486eb1,[]
и NutrientsBodyForRequest@afd496eb,[]
, любые предложения, если это возможно, что это источникпроблема?
Все, чего я хочу добиться, это исправить эту ошибку и проверить в тесте, будет ли поддельный сервис возвращать желаемый результат. Буду благодарен за предложение о том, как достичь цели.