エキサイト株式会社 メディア事業部エンジニアの中です。
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以外を設定すれば、エラーハンドリングのテストもできます。