SpringBootでRestTemplateを使った外部APIを実行している実装をテストする。

エキサイト株式会社 メディア事業部エンジニアの中です。

SpringBootでRestTemplateを使った外部APIを実行している実装をテストする方法を記載します。

やり方は単純で、mockitoを使います。

ユースケース

  • RestTemplateを使った外部APIを実行している実装をテストする

前提条件

amazonにペットショップの一覧と金額を返すAPIがあると、仮に設定します。

  • APIのレスポンス
[
  {
    "id": 1,
    "type": "dog",
    "price": 249.99
  },
  {
    "id": 2,
    "type": "cat",
    "price": 124.99
  },
  {
    "id": 3,
    "type": "fish",
    "price": 0.99
  }
]

実装

  • interface
public interface AmazonStore {
    List<PetGoods> getPetGoods();
}
  • Impl
@Component
@RequiredArgsConstructor
public class AmazonStoreImpl implements AmazonStore {

    private final RestTemplate restTemplate;

    @Value("${spring.amazon.store.pet.url}")
    private String url;

    @Override
    public List<PetGoods> getPetGoods() {
        final UriComponents uriComponents = UriComponentsBuilder
                .fromUriString(url)
                .build();

        HttpHeaders headers = new HttpHeaders();
        final HttpEntity<String> entity = new HttpEntity<>(headers);

        final ResponseEntity<PetGoods[]> exchange = restTemplate
                .exchange(uriComponents.toUri(), HttpMethod.GET, entity, PetGoods[].class);

        if (exchange.getStatusCode().isError()) {
            throw new RuntimeException();
        }

        return Arrays.asList(exchange.getBody());
    }
}
@Data
@Accessors(chain = true)
public class PetGoods {
    private long id;
    private String type;
    private float price;
}

入力例

テストコードです

@ExtendWith(MockitoExtension.class)
class AmazonStoreImplTest {

    @Mock
    private RestTemplate restTemplate;

    @InjectMocks
    private AmazonStoreImpl amazonStore;

    @Test
    @Description("amazon ペットリストの一覧取得")
    void getPetGoods() {

        final PetGoodsDto dog = new PetGoodsDto()
                .setId(1)
                .setType("dog")
                .setPrice((float) 249.99);

        final PetGoodsDto cat = new PetGoodsDto()
                .setId(2)
                .setType("cat")
                .setPrice((float) 124.99);

        final PetGoodsDto fish = new PetGoodsDto()
                .setId(3)
                .setType("fish")
                .setPrice((float) 0.99);

        final List<PetGoodsDto> result = List.of(dog, cat, fish);

        final UriComponents build = UriComponentsBuilder
                .fromUriString("http://localhost/petstore/pets")
                .build();

        HttpHeaders headers = new HttpHeaders();
        final HttpEntity<String> entity = new HttpEntity<>(headers);

        PetGoodsDto[] returnMock = {
                dog, cat, fish
        };

        final ResponseEntity<PetGoodsDto[]> responseEntity = new ResponseEntity<>(returnMock, HttpStatus.OK);

        Mockito.when(restTemplate.exchange(
                build.toUri(),
                HttpMethod.GET, entity, PetGoodsDto[].class))
                .thenReturn(responseEntity);

        ReflectionTestUtils.setField(amazonStore, "url", "http://localhost/petstore/pets", String.class);

        final List<PetGoodsDto> petGoods = amazonStore.getPetGoods();

        Assertions.assertEquals(
                result,
                petGoods
        );
    }
}

出力例(テストが成功しているコンソールログです)

BUILD SUCCESSFUL in 2s

ポイント解説

用意するmockを設定します。

    @Mock
    private RestTemplate restTemplate;

テスト対象をInjectMocksで設定します

    @InjectMocks
    private AmazonStoreImpl amazonStore;

返却されるResponseEntityを設定します。

        final ResponseEntity<PetGoodsDto[]> responseEntity = new ResponseEntity<>(returnMock, HttpStatus.OK);

urlは@ValueでSpringBootのプロパティファイルから設定しているので、以前の記事で設定したReflectionTestUtilsを使って、urlを設定します。

ReflectionTestUtilsで変数に値をセットする - Excite Tech Blog

        ReflectionTestUtils.setField(amazonStore, "url", "http://localhost/petstore/pets", String.class);

あとはいつも通りのmockitoを使ってテストをします。

HttpStatus.OK以外を設定すれば、エラーハンドリングのテストもできます。