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

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

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 &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

@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(&quot;${exchange-api.api-url}&quot;)
    public void setExchangeApiBaseUrl(String apiUrl) {
        EXCHANGE_API_BASE_URL = apiUrl;
    }

    @Value(&quot;${exchange-api.api-key}&quot;)
    public void setExchangeApiKey(String apiKey) {
        EXCHANGE_API_API_KEY = apiKey;
    }

    @Value(&quot;${exchange-api.cache-name}&quot;)
    public void setExchangeCacheName(String cacheName) {
        EXCHANGE_CACHE_NAME = cacheName;
    }

    @Value(&quot;${exchange-api.api-call-limit}&quot;)
    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&lt;EnumCurrency&gt; 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&lt;EnumCurrency, Double&gt; rates = new HashMap&lt;&gt;();
            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 &quot;apikey&quot; header
            HttpHeaders headers = new HttpHeaders();
            headers.add(&quot;apikey&quot;, EXCHANGE_API_API_KEY);
    
            // Create a mock response entity with the expected headers and body
            ResponseEntity&lt;RateResponse&gt; 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(&quot;apikey&quot;, EXCHANGE_API_API_KEY);
            HttpEntity&lt;String&gt; expectedHttpEntity = new HttpEntity&lt;&gt;(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&lt;EnumCurrency&gt; targets) {
    
            String symbols = String.join(&quot;%2C&quot;, targets.stream().map(EnumCurrency::name).toArray(String[]::new));
            return EXCHANGE_API_BASE_URL + rateDate + &quot;?symbols=&quot; + symbols + &quot;&amp;base=&quot; + 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 = &quot;test&quot;)
public abstract class BaseServiceTest {
}

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:

确定