我有一个服务,我需要通过rest向外部服务器询问一些信息:
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
?
这是一个未被弃用的ArgumentMatchers类
的示例when(restTemplate.exchange(
ArgumentMatchers.anyString(),
ArgumentMatchers.any(HttpMethod.class),
ArgumentMatchers.any(),
ArgumentMatchers.<Class<String>>any()))
.thenReturn(responseEntity);
您不需要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);
对于我来说,我必须使用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();
我曾经得到过这样的错误。我找到了一个更可靠的解决办法。我也提到了对我有用的import语句。下面这段代码完美地模拟了restemplate。
进口org.mockito.Matchers;
HttpHeaders headers = new Headers();
headers.setExpires(10000L);
ResponseEntity<String> responseEntity = new ResponseEntity<>("dummyString", headers, HttpStatus.OK);
when(restTemplate.exchange( Matchers.anyString(),
Matchers.any(HttpMethod.class),
Matchers.<HttpEntity<?>> any(),
Matchers.<Class<String>> any())).thenReturn(responseEntity);
如果有人在尝试模拟restTemplate.exchange(…)时遇到此问题,则问题似乎与匹配器有关。例如:
when(ecocashRestTemplate.exchange(Mockito.any()
, Mockito.eq(HttpMethod.GET)
, Mockito.any(HttpEntity.class)
, Mockito.<Class<UserTransaction>>any())
).thenReturn(new ResponseEntity<>(transaction, HttpStatus.OK));
但是这个也可以:
ResponseEntity<UserTransaction> variable = new ResponseEntity<>(transaction, HttpStatus.OK);
when(ecocashRestTemplate.exchange(Mockito.anyString()
, Mockito.eq(HttpMethod.GET)
, Mockito.any(HttpEntity.class)
, Mockito.<Class<UserTransaction>>any())
).thenReturn(new ResponseEntity<>(transaction, HttpStatus.OK));
注意第二个块上的Mockito.anyString()与mockito .any()。
当我们使用restTemplate测试与外部系统通信的客户端时,作为单元测试的一部分,我们需要验证我们发送的httpEntity, header和参数。
ArgumentCaptor在这些情况下很方便。这是我的例子(工作代码)
@Mock
private RestTemplate restTemplate;
@InjectMocks
private MyClient client;
@Captor
ArgumentCaptor<HttpEntity<?>> httpEntityCaptor;
when(restTemplate.exchange(eq(expectedUrl), eq(HttpMethod.POST), Matchers.any(HttpEntity.class), eq(MyTargetResponse.class)).thenReturn(expectedResponse);
verify(restTemplate).exchange(eq(expectedUrl),eq(HttpMethod.POST), httpEntityCaptor.captor(),eq(MyTargetResponse.class));
HttpEntity<?> actualResponse = httpEntityCaptor.getValue();
HttpHeaders headers = actualResponse.getHeaders();
assertEquals(headers.getFirst("Content-Type"), "application/json");
现在可以根据您的用例做出断言,因为您已经获得了被发送的捕获对象。
假设您有一个如下的交换调用:
String url = "/zzz/{accountNumber}";
Optional<AccountResponse> accResponse = Optional.ofNullable(accountNumber)
.map(account -> {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.set("Authorization", "bearer 121212");
HttpEntity<Object> entity = new HttpEntity<>(headers);
ResponseEntity<AccountResponse> response = template.exchange(
url,
GET,
entity,
AccountResponse.class,
accountNumber
);
return response.getBody();
});
要在测试用例中模拟这一点,可以使用mocitko,如下所示:
when(restTemplate.exchange(
ArgumentMatchers.anyString(),
ArgumentMatchers.any(HttpMethod.class),
ArgumentMatchers.any(),
ArgumentMatchers.<Class<AccountResponse>>any(),
ArgumentMatchers.<ParameterizedTypeReference<List<Object>>>any())
)
您可以使用以下未弃用的ArgumentMatchers
lenient().when(restTemplate.exchange(ArgumentMatchers.any(String.class),
ArgumentMatchers.eq(HttpMethod.GET),
ArgumentMatchers.any(),
ArgumentMatchers.eq(new ParameterizedTypeReference<List<ObjectA>>() {
})))
.thenReturn(responseEntity);
我实现了一个非常有用的小库。它提供了一个可以接收一些上下文的ClientHttpRequestFactory
。通过这样做,它允许遍历所有客户端层,例如检查查询参数是否有值,头集是否设置,并检查反序列化是否工作良好。
如果您使用RestTemplateBuilder
可能是通常的事情不会工作。您需要将它与when(condition)一起添加到测试类中。
@Before
public void setup() {
ReflectionTestUtils.setField(service, "restTemplate", restTemplate);
}
如果有人仍然面临这个问题,Captor注释为我工作
@Captor
private ArgumentCaptor<Object> argumentCaptor;
然后我可以通过以下方式嘲弄请求:
ResponseEntity<YourTestResponse> testEntity = new ResponseEntity<>(
getTestFactoryResponse(),
HttpStatus.OK);
when(mockRestTemplate.exchange((String) argumentCaptor.capture(),
(HttpMethod) argumentCaptor.capture(),
(HttpEntity<?>) argumentCaptor.capture(),
(Class<YourTestResponse.class>) any())
).thenReturn(testEntity);
对于这个exchange()例子,我发现存根更简单,旧的override:
var restTemplate = new RestTemplate() {
public <T> ResponseEntity<T> exchange(URI url, HttpMethod method, @Nullable HttpEntity<?> requestEntity,
Class<T> responseType) throws RestClientException {
throw new HttpClientErrorException(HttpStatus.NOT_FOUND);
}
};
少模仿东西…尤其是API总是在变化。eq(. .)任何(). .等。
您可以在返回某些内容或抛出异常之前检查存根exchange()中的arg。
——我知道这不是那个严格问题的答案。但结果是一样的。代码更少&
With mockitto -core-2.23.4
ResponseEntity<YOUR_CLASS> responseEntity = new ResponseEntity(YOUR_CLASS_OBJECT, HttpStatus.OK);
when(restTemplate.exchange(Mockito.anyString(), Mockito.any(), Mockito.any(), Mockito.<ParameterizedTypeReference<YOUR_CLASS>> any()))
.thenReturn(responseEntity);
是让它这样工作- Mockito.when(restTemplate.exchange((URI) any(), (HttpMethod) any(), (HttpEntity>) any(),(类)any()))).thenReturn (responseEntity);
然而,上面的代码不起作用,它表明responseentity是null。我怎样才能正确地修改我的测试呢restTemplate.exchange吗?
返回null,因为使用:
@Mock
private RestTemplate restTemplate;
如果目标是用MockRestServiceServer
而不是Mockito
来模拟,它应该是:
@Autowired
private RestTemplate restTemplate;
或RestTemplate restTemplate = new RestTemplate()
当MockRestServiceServer
和SomeService
提供相同的实例时,
@Test
public void methodWithPostCallTest() throws URISyntaxException {
RestTemplate restTemplate = new RestTemplate();
MockRestServiceServer mockServer = MockRestServiceServer.createServer(restTemplate);
mockServer.expect(ExpectedCount.once(),
requestTo(new URI("post-method-url")))
.andExpect(method(HttpMethod.POST))
.andRespond(withStatus(HttpStatus.OK)
.contentType(MediaType.APPLICATION_JSON)
.body("response-body")
);
YourService yourService = new YourService(restTemplate);
String response = yourService.methodWhichExecutesPostCall();
mockServer.verify();
assertEquals("response-body", response);
}
使用MockRestServiceServer
,它将为POST
调用的任何RestTemplate方法返回模拟响应- postForEntity
, postForObject
或exchange
。
更多细节:测试客户端应用程序
如果您的意图是测试服务而不关心rest调用,我建议不要在单元测试中使用任何注释,以简化测试。
所以,我的建议是重构你的服务,使用注入构造函数接收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构造函数直接访问rest模板。
相关内容
- 没有找到相关文章
最新更新
- 用 C 语言编写纸牌游戏"War"
- AWS中的 s3访问触发器
- 如何用API调用返回的数据填充Angular Boostrap Table Widget ?
- HTML CSS -在表格单元格中心像弹出一样缩放div
- 在docker桌面列表中持久化容器/应用
- Apache AuthLDAPBindDN指令接受任何字段?
- id(String)方法调用允许在插件{}脚本块错误
- python中的类、属性和方法
- 为什么float是准确的,Decimal是错误的
- 数据类型问题从Synapse到Delta表在Databricks?
- 使用列表值创建二进制矩阵
- 如何为被轻敲的键和被保持1000毫秒的键制作不同的热键
- 在MVVM Jetpack Compose上添加依赖项时重复类错误
- 列表理解不会消除重复项
- Windows -有效地将大量数据打印到终端
- R函数转换为HTML
- 裸机中的 Kubernetes 证书管理器问题:等待 HTTP-01 质询传播:状态代码"403"错误,预期'200'
- 使用钩子将状态从一个组件传递到另一个组件(不是子组件)
- 当我们使用暂存标志(--暂存)进行"npm build"时,如何在应用程序中创建"NON PRODUCTION"横幅?
- 循环访问列表,为每个项目创建新列表,然后将新列表作为参数传递给函数,但"var referenced before assignment"
- React单元测试Jest使用Typescript mount()的问题
- 是否有一种方法来隐藏代码使用Swift包管理器?
- 如何限制游戏循环fps?
- Django将更复杂的python类型传递给模板
- r语言 - 创建可使用元素访问的响应值列表
- ECharts Apache:条形图-添加水平水平线
- aws Cloudfront的维护页面
- 是否有任何方法为我的php应用程序从计算机到整个互联网?
- 根据指定值计算集合中所有可能的值组合
- 如何将Django模型限制为单个一对一关系?
热门标签:
javascript python java c# php android html jquery c++ css ios sql mysql arrays asp.net json python-3.x ruby-on-rails .net sql-server django objective-c excel regex ruby linux ajax iphone xml vba spring asp.net-mvc database wordpress string postgresql wpf windows xcode bash git oracle list vb.net multithreading eclipse algorithm macos powershell visual-studio image forms numpy scala function api selenium