Spring Boot Junit Test – 无法从application-test.yml获取某些值(返回null)

huangapple go评论95阅读模式
英文:

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

  1. 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

  1. @Component
  2. public class Constants {
  3. public static String EXCHANGE_API_BASE_URL;
  4. public static String EXCHANGE_API_API_KEY;
  5. public static String EXCHANGE_CACHE_NAME;
  6. public static Integer EXCHANGE_API_CALL_LIMIT;
  7. @Value("${exchange-api.api-url}")
  8. public void setExchangeApiBaseUrl(String apiUrl) {
  9. EXCHANGE_API_BASE_URL = apiUrl;
  10. }
  11. @Value("${exchange-api.api-key}")
  12. public void setExchangeApiKey(String apiKey) {
  13. EXCHANGE_API_API_KEY = apiKey;
  14. }
  15. @Value("${exchange-api.cache-name}")
  16. public void setExchangeCacheName(String cacheName) {
  17. EXCHANGE_CACHE_NAME = cacheName;
  18. }
  19. @Value("${exchange-api.api-call-limit}")
  20. public void setExchangeApiCallLimit(Integer apiCallLimit) {
  21. EXCHANGE_API_CALL_LIMIT = apiCallLimit;
  22. }
  23. }

Here is the relevant part of application-test.yml

  1. exchange-api:
  2. api-url: https://api.apilayer.com/exchangerates_data/
  3. api-key: ${EXCHANGE_API_API_KEY:default-key}
  4. api-call-limit: 60
  5. cache-name: exchanges
  6. cache-ttl: 10000

Here RateServiceTest shown below

  1. import static com.exchangeapi.currencyexchange.constants.Constants.EXCHANGE_API_API_KEY;
  2. import static com.exchangeapi.currencyexchange.constants.Constants.EXCHANGE_API_BASE_URL;
  3. class RateServiceTest extends BaseServiceTest {
  4. @Mock
  5. private RateRepository rateRepository;
  6. @Mock
  7. private RestTemplate restTemplate;
  8. @InjectMocks
  9. private RateService rateService;
  10. @Test
  11. void testCalculateRate() {
  12. // Initialize mocks
  13. MockitoAnnotations.openMocks(this);
  14. // Mocked data
  15. EnumCurrency base = EnumCurrency.EUR;
  16. List<EnumCurrency> targets = Arrays.asList(EnumCurrency.USD, EnumCurrency.GBP);
  17. LocalDate date = LocalDate.of(2023, 5, 22);
  18. // Mocked rate entity
  19. RateEntity mockedRateEntity = new RateEntity();
  20. mockedRateEntity.setBase(base);
  21. mockedRateEntity.setDate(date);
  22. Map<EnumCurrency, Double> rates = new HashMap<>();
  23. rates.put(EnumCurrency.USD, 1.2);
  24. rates.put(EnumCurrency.GBP, 0.9);
  25. mockedRateEntity.setRates(rates);
  26. // Mock repository behavior
  27. when(rateRepository.findOneByDate(date)).thenReturn(Optional.of(mockedRateEntity));
  28. // Mock API response
  29. RateResponse mockedRateResponse = RateResponse.builder()
  30. .base(base)
  31. .rates(rates)
  32. .date(date)
  33. .build();
  34. // Create a HttpHeaders object and set the "apikey" header
  35. HttpHeaders headers = new HttpHeaders();
  36. headers.add("apikey", EXCHANGE_API_API_KEY);
  37. // Create a mock response entity with the expected headers and body
  38. ResponseEntity<RateResponse> mockedResponseEntity = ResponseEntity.ok()
  39. .headers(headers)
  40. .body(mockedRateResponse);
  41. // Mock RestTemplate behavior
  42. when(restTemplate.exchange(
  43. anyString(),
  44. eq(HttpMethod.GET),
  45. any(HttpEntity.class),
  46. eq(RateResponse.class)
  47. )).thenReturn(mockedResponseEntity);
  48. // Call the method
  49. RateDto result = rateService.calculateRate(base, targets, date);
  50. // Verify repository method was called
  51. verify(rateRepository, times(1)).findOneByDate(date);
  52. // Verify API call was made
  53. String expectedUrl = getExchangeUrl(date, base, targets);
  54. HttpHeaders expectedHeaders = new HttpHeaders();
  55. expectedHeaders.add("apikey", EXCHANGE_API_API_KEY);
  56. HttpEntity<String> expectedHttpEntity = new HttpEntity<>(expectedHeaders);
  57. verify(restTemplate, times(1)).exchange(
  58. eq(expectedUrl),
  59. eq(HttpMethod.GET),
  60. eq(expectedHttpEntity),
  61. eq(RateResponse.class)
  62. );
  63. // Verify the result
  64. assertThat(result.getBase()).isEqualTo(base);
  65. assertThat(result.getDate()).isEqualTo(date);
  66. assertThat(result.getRates()).hasSize(2);
  67. assertThat(result.getRates()).containsExactlyInAnyOrder(
  68. new RateInfoDto(EnumCurrency.USD, 1.2),
  69. new RateInfoDto(EnumCurrency.GBP, 0.9)
  70. );
  71. }
  72. private String getExchangeUrl(LocalDate rateDate, EnumCurrency base, List<EnumCurrency> targets) {
  73. String symbols = String.join("%2C", targets.stream().map(EnumCurrency::name).toArray(String[]::new));
  74. return EXCHANGE_API_BASE_URL + rateDate + "?symbols=" + symbols + "&base=" + base;
  75. }
  76. }

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

  1. java.lang.NullPointerException: Cannot invoke &quot;org.springframework.http.ResponseEntity.getBody()&quot; because &quot;responseEntity&quot; 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

  1. @Component
  2. public class Constants {
  3. public static String EXCHANGE_API_BASE_URL;
  4. public static String EXCHANGE_API_API_KEY;
  5. public static String EXCHANGE_CACHE_NAME;
  6. public static Integer EXCHANGE_API_CALL_LIMIT;
  7. @Value(&quot;${exchange-api.api-url}&quot;)
  8. public void setExchangeApiBaseUrl(String apiUrl) {
  9. EXCHANGE_API_BASE_URL = apiUrl;
  10. }
  11. @Value(&quot;${exchange-api.api-key}&quot;)
  12. public void setExchangeApiKey(String apiKey) {
  13. EXCHANGE_API_API_KEY = apiKey;
  14. }
  15. @Value(&quot;${exchange-api.cache-name}&quot;)
  16. public void setExchangeCacheName(String cacheName) {
  17. EXCHANGE_CACHE_NAME = cacheName;
  18. }
  19. @Value(&quot;${exchange-api.api-call-limit}&quot;)
  20. public void setExchangeApiCallLimit(Integer apiCallLimit) {
  21. EXCHANGE_API_CALL_LIMIT = apiCallLimit;
  22. }
  23. }

Here is the relevant part of application-test.yml

  1. exchange-api:
  2. api-url: https://api.apilayer.com/exchangerates_data/
  3. api-key: ${EXCHANGE_API_API_KEY:default-key}
  4. api-call-limit: 60
  5. cache-name: exchanges
  6. cache-ttl: 10000

Here RateServiceTest shown below

  1. import static com.exchangeapi.currencyexchange.constants.Constants.EXCHANGE_API_API_KEY;
  2. import static com.exchangeapi.currencyexchange.constants.Constants.EXCHANGE_API_BASE_URL;
  3. class RateServiceTest extends BaseServiceTest {
  4. @Mock
  5. private RateRepository rateRepository;
  6. @Mock
  7. private RestTemplate restTemplate;
  8. @InjectMocks
  9. private RateService rateService;
  10. @Test
  11. void testCalculateRate() {
  12. // Initialize mocks
  13. MockitoAnnotations.openMocks(this);
  14. // Mocked data
  15. EnumCurrency base = EnumCurrency.EUR;
  16. List&lt;EnumCurrency&gt; targets = Arrays.asList(EnumCurrency.USD, EnumCurrency.GBP);
  17. LocalDate date = LocalDate.of(2023, 5, 22);
  18. // Mocked rate entity
  19. RateEntity mockedRateEntity = new RateEntity();
  20. mockedRateEntity.setBase(base);
  21. mockedRateEntity.setDate(date);
  22. Map&lt;EnumCurrency, Double&gt; rates = new HashMap&lt;&gt;();
  23. rates.put(EnumCurrency.USD, 1.2);
  24. rates.put(EnumCurrency.GBP, 0.9);
  25. mockedRateEntity.setRates(rates);
  26. // Mock repository behavior
  27. when(rateRepository.findOneByDate(date)).thenReturn(Optional.of(mockedRateEntity));
  28. // Mock API response
  29. RateResponse mockedRateResponse = RateResponse.builder()
  30. .base(base)
  31. .rates(rates)
  32. .date(date)
  33. .build();
  34. // Create a HttpHeaders object and set the &quot;apikey&quot; header
  35. HttpHeaders headers = new HttpHeaders();
  36. headers.add(&quot;apikey&quot;, EXCHANGE_API_API_KEY);
  37. // Create a mock response entity with the expected headers and body
  38. ResponseEntity&lt;RateResponse&gt; mockedResponseEntity = ResponseEntity.ok()
  39. .headers(headers)
  40. .body(mockedRateResponse);
  41. // Mock RestTemplate behavior
  42. when(restTemplate.exchange(
  43. anyString(),
  44. eq(HttpMethod.GET),
  45. any(HttpEntity.class),
  46. eq(RateResponse.class)
  47. )).thenReturn(mockedResponseEntity);
  48. // Call the method
  49. RateDto result = rateService.calculateRate(base, targets, date);
  50. // Verify repository method was called
  51. verify(rateRepository, times(1)).findOneByDate(date);
  52. // Verify API call was made
  53. String expectedUrl = getExchangeUrl(date, base, targets);
  54. HttpHeaders expectedHeaders = new HttpHeaders();
  55. expectedHeaders.add(&quot;apikey&quot;, EXCHANGE_API_API_KEY);
  56. HttpEntity&lt;String&gt; expectedHttpEntity = new HttpEntity&lt;&gt;(expectedHeaders);
  57. verify(restTemplate, times(1)).exchange(
  58. eq(expectedUrl),
  59. eq(HttpMethod.GET),
  60. eq(expectedHttpEntity),
  61. eq(RateResponse.class)
  62. );
  63. // Verify the result
  64. assertThat(result.getBase()).isEqualTo(base);
  65. assertThat(result.getDate()).isEqualTo(date);
  66. assertThat(result.getRates()).hasSize(2);
  67. assertThat(result.getRates()).containsExactlyInAnyOrder(
  68. new RateInfoDto(EnumCurrency.USD, 1.2),
  69. new RateInfoDto(EnumCurrency.GBP, 0.9)
  70. );
  71. }
  72. private String getExchangeUrl(LocalDate rateDate, EnumCurrency base, List&lt;EnumCurrency&gt; targets) {
  73. String symbols = String.join(&quot;%2C&quot;, targets.stream().map(EnumCurrency::name).toArray(String[]::new));
  74. return EXCHANGE_API_BASE_URL + rateDate + &quot;?symbols=&quot; + symbols + &quot;&amp;base=&quot; + base;
  75. }
  76. }

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

  1. @SpringBootTest
  2. @ExtendWith(MockitoExtension.class)
  3. @ActiveProfiles(value = "test")
  4. public abstract class BaseServiceTest {
  5. }
英文:

After I added @SpingBootTest annotation in BaseServiceTest and clicked "Invalid Cache" , the issue was disappeared.

Here is BaseServiceTest shown below

  1. @SpringBootTest
  2. @ExtendWith(MockitoExtension.class)
  3. @ActiveProfiles(value = &quot;test&quot;)
  4. public abstract class BaseServiceTest {
  5. }

huangapple
  • 本文由 发表于 2023年5月22日 23:35:48
  • 转载请务必保留本文链接:https://go.coder-hub.com/76307807.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定