私はいくつかの情報を休憩経由で外部サーバーに要求する必要があるサービスを持っています:
public class SomeService {
public List<ObjectA> getListofObjectsA() {
List<ObjectA> objectAList = new ArrayList<ObjectA>();
ParameterizedTypeReference<List<ObjectA>> typeRef = new ParameterizedTypeReference<List<ObjectA>>() {};
ResponseEntity<List<ObjectA>> responseEntity = restTemplate.exchange("/objects/get-objectA", HttpMethod.POST, new HttpEntity<>(ObjectAList), typeRef);
return responseEntity.getBody();
}
}
getListofObjectsA()
のJUnitテストを作成するにはどうすればよいですか?
私は以下で試しました:
@RunWith(MockitoJUnitRunner.class)
public class SomeServiceTest {
private MockRestServiceServer mockServer;
@Mock
private RestTemplate restTemplate;
@Inject
private SomeService underTest;
@Before
public void setup() {
mockServer = MockRestServiceServer.createServer(restTemplate);
underTest = new SomeService(restTemplate);
mockServer.expect(requestTo("/objects/get-objectA")).andExpect(method(HttpMethod.POST))
.andRespond(withSuccess("{json list response}", MediaType.APPLICATION_JSON));
}
@Test
public void testGetObjectAList() {
List<ObjectA> res = underTest.getListofObjectsA();
Assert.assertEquals(myobjectA, res.get(0));
}
ただし、上記のコードは機能せず、responseEntitty
がnull
であることを示しています。 restTemplate.exchange
を正しくモックするようにテストを修正するにはどうすればよいですか?
MockRestServiceServer
オブジェクトは必要ありません。注釈は@InjectMocks
ではなく@Inject
です。以下は動作するはずのサンプルコードです
@RunWith(MockitoJUnitRunner.class)
public class SomeServiceTest {
@Mock
private RestTemplate restTemplate;
@InjectMocks
private SomeService underTest;
@Test
public void testGetObjectAList() {
ObjectA myobjectA = new ObjectA();
//define the entity you want the exchange to return
ResponseEntity<List<ObjectA>> myEntity = new ResponseEntity<List<ObjectA>>(HttpStatus.ACCEPTED);
Mockito.when(restTemplate.exchange(
Matchers.eq("/objects/get-objectA"),
Matchers.eq(HttpMethod.POST),
Matchers.<HttpEntity<List<ObjectA>>>any(),
Matchers.<ParameterizedTypeReference<List<ObjectA>>>any())
).thenReturn(myEntity);
List<ObjectA> res = underTest.getListofObjectsA();
Assert.assertEquals(myobjectA, res.get(0));
}
ResponseEntity<String> responseEntity = new ResponseEntity<String>("sampleBodyString", HttpStatus.ACCEPTED);
when(restTemplate.exchange(
Matchers.anyString(),
Matchers.any(HttpMethod.class),
Matchers.<HttpEntity<?>> any(),
Matchers.<Class<String>> any()
)
).thenReturn(responseEntity);
これは非推奨の ArgumentMatchers クラスの例です
when(restTemplate.exchange(
ArgumentMatchers.anyString(),
ArgumentMatchers.any(HttpMethod.class),
ArgumentMatchers.any(),
ArgumentMatchers.<Class<String>>any()))
.thenReturn(responseEntity);
私にとっては、Matchers.any(URI.class)を使用する必要がありました
Mockito.when(restTemplate.exchange(Matchers.any(URI.class), Matchers.any(HttpMethod.class), Matchers.<HttpEntity<?>> any(), Matchers.<Class<Object>> any())).thenReturn(myEntity);
私の側でこの作品。
ResourceBean resourceBean = initResourceBean();
ResponseEntity<ResourceBean> responseEntity
= new ResponseEntity<ResourceBean>(resourceBean, HttpStatus.ACCEPTED);
when(restTemplate.exchange(
Matchers.anyObject(),
Matchers.any(HttpMethod.class),
Matchers.<HttpEntity> any(),
Matchers.<Class<ResourceBean>> any())
).thenReturn(responseEntity);
RestTemplate
インスタンスは、実際のオブジェクトでなければなりません。 RestTemplate
の実際のインスタンスを作成し、@Spy
にすると機能するはずです。
@Spy
private RestTemplate restTemplate = new RestTemplate();
残りの呼び出しを気にせずにサービスをテストすることを意図している場合は、テストを簡素化するためにユニットテストで注釈を使用しないことをお勧めします。
したがって、私の提案は、サービスをリファクタリングして、インジェクションコンストラクターを使用してresttemplateを受け取ることです。これにより、テストが容易になります。例:
@Service
class SomeService {
@AutoWired
SomeService(TestTemplateObjects restTemplateObjects) {
this.restTemplateObjects = restTemplateObjects;
}
}
コンポーネントとしてのRestTemplate。インジェクトおよびモックの対象:
@Component
public class RestTemplateObjects {
private final RestTemplate restTemplate;
public RestTemplateObjects () {
this.restTemplate = new RestTemplate();
// you can add extra setup the restTemplate here, like errorHandler or converters
}
public RestTemplate getRestTemplate() {
return restTemplate;
}
}
そしてテスト:
public void test() {
when(mockedRestTemplateObject.get).thenReturn(mockRestTemplate);
//mock restTemplate.exchange
when(mockRestTemplate.exchange(...)).thenReturn(mockedResponseEntity);
SomeService someService = new SomeService(mockedRestTemplateObject);
someService.getListofObjectsA();
}
このようにして、SomeServiceコンストラクターによって残りのテンプレートをモックするための直接アクセスができます。
RestTemplateBuilder
を使用している場合、通常はうまくいかないかもしれません。これをテストクラスにwhen(condition)と共に追加する必要があります。
@Before
public void setup() {
ReflectionTestUtils.setField(service, "restTemplate", restTemplate);
}
小さなライブラリ を実装しました。これは非常に便利です。コンテキストを受信できるClientHttpRequestFactory
を提供します。そうすることにより、クエリパラメータの値の確認、ヘッダーの設定、逆シリアル化の動作確認など、すべてのクライアントレイヤーを通過できます。