I have a problem to write the JUnit Test with the usage of resttemplate.
When I run the testCalculateRate
, I got this error message shown below
java.lang.NullPointerException: Cannot invoke "org.springframework.http.ResponseEntity.getBody()" because "responseEntity" is null
I noticed that ResponseEntity<RateResponse> responseEntity = restTemplate.exchange(url, HttpMethod.GET, headersEntity, RateResponse.class);
returns null
.
Next, I debug the code
Here is saveRatesFromApi method of RateService
private RateEntity saveRatesFromApi(LocalDate rateDate, EnumCurrency base, List<EnumCurrency> targets) {
log.info("ExchangeService | saveRatesFromApi is called");
HttpHeaders headers = new HttpHeaders();
headers.add("apikey", EXCHANGE_API_API_KEY);
headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
final HttpEntity<String> headersEntity = new HttpEntity<>(headers);
String url = getExchangeUrl(rateDate, base, targets);
ResponseEntity<RateResponse> responseEntity = restTemplate.exchange(url, HttpMethod.GET, headersEntity, RateResponse.class); ---> ERROR LINE
RateResponse rates = responseEntity.getBody();
RateEntity entity = convert(rates);
return rateRepository.save(entity);
}
Here RateServiceTest shown below
import static com.exchangeapi.currencyexchange.constants.Constants.EXCHANGE_API_API_KEY;
import static com.exchangeapi.currencyexchange.constants.Constants.EXCHANGE_API_BASE_URL;
class RateServiceTest extends BaseServiceTest {
@Mock
private RateRepository rateRepository;
@Mock
private RestTemplate restTemplate;
@InjectMocks
private RateService rateService;
@Test
void testCalculateRate() {
// Initialize mocks
MockitoAnnotations.openMocks(this);
// Mocked data
EnumCurrency base = EnumCurrency.EUR;
List<EnumCurrency> targets = Arrays.asList(EnumCurrency.USD, EnumCurrency.GBP);
LocalDate date = LocalDate.of(2023, 5, 22);
// Mocked rate entity
RateEntity mockedRateEntity = new RateEntity();
mockedRateEntity.setBase(base);
mockedRateEntity.setDate(date);
Map<EnumCurrency, Double> rates = new HashMap<>();
rates.put(EnumCurrency.USD, 1.2);
rates.put(EnumCurrency.GBP, 0.9);
mockedRateEntity.setRates(rates);
// Mock repository behavior
when(rateRepository.findOneByDate(date)).thenReturn(Optional.of(mockedRateEntity));
// Mock API response
RateResponse mockedRateResponse = RateResponse.builder()
.base(base)
.rates(rates)
.date(date)
.build();
// Create a HttpHeaders object and set the "apikey" header
HttpHeaders headers = new HttpHeaders();
headers.add("apikey", EXCHANGE_API_API_KEY);
// Create a mock response entity with the expected headers and body
ResponseEntity<RateResponse> mockedResponseEntity = ResponseEntity.ok()
.headers(headers)
.body(mockedRateResponse);
// Mock RestTemplate behavior
when(restTemplate.exchange(
anyString(),
eq(HttpMethod.GET),
any(HttpEntity.class),
eq(RateResponse.class)
)).thenReturn(mockedResponseEntity);
// Call the method
RateDto result = rateService.calculateRate(base, targets, date);
// Verify repository method was called
verify(rateRepository, times(1)).findOneByDate(date);
// Verify API call was made
String expectedUrl = getExchangeUrl(date, base, targets);
HttpHeaders expectedHeaders = new HttpHeaders();
expectedHeaders.add("apikey", EXCHANGE_API_API_KEY);
HttpEntity<String> expectedHttpEntity = new HttpEntity<>(expectedHeaders);
verify(restTemplate, times(1)).exchange(
eq(expectedUrl),
eq(HttpMethod.GET),
eq(expectedHttpEntity),
eq(RateResponse.class)
);
// Verify the result
assertThat(result.getBase()).isEqualTo(base);
assertThat(result.getDate()).isEqualTo(date);
assertThat(result.getRates()).hasSize(2);
assertThat(result.getRates()).containsExactlyInAnyOrder(
new RateInfoDto(EnumCurrency.USD, 1.2),
new RateInfoDto(EnumCurrency.GBP, 0.9)
);
}
private String getExchangeUrl(LocalDate rateDate, EnumCurrency base, List<EnumCurrency> targets) {
String symbols = String.join("%2C", targets.stream().map(EnumCurrency::name).toArray(String[]::new));
return EXCHANGE_API_BASE_URL + rateDate + "?symbols=" + symbols + "&base=" + base;
}
}
How can I fix the issue?
Here is the repo : Link