模拟问题:模拟正在调用实际方法



我在使用Mockito时遇到了问题,其中在测试中调用了真正的方法而不是模拟方法。搜索了几个小时,但一直找不到合适的答案。

这是我正在测试的服务:

@Service
public class DwpApiService {
@Autowired
private RestTemplate restTemplate = new RestTemplate();
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
private GeoService geoService = new GeoService();
private String baseUri = "XXXX";
private double cityLatt = 51.5074;
private double cityLong = -0.1278;
public List<PersonApiModel> getAllUsersInCityOrWithinDistanceOfCity(String cityName, double distanceInMiles) throws RuntimeException {
HashMap<Integer, PersonApiModel> usersInCityOrWithinDistance = new HashMap<>();
List<PersonApiModel> usersInCity = getAllUsersInCity(cityName).getBody();
for (PersonApiModel person: usersInCity) {
usersInCityOrWithinDistance.put(person.getId(), person);
}
List<PersonApiModel> allUsers = getAllUsers().getBody();
for (PersonApiModel person : allUsers) {
boolean withinDistance = geoService.isLocationWithinDistance(distanceInMiles, cityLatt, cityLong, person);
if (withinDistance && !usersInCityOrWithinDistance.containsKey(person.getId())) {
usersInCityOrWithinDistance.put(person.getId(), person);
}
}
return new ArrayList<>(usersInCityOrWithinDistance.values());
}
public ResponseEntity<List<PersonApiModel>> getAllUsersInCity(String cityName) throws RuntimeException {
String cap = cityName.substring(0, 1).toUpperCase() + cityName.substring((1));
if (!cap.equals("London")) {
throw new IllegalArgumentException("Invalid city name. Please only use the city of London.");
}
return restTemplate.exchange(
baseUri + "city/" + cap + "/users",
HttpMethod.GET,
null,
new ParameterizedTypeReference<List<PersonApiModel>>(){}
);
}
public ResponseEntity<List<PersonApiModel>> getAllUsers() throws RuntimeException {
return restTemplate.exchange(
baseUri + "/users",
HttpMethod.GET,
null,
new ParameterizedTypeReference<List<PersonApiModel>>(){}
);
}
}

此服务调用以下地理服务类:

public class GeoService {
//taken from https://www.movable-type.co.uk/scripts/latlong.html
public boolean isLocationWithinDistance(double radiusInMiles,
double sourceLat, double sourceLong,
PersonApiModel personApiModel) throws IllegalArgumentException {
boolean isLatLongWithinRange = checkIfLatLongAreWithinRange(
sourceLat, sourceLong,
personApiModel.getLatitude(), personApiModel.getLongitude());
if (!isLatLongWithinRange) {
throw new IllegalArgumentException("Latitude or Longitude are not valid values for id=" + personApiModel.getId());
}
int earthMeanRadius = 6371;
double latDiff = Math.toRadians(sourceLat -  personApiModel.getLatitude());
double longDiff = Math.toRadians(sourceLong - personApiModel.getLongitude());
double a = Math.sin(latDiff / 2) * Math.sin(latDiff / 2)
+ Math.cos(Math.toRadians(sourceLat)) * Math.cos(Math.toRadians(personApiModel.getLatitude()))
* Math.sin(longDiff / 2) * Math.sin(longDiff / 2);
double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
double distance = earthMeanRadius * c;
double radiusInKm = radiusInMiles * 1.609344;
return radiusInKm >= distance;
}
public boolean checkIfLatLongAreWithinRange(double sourceLat, double sourceLong,
double destinationLat, double destinationLong) {
boolean sourceLatInRange = Math.abs(sourceLat) <= 90;
boolean destLatInRange = Math.abs(destinationLat) <= 90;
boolean sourceLongLessThanMinus180 = sourceLong >= -180;
boolean sourceLongLessThan80 = sourceLong <= 80;
boolean destLongLessThanMinus180 = destinationLong >= -180;
boolean destLongLessThan80 = destinationLong <= 80;
return sourceLatInRange && destLatInRange
&& sourceLongLessThanMinus180 && sourceLongLessThan80
&& destLongLessThanMinus180 && destLongLessThan80;
}
}

我的测试用例如下所示:

@RunWith(MockitoJUnitRunner.class)
public class DwpApiServiceTest {
@Mock
private RestTemplate restTemplate;
@Mock
private GeoService geoService;
@InjectMocks
private final DwpApiService dwpApiService = new DwpApiService();
private final PersonApiModel personApiModel1 = new PersonApiModel();
private final PersonApiModel personApiModel2 = new PersonApiModel();
private final List<PersonApiModel> fakeList1 = new ArrayList<>();
private final List<PersonApiModel> fakeList2 = new ArrayList<>();
private final String baseUri = "XXXXXX";
private double cityLatt = 51.5074;
private double cityLong = -0.1278;
@Test
public void givenMocks_whenGetAllUsersInAndAroundCity_returnMockedPersonList() {
personApiModel1.setId(1);
fakeList1.add(personApiModel1);
ResponseEntity<List<PersonApiModel>> expected1 = new ResponseEntity<>(fakeList1, HttpStatus.OK);
personApiModel2.setId(2);
fakeList2.add(personApiModel2);
ResponseEntity<List<PersonApiModel>> expected2 = new ResponseEntity<>(fakeList2, HttpStatus.OK);
PersonApiModel person = personApiModel2;
List<PersonApiModel> expected = new ArrayList<>();
expected.addAll(fakeList1);
expected.addAll(fakeList2);
String cityName = "london";
double distanceInMiles = 20;
Mockito.when(dwpApiService.getAllUsers())
.thenReturn(expected1);
Mockito.when(dwpApiService.getAllUsersInCity(cityName))
.thenReturn(expected2);
Mockito.when(geoService.isLocationWithinDistance(distanceInMiles, cityLatt, cityLong, person))
.thenReturn(true);
List<PersonApiModel> actual = dwpApiService.getAllUsersInCityOrWithinDistanceOfCity(cityName, distanceInMiles);
Assert.assertEquals(expected, actual);
}

现在,模拟DwiApiService工作正常,这个测试用例和其他测试用例。但是嘲笑地理服务似乎会引起问题。当我尝试运行测试时,它实际上并没有调用模拟方法,而是调用了真正的方法。这些错误不是很有帮助...

[MockitoHint] DwpApiServiceTest.givenMocks_whenGetAllUsersInAndAroundCity_returnMockedPersonList (see javadoc for MockitoHint):
[MockitoHint] 1. Unused... -> at com.dwpAPI.services.DwpApiServiceTest.givenMocks_whenGetAllUsersInAndAroundCity_returnMockedPersonList(DwpApiServiceTest.java:115)
[MockitoHint]  ...args ok? -> at com.dwpAPI.services.DwpApiService.getAllUsersInCityOrWithinDistanceOfCity(DwpApiService.java:47)

有人知道这里发生了什么吗?花几个小时在这上面,似乎想不通。

可能的问题是您指定了确切的参数。将 mockito 配置视为一组规则,因此精确的值在那里不起作用(除非您真的很幸运并且它只是神奇地适用于某些特定的狭窄情况(。

因此,请尝试替换此一个:

when(geoService.isLocationWithinDistance(distanceInMiles, cityLatt, cityLong, person))
.thenReturn(true);

有了这个:

when(geoService.isLocationWithinDistance(eq(distanceInMiles), eq(cityLatt), eq(cityLong), eq(person)))
.thenReturn(true);

此外,我有时在使用when(...).thenReturn(...)结构时会遇到问题 - 例如,它无法正常工作@Spy。所以我通常更喜欢这种方法:

doReturn(true).when(geoService)
.isLocationWithinDistance(eq(distanceInMiles), eq(cityLatt), eq(cityLong), eq(person));

如果某些参数对您(或所有参数(来说并不重要,请不要使用eq(...),也不要使用任何值 - 只需将相关参数替换为any()即可。

PS:我只修改了一个嘲笑的规则,假设你会自己评论别人。

原因可能是因为你直接实例化了它

private GeoService geoService = new GeoService();

您可能需要使用 @Service 对类地理服务进行注释,并在 DwpApiService 上自动连接它

最新更新