我有一个接口,该接口的方法不带参数
public interface HealthStatus {
Integer healthCheck();
}�
实现如下
@Override
public Integer healthCheck() {
Integer status = 0;
try {
SimpleClientHttpRequestFactory simpleClientHttpRequestFactory = new SimpleClientHttpRequestFactory();
if (proxyConfigProperties.getEnabled()) {
Proxy proxy = new Proxy(java.net.Proxy.Type.HTTP, new InetSocketAddress(proxyConfigProperties.getUrl(), proxyConfigProperties.getPort()));
simpleClientHttpRequestFactory.setProxy(proxy);
}
RestTemplate restTemplate = new RestTemplate(new BufferingClientHttpRequestFactory(simpleClientHttpRequestFactory));
ResponseEntity<String> healthResponse = restTemplate.exchange(eVerifyGovernmentProperties.getUrl(), HttpMethod.GET, null, String.class);
status = (healthResponse.getStatusCode() == HttpStatus.OK) ? 200 : 202;
} catch (Throwable th) {
th.printStackTrace();
}
return status;
}�
如何针对正面和负面场景对这种方法进行单元测试。
编辑:
我已经按照以下方式重构了班级
@Service
@Qualifier("implementation")
public class HealthStatusImpl implements HealthStatus {
@Autowired
RestTemplateConfig restTemplateConfig;
@Autowired
private EVerifyGovernmentProperties eVerifyGovernmentProperties;
@Override
public Integer healthCheck() {
Integer status = 0;
try {
ResponseEntity<String> healthResponse = restTemplateConfig.getRestTemplate().exchange(eVerifyGovernmentProperties.getUrl(), HttpMethod.GET, null, String.class);
status = (healthResponse.getStatusCode() == HttpStatus.OK) ? 200 : 202;
} catch (Throwable th) {
th.printStackTrace();
}
return status;
}
}
这是实例化RestTemplate的类
@Component
public class RestTemplateConfig {
@Autowired
ProxyConfigProperties proxyConfigProperties;
public RestTemplate getRestTemplate(){
SimpleClientHttpRequestFactory simpleClientHttpRequestFactory = new SimpleClientHttpRequestFactory();
if (proxyConfigProperties.getEnabled()) {
Proxy proxy = new Proxy(java.net.Proxy.Type.HTTP, new InetSocketAddress(proxyConfigProperties.getUrl(), proxyConfigProperties.getPort()));
simpleClientHttpRequestFactory.setProxy(proxy);
}
RestTemplate restTemplate = new RestTemplate(new BufferingClientHttpRequestFactory(simpleClientHttpRequestFactory));
return restTemplate;
}
}
答案 0 :(得分:1)
实际上这是您要模拟的内容:
restTemplateConfig.getRestTemplate().exchange(eVerifyGovernmentProperties.getUrl(), HttpMethod.GET, null, String.class);
但是这将需要深入的模拟,这也是一种难闻的气味:您无需定义这样的语句即可调用其余模板。此责任应在特定的类别中定义。
因此,将其移至特定的bean方法(例如RestTemplateService
)中,这将使您避免传递尽可能多的参数,并且还将通过减少其依赖关系来平衡此类的更好职责:
ResponseEntity<String> healthResponse = restTemplateService.getForHealthCheck();
现在只需使用Mockito对其进行模拟。
关于RestTemplateService
,您可以创建自己的类,也可以依赖Feign(在这里Spring Feign更有意义)来通过接口启用声明式rest客户。
它将给出:
public class HealthStatusImpl implements HealthStatus {
private RestTemplateService restTemplateService;
// favor constructor injection
public HealthStatusImpl(RestTemplateService restTemplateService){
this.restTemplateService = restTemplateService;
}
@Override
public Integer healthCheck() {
Integer status = 0;
try {
ResponseEntity<String> healthResponse = restTemplateService.getForHealthCheck();
status = healthResponse.getStatusCode().is2xxSuccessful() ? 200 : 400;
} catch (Throwable th) {
th.printStackTrace();
}
return status;
}
}
请注意,Status.is2xxSuccessful()
通常更好,因为对于任何成功的响应(200
,201
等),它都返回true。如果不成功,则要返回错误响应代码。
从单元测试方面,您应该模拟此依赖关系,并根据您的方案记录模拟行为。
请注意,在您的情况下,您不想加载整个spring上下文,而是想要执行一个普通的单元测试,即没有容器。因此,请勿使用@SpringBootTest
,而应仅使用JUnit和Mockito。
例如,对于JUnit 5:
@ExtendWith(MockitoExtension.class)
public class HealthStatusImplTest{
private HealthStatusImpl healthStatusImpl;
@Mock
private RestTemplateService restTemplateServiceMock;
@BeforeEach
public void beforeEach(){
healthStatusImpl = new HealthStatusImpl(restTemplateService);
}
@Test
public void healthCheck_when_200_is_returned(){
Mockito.when(restTemplateServiceMock)
.getForHealthCheck().thenReturn(new ResponseEntity(HttpStatus.OK));
assertEquals(200, healthStatusImpl.healthCheck());
}
@Test
public void healthCheck_when_200_is_not_returned(){
Mockito.when(restTemplateServiceMock)
.getForHealthCheck().thenReturn(new ResponseEntity(HttpStatus.NOT_FOUND));
assertEquals(400, healthStatusImpl.healthCheck());
}
}
当然,RestTemplateService
也应该是单一测试,并且单一测试不能免除编写针对更高级别组件的集成测试的麻烦。
答案 1 :(得分:0)
要对Web客户端(例如基于RestTemplate的类)进行单元测试,您需要一个模拟服务器的框架,例如
答案 2 :(得分:0)
我不确定您的支票Text('You have no messages')
是否正确,因为如果状态不是(healthResponse.getStatusCode() == HttpStatus.OK)
,2xx
会抛出RestTemplate
。
这就是为什么如果您与第三方集成在一起,总是有一些模拟效果会更好。
这就是为什么我还建议您在测试中考虑HttpStatusCodeException
的原因。请参阅@Puce答案以获取链接。
此外,也不必为每个请求创建一个新的MockRestServiceServer
。
这就是为什么我也建议您考虑构造函数注入的原因。有关重构方法,请参见@davidxxx答案。并且不要忘记在您的RestTemplate
设置中添加连接超时。