首先问一个问题,在接口测试中,验证被测接口的返回值是否符合预期是不是就够了呢?
转账是银行等金融系统中常见的一个场景。在在最近的一个针对转账服务的单元测试中,笔者就遇到了上述问题。一个极端简化的转账申请如下图:
在一个B端用户通过转账服务接口发起转账申请后,转账服务接口在完成发起转账申请的过程中,在完成各项合法性校验,确定可以发起转账时,会从外部流水号服务那里申请到一个全局唯一且单调递增的流水号,该流水号将作为转账申请提交成功的返回值向申请方返回。同时,该流水号将作为转账申请记录的一部分,写入后台数据库等待后续审核。
从上述介绍中,我们得以了解到,这里的转账服务接口只是完成了申请的接收工作。转账申请需要后续被人工审核后才能完成实际的转账。
public class EntryRepository {
...
public void save(Entity entity) {
System.out.println(entity.getFlowNo);
}
}
@Service
public class EntryService throws Exception{
@Autowired
private EntryRepository entryReposity;
@Autowired
private FlowNoService flowNoService;
@Transactional
public String submit(Entity entity) {
validEntity(entity);
entity.setFlowNo(flowNoService.getNextFlowNo()); //流水号
entity.setStatus("SUBMITTED"); //已提交
entryReposity.save(entity);
return entity.getFlowNo();
}
}
以上是一个极简的代码实现逻辑,完成了申请单检查、流水号获取、数据库保存以及接口返回。
public class EntryServiceTest {
@InjectMocks
private EntryService entryService;
@Mock
private EntryRepository entryReposity;
@Mock
private FlowNoService flowNoService;
@Test
public void shouldReturnFlowNo(){
Entity entity= new entity;
entity.setAmount("一个亿");
String flowNo="20200307000001";
Mockito.when(flowNoService.getNextFlowNo()).thenReturn(flowNo);
assertThat(entryService.submit(entity)).isEqualTo(flowNo);
}
}
第一个用例首先验证了接口的返回值。
@Captor
private ArgumentCaptor<Entity> captor;
@Test
public void shouldCapture() {
Entity entity= new entity;
entity.setAmount("一个亿");
String flowNo="20200307000001";
Mockito.when(flowNoService.getNextFlowNo()).thenReturn(flowNo);
Mockito.verify(entryReposity,times(1)).save(captor.capture());
Entity captured= captor.getValue();
Entity expected= new Entity();
expected.setFlowNo(flowNo);
expected.setStatus("SUBMITTED");
assertThat(captured).isEqualToComparingOnlyGivenFields(expected,"flowNo","status");
}
}
在之前的测试用例类中,我们再添加第二个单元测试用例,来验证数据库写库的数据是否符合预期结果。
如何对两笔申请进行单元测试,Mock又如何写?这个就留给读者自行练习了。
如果不是写库,而是通过MQ对外发布?又如何进行测试呢?
本案例演示了如何使用Mockito提供的Capture特性来验证方法的传参,同时也展示了如何使用AssertJ进行对象的多个属性的断言。