英文:
Spring Boot Junit Test - Cannot get some values from application-test.yml (returns null)
问题
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
Next, I debug the code
getExchangeUrl
returns this null2023-05-22?symbols=USD%2CGBP&base=EUR as EXCHANGE_API_BASE_URL
returns null. EXCHANGE_API_API_KEY
also returns null.
Here is the Constants shown below
@Component
public class Constants {
public static String EXCHANGE_API_BASE_URL;
public static String EXCHANGE_API_API_KEY;
public static String EXCHANGE_CACHE_NAME;
public static Integer EXCHANGE_API_CALL_LIMIT;
@Value("${exchange-api.api-url}")
public void setExchangeApiBaseUrl(String apiUrl) {
EXCHANGE_API_BASE_URL = apiUrl;
}
@Value("${exchange-api.api-key}")
public void setExchangeApiKey(String apiKey) {
EXCHANGE_API_API_KEY = apiKey;
}
@Value("${exchange-api.cache-name}")
public void setExchangeCacheName(String cacheName) {
EXCHANGE_CACHE_NAME = cacheName;
}
@Value("${exchange-api.api-call-limit}")
public void setExchangeApiCallLimit(Integer apiCallLimit) {
EXCHANGE_API_CALL_LIMIT = apiCallLimit;
}
}
Here is the relevant part of application-test.yml
exchange-api:
api-url: https://api.apilayer.com/exchangerates_data/
api-key: ${EXCHANGE_API_API_KEY:default-key}
api-call-limit: 60
cache-name: exchanges
cache-ttl: 10000
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
英文:
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
Next, I debug the code
getExchangeUrl
returns this null2023-05-22?symbols=USD%2CGBP&base=EUR as EXCHANGE_API_BASE_URL
returns null. EXCHANGE_API_API_KEY
also returns null.
Here is the Constants shown below
@Component
public class Constants {
public static String EXCHANGE_API_BASE_URL;
public static String EXCHANGE_API_API_KEY;
public static String EXCHANGE_CACHE_NAME;
public static Integer EXCHANGE_API_CALL_LIMIT;
@Value("${exchange-api.api-url}")
public void setExchangeApiBaseUrl(String apiUrl) {
EXCHANGE_API_BASE_URL = apiUrl;
}
@Value("${exchange-api.api-key}")
public void setExchangeApiKey(String apiKey) {
EXCHANGE_API_API_KEY = apiKey;
}
@Value("${exchange-api.cache-name}")
public void setExchangeCacheName(String cacheName) {
EXCHANGE_CACHE_NAME = cacheName;
}
@Value("${exchange-api.api-call-limit}")
public void setExchangeApiCallLimit(Integer apiCallLimit) {
EXCHANGE_API_CALL_LIMIT = apiCallLimit;
}
}
Here is the relevant part of application-test.yml
exchange-api:
api-url: https://api.apilayer.com/exchangerates_data/
api-key: ${EXCHANGE_API_API_KEY:default-key}
api-call-limit: 60
cache-name: exchanges
cache-ttl: 10000
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
答案1
得分: 0
After I added @SpingBootTest annotation in BaseServiceTest and clicked "Invalid Cache", the issue was disappeared.
Here is BaseServiceTest shown below
@SpringBootTest
@ExtendWith(MockitoExtension.class)
@ActiveProfiles(value = "test")
public abstract class BaseServiceTest {
}
英文:
After I added @SpingBootTest annotation in BaseServiceTest and clicked "Invalid Cache" , the issue was disappeared.
Here is BaseServiceTest shown below
@SpringBootTest
@ExtendWith(MockitoExtension.class)
@ActiveProfiles(value = "test")
public abstract class BaseServiceTest {
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论