在测试用例中,对服务调用进行契约检查!

乔梁 | 2021-02-07

下面的测试用例将对 CloudService 的服务调用进行模拟替代( mocks )。 这个测试用例真的能为我们提供足够的信心吗,它让你相信:这个服务调用是可以正确工作的了么?

@Test public void uploadFileToCloudStorage() {
  when(mockCloudService.write(
          WriteRequest.newBuilder().setUserId(testuser).setFileType(plain/text)...))
    .thenReturn(WriteResponse.newBuilder().setUploadId(uploadId).build());

  CloudUploader cloudUploader = new CloudUploader(mockCloudService);

  Uri uri = cloudUploader.uploadFile(new File(/path/to/foo.txt));
  // The uploaded file URI contains the user ID, file type, and upload ID. (Or does it?)
  assertThat(uri).isEqualTo(new Uri(/testuser/text/uploadId.txt));

这个测试中,很多地方都可能出错, 尤其是当服务契约变得复杂时。例如,plain/text 可能不是一个有效的文件类型,而你验证上传文件的 URI 是正确的。

**如果被测代码依赖于某个服务的契约,建议对这个服务调用进行检查,**而不是对其进行模拟替代( mocking it out )。这让你对能够正确使用这个外部服务更有信心:

  @Test public void uploadFileToCloudStorage() {
    CloudUploader cloudUploader = new CloudUploader(cloudService);
    Uri uri = cloudUploader.uploadFile("/path/to/foo.txt");
      assertThat(cloudService.retrieveFile(uri)).isEqualTo(readContent("/path/to/foo.txt"));

你如何检查这个服务调用呢?

  1. 使用一个 fake 服务。一个 fack 对象是某个真实服务的快速且轻量级的实现,它的行为就象它模仿的真实服务一样。一个fake服务通常是由服务的提供者负责维护;除非你能保证Fake服务的行为与真实实现一直保持一致,否则不要自己创建自己的fake服务。关于更多的Fake服务,参见 fake .
  2. 使用一个封闭服务器( hermetic )。这是一个真实的服务,它是由测试用例在其运行的同一台机器上启动的。使用密闭服务器的缺点是启动它并与其交互会让测试运行比较慢。关于更多的密闭服务器,参见 使用封闭服务进行端到端测试.

假如很多服务都没有自己的 fake 服务或者密闭服务器,模拟器(mock)可有是你的唯一选择了。但是,如果你的测试并没有使用真正的调用契约来请求真正的服务,那你要格外小心,确保那个测试所调用的真正服务是正常工作的,例如可以通过一个全面的端到端的测试用例,或者求助于手工测试(但这可能效率低,而且不容易扩展规模)。


发表时间:November 27, 2018

原文作者:Ben Yu

原文链接Testing on the Toilet: Exercise Service Call Contracts in Tests