系列文章目录:

    《微服务设计》读书笔记大纲

 

一.测试象限(Brain Marick)

      

微服务 单元测试里面没有启动类_微服务 单元测试里面没有启动类

 

二.测试金字塔(Mike Cohn)

    

微服务 单元测试里面没有启动类_ui_02

    

  1.单元测试

      通常只测试一个函数或方法调用,通过TDD或者基于属性而写的测试就属于这一类,在UnitTest中,我们不会启动服务,对且对外部文件和网络连接的使用也很有限,通常我们需要大量的单元测试。

      

微服务 单元测试里面没有启动类_ui_03

  单元测试是帮助开发人员,是面向技术而非业务的。

      2.服务测试

      对于包含多个服务的系统,一个服务测试只测试其中一个单独服务的功能。只测试一个单独的服务可以提高测试的隔离性,这样我们可以更快地定位并解决问题,为了达到这种隔离性,我们需要给所有的外部合作者打桩,以便只把服务本身保留在测试范围内。(打桩是指为被测服务的请求创建一些有预调响应的服务。)

      

微服务 单元测试里面没有启动类_测试_04

 对于每一位下游合作者,我们都需要一个打桩服务,然后在运行服务测试的时候启动它们,在测试过程中连接这些打桩服务,为了模仿真实的服务,我们还需要配置打桩服务返回请求响应。例如,我们可以配置积分账户为不同的客户返回不同的预设积分。

      另一种替换打桩有方式是mock。与打桩相比,mock还会进一步验证请求本身是否被正确调用,如果与期望请求不匹配,测试便会失败,过度使用mock会让测试变得脆弱。两者之间需要一些权衡。

      我们可以使用一些打桩的软件来协助我们测试,如Mountebank等。

      3.UI测试(端到端测试)

      对于包含多个微服务的系统来说,“端到端的测试”这个名称相比“UI测试”可能更适合,端到端测试会覆盖整个系统,这类测试通常用图形界面来模仿用户交互动作。这类的测试会覆盖大范围的产品代码,因此当它们通过时你会感觉很好。

      

微服务 单元测试里面没有启动类_json_05

  因为端到端测试涉及到多个微服务,我们可以用扇入模型来解决多个微服务同时开发,同时测试的问题。

      

微服务 单元测试里面没有启动类_测试_06

  虽然扇入模型可以来解决一些难题,但是端到端测试仍然有着很多的缺点。

      首先,端到端测试非常脆弱。

      有时测试失败并不是因为功能真的被破坏了,而是由其他一些原因引起的,比如其他服务停止或者网络故障等,这些跟测试的功能本身没有关系。

      包含在测试中的服务数量越多,测试就会越脆弱,不确定性也就越强。

      一个包括脆弱测试的测试套件往往成为Diane Vaughn所说的异常正常化的受害者,也就是说,随着时间的推移,我们对事情出错变得习以为常,并开始接受它们是正常的。

      Martin Flower在中提议,在碰到脆弱的测试时应该立刻记录下来,当不能立即修复时,需要把它们从测试套件中移除,然后就可以不受打扰地安心修复他们。修复时,看能否通过重写来避免被测代码运行在多个线程中,再看是否能让运行的环境更稳定,更好的办法是,看能否用不易出问题的小范围测试取代脆弱的端到端测试。

      单个微服务的测试可以由这个特性团队来维护。那么涉及到多个微服务的端到端测试该由谁负责?

      虽然这种责任分配方式还在探索当中,但将测试对所有人开放或者把这些测试交给专门的团队来维护都是错误的,前者可能会导致团队成员可以在无须对测试套件质量有任何理解的情况下随意添加测试,这会导致测试用例爆炸,有时导致测试没有真正的拥有者,当测试失败时,每个人都认为这是别人的问题,而不在乎测试是否通过;后者则会导致开发人员没有在第一时间得到自己代码的测试反馈,这会出现很大问题。一个可能的解决方案是,让各特性团队共享端到端测试套件的代码权,对测试套件联合负责。

      端到端的测试通常需要的时间最长,如果经常有与功能破坏无关的测试失败,这就是个灾难,这样即使真的是功能被破坏了,也需要花很多长时间才能发现,而此时大家已经转而做其他的事情了,切换大脑的上下文来修复测试是很痛苦的。

       我们可以用Selenium Grid工具通过并行运行测试来改善缓慢的问题,但是长期的积累,测试用例越来越多,但我们却不敢删除测试,而这可能是端到端测试面临着管理、维护困难的局面。我们应该改变我们的端到端测试思路,把测试整个系统的重心放到少量核心的场景上来,把任何在这个核心场景之外的功能放到相互隔离的服务测试中覆盖。

      同时,人们容易有这样的想法:既然所有服务在这些版本下能够一起工作,为什么不一起部署它们呢?如果接受了这个观念,慢慢的这成了常态,我们就会丢弃微服务的主要优势之一,独立于其他服务单独部署一个服务的能力。不用很长时间,本来分享地很好的服务就会与其他服务纠缠得越来越紧密,最终系统杂乱无序,你必须同时部署多个服务。这非常糟糕。

      4.三种测试之间的权衡

      越靠近金字塔的顶端,测试覆盖的范围越大,同时我们对被测后的功能也越有信心,缺点则是需要更长的时间运行测试,反馈周期也会变长,测试失败后很难定位哪个功能被破坏;而靠近金字塔的底部,测试更快,反馈周期也会变短,测试失败后更容易定位破坏的功能和代码,CI也很短。一般来说,顺着金字塔向下,下一层的测试数量要比上一层多一个数量级。

 

三.消费者驱动测试

使用之前的端到端测试,我们试图解决的关键问题是什么?是试图确保部署新的服务到生产环境后,变更不会破坏新服务的消费。有一种不需要用真正的消费者也能达到相同的目的,我们可以使用消费者驱动的契约(CDC)。当使用CDC时,我们会定义服务的消费者的期望,这些期望最终会变成对生产者运行的测试代码,如果使用得到,CDC应该成为服务提供者CI的一部分,这样可以确保如果这些契约被破坏了,服务提供者无法部署。

      让我们回头看一下前面图片中的例子,客户服务有两个独立的消费者:帮助台和Web客户端,我们将创建两个测试集合(其实就是模拟消费者),每个集合分别体现帮助台和Web客户端对客户服务的使用方式。因为这些CDC是对客户服务如何工作的期望,所以,客户服务本身的所有下游依赖都可以使用打桩,从测试范围来看,它与测试金字塔中的服务测试位于同一层,但侧重点不同,这些测试重在消费者如何使用服务。

      Pack(https://github.com/realestate-com-au/pact )是一个消费者驱动的测试工具,开始时,消费者使用Ruby DSL来定义生产者的期望,然后启动本地一个mock服务器(以后就可以单独地来测试消费者),并对其运行这些期望来生成pact规范文件,pack规范文件是一个标准的JSON规范;在生产者这边,你可以使用 JSON Pact规范来驱动对生产者API的调用,然后验证响应以测试消费者的规范是否被满足,因为生产者代码需要访问Pact文件,所以我们这里采用JSON。

      Pack的JSON规范是由消费者生成的,该规范需要成为一个生产者可访问的构建物,你可以把它存储在CI/CD仓库或Pack Broker中。

      另外Pacto(https://github.com/thoughtworks/pacto )和janus(https://github.com/gga/janus )也是开源的消费契约测试工具。

 

四.线上测试

在生产环境我们也需要进行测试,我们可以在高峰或请求来临之前进行测试,这样可以发现特定环境中的问题。

      1.冒烟测试

      我们可以对生产环境进行冒烟测试,用来帮助我们识别与环境有关的任何问题,我们应该把冒烟测试加入到我们的部署脚本中。

      2.蓝绿发布

      使用蓝绿发布时,我们会部署两份软件,但只有一个接受真正的请求。我们对新部署的版本运行一些测试,等测试没有问题时,我们再切换生产负荷到新部署的版本,通常情况下,我们会保留旧版本一小段时间,这样如果发生任何错误,能够快速恢复到旧的版本。这样可以降低风险,还可以大幅减少发布软件所需要的停机时间,我们甚至可以达到零宕机部署。

      3.金丝雀发布

      金丝雀发布是指通过将部分流量引流到新部署的系统,来验证系统是否按预期执行,与蓝绿发布不同,金丝雀发布过程中,新旧版本共存的时间更长,而且经常会调整流量。

      当考虑使用金丝雀发布时,你需要选择是引导部分生产流量到金丝雀,还是直接复制一份生产请求,如果你选择复制一份生产请求,然后引导师复制的请求至金丝雀,这样现行的版本和金丝雀版本将面对相同的请求,只是生产环境的请求是外部可见的,这样方便大家对新旧版本做比较,同时又避免金丝雀发布失败时影响客户的请求,但复制请求的工作可能会很复杂,尤其是在请求不幂等的情况下。

      4.灰度发布

      灰度发布是指在黑与白之间,能够平滑过渡的一种发布方式。AB test就是一种灰度发布方式,让一部分用户继续用A,一部分用户开始用B,如果用户对B没有什么反对意见,那么逐步扩大范围,把所有用户都迁移到B上面来。灰度发布可以保证整体系统的稳定,在初始灰度的时候就可以发现、调整问题,以保证其影响度。

      这些部署技术的原则,无外乎说明:更快的从错误中修复比千方百计地防范错误更重要,当然这不是说防范错误不重要,而是因为错误不可避免。

 

五.非功能测试

  非功能测试包括一些服务延迟测试、并发量测试、负载量测试、安全性测试、性能测试等。微服务拆分之后,会导致一个功能引发多个微服务的协同工作,这样跨网络边界调用的次数明显增加了,因此性能测试也是一件非常重要的事。

 

参考

      《微服务设计》(Sam Newman 著 / 崔力强 张骏 译)