Java微服务测试:基于Arquillian、Hoverfly、AssertJ、JUnit、Selenium与Mockito(txt+pdf+epub+mobi电子书下载)


发布时间:2020-07-02 05:16:03

点击下载

作者:刘梦馨

出版社:电子工业出版社有限公司

格式: AZW3, DOCX, EPUB, MOBI, PDF, TXT

Java微服务测试:基于Arquillian、Hoverfly、AssertJ、JUnit、Selenium与Mockito

Java微服务测试:基于Arquillian、Hoverfly、AssertJ、JUnit、Selenium与Mockito试读:

译者序

微服务架构的理念日渐深入人心,然而隐藏在其架构优势下的还有对传统软件开发模式的强烈冲击。如何在测试环境中部署种类繁多的微服务?如何对复杂的交互、多样的场景进行完整覆盖?单元测试、组件测试、集成测试以及端到端测试又有怎样的不同?有没有专门针对微服务架构的测试框架和工具?本书就将告诉你上述问题的答案。

本书介绍了大量Java微服务测试领域最为前沿的技术。本书从微服务架构讲起,介绍了测试在这种新型架构下所面临的问题和挑战,之后逐一介绍了单元测试、组件测试、集成测试、契约测试和端到端测试领域针对这些问题和挑战的应对方法。本书还提供了一个完整的微服务应用作为样例,从代码层面介绍如何编写不同领域的测试,给作者提供了上手实践的方法。本书的几位作者都是在测试领域内耕耘多年的老兵,相信他们宝贵的经验能给你带来收获。

本书主要介绍Java微服务测试,但是其中的方法和理念对于使用任何一门编程语言的开发和测试人员都会有很大的帮助。希望本书能够帮助你更好地改善工作中开发和测试的流程,提升整体的代码质量。

愿你的程序永远不出bug。

其他

致我的父母:感谢你们给我买的ZX Spectrum(早期的8位个人计算机)。——Alex Soto Bueno

致我的孩子:Antony 和 Toriann。你们拥有我,但是我拥有这本书!——Andy Gumbrecht

致伟大的软件工程师社区:我们在一起做令人惊叹的事!

致我的家人(尤其是我的妻子Tessie):感谢在这“疯狂”的人生旅程中能与你们一路同行。——Jason Porter

序言

在编程的早期是没有任何框架的,测试散落在代码的各个角落,以确保重要的软件功能的确是经过测试验证的。在那个时候,存储空间是十分宝贵且有限的。

后来,单元测试从一个流行概念演化成了软件测试事实上的标准。存储空间的问题也得到了很大的缓解,再也不能成为不编写测试代码的借口。如今,几乎所有的开发者都已经早早开始学习并使用单元测试的方法,并且单元测试已经成为软件开发成功的基础。

今天的企业级应用已经不可能仅依赖简单的单元测试来保证功能的完备。如今的客户也变得更加苛刻,要求也越来越高。如果企业想要充分满足客户的要求,就需要在开发的过程中使用多种测试策略。

本书一方面介绍如何解决当下企业测试的问题,另一方面介绍如何应对未来将微服务引入应用架构所带来的测试挑战,这将会给读者带来更大的价值。

我们花了很长时间来编写本书:最初它只是一个关于某个框架的小册子,现如今它已经成为一个介绍大量测试策略供读者选择的具有10章且内容丰富的图书。随着技术的不断演进,我们在这个过程中收获颇多。为了能给读者提供尽可能多的选择,我们尽可能地聚焦于策略、方法论和解决方案,而不是简洁的代码和看起来令人兴奋然而最终却不可用的应用。如果通过本书你可以对如何测试自己的应用有新的想法,那么我们的目的就达到了。

我们十分感谢你愿意花时间来读这样一本书,希望你能够喜欢我们的写作风格。

致谢

本书由3位独立开发者完成,在这里让我们感谢彼此的理解和支持,感谢每个人的辛苦工作和反馈。

十分感谢Arquillian项目的每一位参与者,尤其是Aslak Knutsen、Dan Allen、Bartosz Majsak和Matous Jobanek。在你读完这本书的时候,就会了解他们在这个伟大的项目上投入了多少精力,以及这个项目会如何显著地帮助你改善软件测试的质量。

开源软件(OSS)社区提供了很多极其有用的工具来帮助每个人更有效地进行测试。这些项目中的大部分都是由那些不知疲倦且无人知晓的开发者在他们的空闲时间完成的,在这里我们要表达对他们这份难能可贵工作的敬意。我们同时希望作为读者的你,也能够在每个合适的机会对这些“不可思议”的开发者表达谢意。

感谢Daniel Bryant和Marcin Grzejszczak花费时间对契约测试部分进行讨论。

在我们徘徊不前之际,Cynthia Kane和Tiffany Taylor两位编辑给我们提供了前进的动力,帮助我们完成了这个工作。利用空闲时间写书是一项很有挑战性的任务,十分感谢Cynthia和Tiffany帮助我们完成了这项任务。同时也要感谢帮助这本书从可能变为现实的Manning出版社的每一位工作人员:出版者Marjan Bace以及相关编辑和产品团队。

Joshua White提供了大量技术上的论证,感谢他解决了本书质量上的问题。他对我们的测试进行了测试!

感谢每一位花费时间阅读并提供反馈的读者,他们帮助我们完成了最终的图书产品。这些读者包括我们的技术同行评审员:Aleksandar、Alex Jacinto、Anshuman Purohit、Boris Vasile、Conor Redmond、Eddú Meléndez Gonzales、Ethan A.Rivett、Fabrizio Cucci、Gualtiero Testa、Henrik Løvborg、Jan Paul Buchwald、Jonathan Thoms、José Díaz、Kiran Anantha、Leo van den Berg、Mari Machado、Nilesh Thali、Piotr、Robert Walsh、Yagiz Erkan 和 Zorodzayi Mukuya。

最后,要感谢家人帮助我们度过了写书过程中一个个漫长的周末以及交稿前的忙乱和写作过程中无数起起伏伏的时光。没有你们的支持,我们将无法完成这项任务!1  微服务概述本章要点:■为什么要向微服务架构迁移。■微服务的现状如何,未来又会如何发展。■微服务的基本组件。■测试策略。传统的单体应用通常以一个单独的Web文件或者企业归档文件(WAR或者EAR)进行部署。它们包含了完成所有任务需要的业务逻辑,通常会包含一个渲染用户接口(UI,或者GUI)的组件。这也就意味着在扩容时,我们需要将完整的应用包复制到另一台新的机器上(通常是部署到同一个集群的另一台服务器上)。无论系统的负载和瓶颈出现在哪里,即使是整个应用里的一小部分出现了瓶颈问题,也需要按照这种要么全都扩容要么完全不扩容(all-or-nothing)的方式进行处理。微服务正是通过将业务逻辑拆分成更小、更可控的元素,并将这些元素灵活运用来解决这种“要么全都,要么完全不”问题的。在本书中,我们会假设你已经了解了微服务主题的相关基本概念。本书不会是一本你如今在很多地方都可以找到的关于微服务架构的教程,而是专注于帮助你解决那些所有微服务应用在测试方面所共同面对的挑战。为了达到这个目的,本章将介绍一些关于微服务究竟是什么的通用概念,帮助你了解后续章节将会涉及的概念。转向日益流行的微服务架构意味着你需要在开发、测试和重构过程中使用新的策略,并放弃那些从单体应用时代保留下来的经验。使用微服务使得你可以对单个服务进行扩容,并可以由多个团队并行地开发和维护多个服务,但是当面临测试时,你仍然需要一个有力的武器来解决将要遇到的问题。在本书中我们将会讨论多种新型的针对微服务的测试方法来帮助你解决需要在多个团队之间保持稳定的测试方法。之后的几章将会通过一个示例应用来介绍我们是如何调整测试策略的,这会帮助你更好地理解如何创建测试环境。Arquillian是一个专门处理通用测试问题的测试框架,你将会看到并使用到它的许多功能。Arquillian 生态中有一系列十分成熟的工具,也就是说 Arquillian 已经可以很好地和你已经了解的许多测试工具进行集成。尽管还有很多其他的测试工具,但是我们选择了它,并会着重介绍它。

关于软件版本的说明

本书使用了许多不同的软件包和工具,并且它们都在时刻更新着。在本书中我们尽可能选择那些不会被这些更新所影响的示例和技术。所有的示例都需要在Java 8 环境下运行,尽管当我们写完本书时,Java 10 已经发布了。由于从微服务的角度看,Java新版本的发布并没有提供新的功能,因此我们并没有对代码示例进行更新。同样的事情也发生在 JUnit 5上。由于本书开始的时候JUnit 5 还没有开发,书中所有实例均使用的是JUnit 4.12。在我们写完本书时,并不是所有的框架都已经官方支持了JUnit 5,所以我们也并没有更新JUnit的版本。其他的一些库和工具,例如 Spring Boot 和 Docker(Compose)也一直在进行更新,不过它们的更新对本书中的示例没有任何影响。1.1 什么是微服务,为什么要使用微服务

这个问题已经有了很多答案,本节将介绍我们心目中认为最合理的答案,这个答案可以帮助你更深入地认识微服务架构。然而技术创新在不断地发展,因此我们不会对未来做出预测。正如之前所说,本书关注于微服务测试的原则方面问题,而这些问题并不会随着时间而发生显著的变化。

如果此时你还没有完全理解微服务架构,这并没有什么关系;如果在本章之后你依然对微服务感到困惑,那么我们希望你能从其他的资源里获取更多关于微服务的信息。

建议 加入MicroProfil(e http://microprofile.io)的讨论会让你受益颇多。这个社区由 IBM、London Java Community(LJC)、Red Hat、Tomitribe、Payara和Hazelcast发起并维护,旨在分享企业级Java微服务的相关定义,并进行标准化。1.1.1 为什么要使用微服务

在深入微服务的本质之前,先让我们来回答这个“为什么”的问题。长久以来,单体应用都是一个常用的开发模式,至今在一些不需要扩容的场景中它依然能完美地满足需求。对单体应用进行扩容的问题正如图1.1所示,是显而易见的。微服务的出现并不是要证明其他模式都是错的,而是提供了一个新的架构,它相对于单体应用来说更加可靠、更加适应未来的变化。图1.1 扩容单体应用

微服务使得你可以将应用的各个小的部分进行隔离,并单独扩容,而不是从一整个应用的角度去操作。想象下面这种场景:你从核心逻辑中抽取出两个服务,即A和B。其中A提供访问物品目录的功能,B提供简单统计的功能。你会发现,通常来说服务A每小时都会被调用数百万次,而服务B每天只被调用一次。对于单体应用来说,扩容意味着,需要新增一个节点,部署同时包含服务A和服务B的一个应用。

如果能只对服务A进行扩容难道不是更好吗?这就是微服务能带来的最明显的好处。在如图1.2所示的新架构中,服务A和服务B变成了微服务A和微服务B。这里依然可以整体对应用进行扩容,但是这次你有了额外的灵活性:可以选择对负载最高的服务进行扩容。更进一步地,你可以选择一个专门的开发团队维护微服务A,另一个团队专门维护微服务B。你不用为了增加一个功能或修改一个bug而去修改整个应用,A和B现在可以独立地演进。图1.2 单独扩容一个应用下的某个微服务

像Netflix、Google、Amazon和eBay这样的公司已经将很多应用以微服务的架构进行管理,并且慷慨地将这些信息免费地分享出来。尽管目前主要是Web类应用在使用微服务架构,但你也可以将这种架构应用于任意类型的应用上,希望这能引起你的兴趣!1.1.2 什么是微服务

从字面上来看,“微”这个字会令我们想到很小的应用,占用很少的资源。但是对于一个应用来说,什么样的大小是合适的,并没有一个简单准确的方法可以衡量,需要根据经验来进行判断。根据业务逻辑的需求,一个微服务可能包含数行、几百行甚至上千行代码;从经验上来看,一个服务的逻辑粒度正好能够被一个单独的团队来处理是正合适的。理想情况下,你只需要关注服务的接入点(可能会提供多组资源),但是这里需要再次提醒的是,并没有一个简单准确的方法来衡量,你需要根据自己的业务情况进行判断。

从概念上来讲,一个单独完整的应用是一个微服务体积的上限。在典型的一个应用服务器运行多个应用的场景下,也就意味着切分你的应用使得它们可以同时运行在一个应用服务器上。从理论上来说,可以将你的第一个微服务看成拼图游戏中的一块拼图,并想象它如何和其他的拼图一起组成一张完整的图片。

你可以将一个单体应用如图1.3那样切分成多块逻辑拼图。在每一块拼图中都应该包含了足够的信息,使得它们在一起可以拼出一张完美的图片。在微服务架构中,这些拼图之间的关系通常会更加松散,如图1.4所示。图1.3 每个服务都是完整应用的一部分图1.4 每个微服务依然是完整应用的一部分,但是可以有单独隔离的环境1.1.3 持续集成、持续部署和Docker

将单体应用解耦为多个可扩展的微服务单元意味着,你必须在一开始就考虑持续集成(CI)和持续部署(CD)流水线。和单体应用时代你只需要一个构建脚本和一次部署不同,现在你需要考虑多个独立的构建过程,还需要考虑如何将它们编排在一起来进行集成测试,以及如何部署在多个不同的服务器上。

你会发现这所花费的精力远比你想象的要少。这很大程度上是因为归根到底微服务和其他应用是一样的,唯一的区别在于你可以将应用和它的运行环境进行打包。现在最简单也是最流行的做法是通过Docker镜像(www.docker.com)来创建并部署微服务。

说明 Docker是目前世界上领先的容器化软件平台。如果你不了解什么是Docker,请访问 www.docker.com下的“What is Docker?”教程。不要担心,在本书结束时我们会把所有微服务部分的元素进行组合,并指导你如何利用这些工具完成完整的流水线。

重量级的CI/CD工具包括Travis(https://travis-ci.org)、Bamboo(https://de.atlassian.com/software/bamboo),以及Jenkins(https://jenkins.io),它们都提供了针对Docker镜像的微服务部署流水线支持。本书中我们选择使用Jenkins,因为它是开源的,并拥有一个活跃的社区。Jenkins可能并不是一个最容易使用的工具,但是通过插件它可以提供最为丰富的功能。在第8章中我们会重点介绍所有本书中使用到的技术细节,并指导你来创建一个真正可使用的CI/CD流水线。1.2 微服务网络及其功能

微服务之间的松耦合会带来新的问题。微服务是如何组合的?这个架构可以提供哪些新的功能?下面我们会寻找这些问题的答案。但是首先要记住的是微服务之间是通过网络来进行隔离的。1.2.1 微服务网络

微服务大多数情况下通过RESTful(Representational State Transfer,表述性状态转移) API 使用HTTP和HTTPS进行集成。但是它们之间也可以通过其他方式进行集成,例如通过某个协议来访问一个资源或功能的接入点。使用何种方式会是一个很宽泛的话题,在这里我们只会介绍和讨论Java REST,并使用JAX-RS。

建议 如果你还不了解如何通过JAX-RS(https://jax-rs-spec.java.net)来创建RESTful Web服务,现在是时候实际了解一下这些主题了。

通过这些信息,你应该已经初步建立了一些对微服务的认知和想法。下面让我们继续看一些简单的例子。微服务A提供目录服务,并通过网络层和UI以及提供统计功能的微服务B相隔离。B和A通过定义好的请求/响应协议通信来收集统计信息。它们各自拥有自己的功能领域,并且和外部资源相互之间是完全独立的。UI服务可以调用A和B来将信息以用户友好的方式进行展现(可能是一个网站或者是一个重客户端),如图1.5所示。图1.5 每个服务通过定义好的协议进行通信

超媒体

服务在构建的时候需要考虑超媒体。这是最近的一个流行词汇,它意味着服务需要在架构中通过提供外部资源的连接来实现自文档性。目前在这个领域还没有一个明显的赢家,现在就开始对它们“下注”也是不明智的,但是你可以了解一下这些先行者,做出一些猜测:JSON-LD(http://json-ld.org)、JSON Hypertext Application Language(HAL, https://tools.ietf.org/html/draft-kelly-jsonhal-08)、Collection+JSON(https://github.com/collection-json/spec)以 及 Siren (https://github.com/kevinswiber/siren)

测试在设计时必须考虑完整地覆盖任何与外部服务进行的交互。由于网络交互本身就包含了很多挑战,因此正确地处理这些交互十分重要。我们会在第5章重点介绍这些内容。

现在你应该清楚根据应用的大小,微服务也可以很大,“微”代表了应用对外的接口范围。由于今天的云空间已经很便宜,因此微服务物理上的体积大小已经不像之前那样重要。

另一个我们经常听到的疑虑是,“网络速度会不会是个问题?”通常微服务都部署在同一个局域网内,而局域网通常都有着万兆甚至更好的网络带宽。因此从客户端的角度来看,由于每个微服务都可以很简单地进行扩容,因此响应时间通常比我们想象的要好。不要认为我们只是随便一说,想一下Netflix、Google、Amazon/AWS和eBay的使用情况。1.2.2 微服务特性

在我们的例子中,微服务A和微服务B可以分别被完全不同的团队独立开发和部署。每一个团队只需要理解他们共同工作的微服务的资源组件层而不是整个业务领域组件,就可以进行协作。这是首要的优点:每个团队在各自的范围下可以更容易理解自己的业务,以及可以更快速地进行开发。

JavaScript Object Notation(JSON,www.json.org) 和 Extensible Markup Language (XML,www.w3.org/XML))是最常用的两种资源描述语言,因此很容易就可以开发这类服务的客户端。有些情况下可能需要使用定制的方式,但是它们的基本理念是一致的:接入点可以被多个设备和客户端通过实现预定义的协议来进行访问。

多个微服务通过相互连接组成了一个网络,这个网络中的每一个单独的微服务都可以独立进行扩容。现在云上的弹性部署已经十分常见,例如,可以根据负载来对单一的服务进行自动的扩容和缩容。

微服务还能带来一些其他的好处,例如提高故障隔离和改善内存管理。在单体应用中,一个单独组件的故障会导致整个服务不可用。通过微服务,在故障服务问题被解决前,大部分的功能仍然可以对外提供服务。如图1.6所示,统计服务对于整个应用的功能来说真的是必要的吗?你能不能容忍统计服务有一段时间的不可用呢?图1.6 使用熔断器的弹性设计

当然,微服务除了带来上述的优点外,也引入了一些新的问题。开发者需要学习并理解开发一个分布式应用的复杂度,包括如何最好地使用针对单体应用开发而设计的IDE。对于那些需要调用多个服务但又没有被分布式事务所包含的使用场景,相对于单体应用的同样场景来说,我们需要进行更加仔细的规划。另外,测试通常情况下也变得更加困难,至少对于相互连接的组件如此,这也是我们要写这本书的原因。1.3 微服务架构

微服务的架构(如图1.7所示)可能是多变的,但是它们的设计是相近的。一些元素可以被组织起来构成应用组件层。测试覆盖到每一个层级是十分重要的,在这个过程中你会碰到许多挑战;本书将帮助你应对这些挑战,并提供解决方案。图1.7 微服务的基础组件

让我们自顶向下地观察微服务的每一个组件层。

说明 一个微服务应该被很好地封装并提供定义好范围的服务。这并不意味着不允许其他系统以其他方式进行交互。例如,你的服务提供一个从ElasticSearch(ES)中获得指定文档的功能。在这个例子中,对于其他应用来说,直接访问ES来寻找某个文档也是完全合理的。1.3.1 资源组件

资源负责通过预先选择的协议对外提供服务的交互功能。这些交互通用JSON或者XML进行对象映射来传递信息。这些映射对象代表了业务领域的输入和输出。如图1.8所示,处理输入对象和构建协议定义的响应通常发生在这一层。图1.8 暴露到公网的资源组件服务

说明 既然我们已经到了这一步,有必要提一下,资源组件层正是“微服务”中“微”的来源。

出于简化的目的,在本书之后的部分,我们只关注如今最为通用[1]的资源提供形式:RESTful接入点 。如果你还不了解RESTful Web服务,就需要花些时间去了解一下这个十分重要的话题。1.3.2 业务领域组件

业务领域组件是应用中最为核心的部分,并且高度特定于你正在开发的业务逻辑。业务领域可能会和多个其他服务(包括其他微服务)进行通信以处理来自资源组件的请求,并返回对应的计算响应结果,如图1.9所示。图1.9 服务业务逻辑的业务领域组件

在业务领域组件和资源组件之间需要一个桥梁,这通常会是一个远程组件。大部分的微服务都需要在某一个时间点和另一个微服务进行通信。1.3.3 远程资源组件

你可以把这个组件层想象成拼图游戏中需要将一块拼图和另一块拼图进行连接的部分。它包含了一个知道如何从其他微服务接入点发送和接收资源对象的客户端,并了解如何将其翻译成业务领域组件层使用的对象,如图1.10所示。图1.10 作为其他服务网关的远程资源组件

由于远程资源的一些天然属性,因此在设计时必须特别关注弹性。一个富有弹性的框架可以在故障发生时提供诸如熔断和超时重试的功能。不要尝试重新制造“轮子”,已经有了很多弹性框架可供选择,我们首选Hystrix(https://github.com/Netflix/Hystrix/wiki),它是由Netflix贡献并开源的。

网关服务可以扮演业务领域组件和客户端组件之间的桥梁。它负责翻译从所有远程资源客户端发送来的请求和响应。这里也是当服务不可访问时提供优雅失败的最佳场所。

客户端负责以你选择的协议进行对话,90%的情况下这个语言会是HTTP/S 下的JAX-RS(https://jax-rs-spec.java.net)Restful Web服务。

在这里我们极力推荐在这个组件层使用开源的服务框架Apache CXF(http://cxf.apache.org),因为它可以完全和JAX-WS、JAX-RS以及其他协议兼容,这可以让你不至于绑定在某一个特定的平台。1.3.4 持久化组件

通常情况下,应用都需要使用一些类型的持久化存储和数据获取[2](见图1.11)。这经常伴随着对象关系映射(ORM)机制,例如[3]Java持久化API (JPA),但也可以是一个简单的内嵌数据库或者配置文件。图1.11 数据存储的持久化组件1.4 微服务单元测试

第3章会深入介绍真实世界的单元测试场景。在下面几个段落中我们会介绍一下相关术语以及你在开发测试策略时可能会碰到的问题。

典型的单元测试会被设计得足够小,以便测试最简单的部分:一个工作单位。在微服务的上下文中,一个工作单位可能很难被表示,因为通常在表面的服务下隐藏着很多复杂性。

单元测试通常会带来一个结论,就是你需要重构代码来减少组件在测试场景下的复杂度。这也使得测试成为一个十分有用的设计工具,尤其在你使用测试驱动开发(TDD)的时候就更是如此了。单元测试带来的另外一个好处就是,可以让你在持续开发的过程中及时进行回归,以发现是否出现了问题。

尽管你在测试中可能会碰到更多具体的场景,但基本上可以将单元测试分为两类:联合型和孤立型。这两种类型的区别主要在于单元测试是否和其他底层协作者隔离。这两种类型并不是互斥的,它们可以很好地互相补充。根据测试挑战性的不同,你需要同时使用这两种类型的测试。我们会在本书后面的部分对这些概念展开讨论。1.4.1 孤立型单元测试

孤立型单元测试主要关注一个单独对象类的交互。这种测试应该只包括这个类自己的依赖。你经常会使用孤立型单元测试来测试资源组件、持久化组件和远程组件,因为这些组件之间通常不会直接交互,如图1.12所示。

这里需要通过插桩或模拟所有和这个类协作的部分来对要测试的类进行隔离。你需要测试这个类的所有方法,但是不需要跨越边界进入其他类。通常来讲,这意味着你需要将可能的返回结果注入到插桩或模拟的实现中。这样做的一个主要目的是尽可能提高被测类的代码覆盖率。图1.12 孤立型单元测试的主要组件1.4.2 联合型单元测试

联合型单元测试主要通过观察模块状态的变化来测试模块的行为。这种方式将被测单元当成一个黑盒,完全通过对外的接口进行测试。业务领域组件由于需要和其他部分进行协作来处理请求并返回响应,因此十分适用于联合型单元测试,如图1.13所示。图1.13 联合型单元测试的主要组件

在测试中你可能仍需要对某些复杂的协作者类进行插桩或模拟,但需要在离协作对象的层级尽可能远的地方进行这些模拟。你不仅要测试一个类发送和接收的数据是否正确,还需要确保类中的协作者按照预期的方式进行工作。在理想情况下,测试需要覆盖所有的模型、变量以及协作者类。测试这个类是否能处理非法的响应(反向测试)同样也很重要。总结

■一个微服务是一个单体应用被切分成的一个更小的逻辑单元部分。

■微服务可以使应用有针对性地进行扩容,并使开发更为聚焦。

■微服务在逻辑上允许你可以根据什么时间、什么部分对性能有需求来进行扩容,以满足应用扩展性的需求。

■你可以通过将单体应用切分成更小的部分来应用微服务架构。

■微服务可以让多个团队聚焦在单独的、互不冲突的任务上来构建更宏伟的项目。

■孤立型单元测试用来测试那些无须存储状态以及无须协作的组件。

■联合型单元测试用来测试那些必须进行协作或者存储状态的组件。[1].参见Java EE 6教程中的“What Are RESTful Web Services?”,http://mng.bz/fIa2。[2].参见Java EE 6教程中的“Hibernate ORM: What Is Object/Relational Mapping?”,http://hibernate.org/orm/what-is-an-orm。[3].参见Java EE 6教程中的“Introduction to the Java Persistence API”,http://mng.bz/Cy69。2  测试下的应用本章要点:■浏览示例应用。■了解代码的核心部分。■通过Java EE和Spring Boot开发微服务。第1章介绍了微服务的基本概念和架构。学习这些内容可以帮助你了解在微服务架构中将会遇到什么类型的测试。本章会介绍一个贯穿全书的应用,并通过这个应用来展示微服务架构下的开发和测试过程。我们的目标是提供一个易于理解的样例,以帮助你通过这个样例来了解此后将会遇到的各测试类型。我们会尽可能地遵循微服务架构的最佳实践,但是出于教学目的,会在一些地方做出适当的调整来保证样例可以足够简洁。比如,我们会用到比常规项目更多的技术种类,同时会减少微服务中从测试角度看来并不会提供额外价值的过多层级。在这个例子中我们会指出使用某一种方法的原因,并讨论如何在现实世界的编程中应用它们。我们始终会提供一个推荐的方法,但是作为一个开发者,选择使用哪种合适的工具依然是你的责任。2.1 准备开始“Gamer”这个样例应用是一个简单的玩家门户软件。它的目的是为玩家提供游戏软件的重要信息,并使得他们可以观看所玩游戏的视频,以及对他们玩过的游戏进行评论和打分。尽管这个应用本质上并不复杂,但是它覆盖了所有我们需要在微服务架构中展示的主题。在本书剩下的部分,我们将会指导你如何使用多种测试技术来测试微服务应用。

为了帮助你对玩家可能的行为有个全局的概览,我们首先会展示一些Gamer应用的使用案例。玩家们可能会做出以下行为:

■通过名字来搜索游戏,并得到一个符合他们兴趣的游戏列表。

■阅读游戏的重要信息,例如发行日期和游戏支持的平台。

■阅读其他玩家的游戏评论来帮助他们决定是否会喜欢这个游戏并进行购买。

■编写游戏评论,其他玩家可以从他们的评价中获益。

■对游戏进行星级评分,并快速查找到评分最高的游戏。

■观看与游戏相关的视频,例如试玩、教程以及真实的游戏直播。

首先让我们来定义一下应用所需要的数据结构。在这里我们不会关注太多的技术细节,而仅仅关注概念上的数据模型。

一个主要的数据结构是游戏,表2.1展示了游戏的组成。表2.1 game(游戏)的组成

表2.2展示了发行日期的组成。表2.2 ReleaseDate(发行日期)的组成

表2.3展示了评论的组成。表2.3 comment(评论)的组成

现在你已经了解了所有Gamer应用的数据结构,我们可以更深入地来了解这个应用的架构了。2.2 准备工作

本书并不是一部Java教程。如果你对Java这门编程语言并不了解,那么接下来的过程对你来说可能会很痛苦。我们希望下面展示的信息能够对各个阶段的读者都有所帮助。Java教程(https://docs.oracle.com/javase/tutorial)对任何一个Java开发者来说都是极为出色的参考资料。

本书也不是一部学术著作。本书的大部分作者都是开发者,而且对于有的作者来说,英语并不是自己的母语。我们喜欢亲自动手实践,希望你也是这样的。我们希望你能保持开放的心态,因为并不是所有人都认同我们的观点。并没有任何一种方法是完全正确或者完全错误的,我们的信息只是给你的创造性思维提供原材料。

说明 由于打印的原因,本书中大部分代码的格式受到了限制,这可能导致布局有些冗长。在使用这些代码时,你可以根据自己的偏好对格式进行修改。2.2.1 Java Development Kit

你需要最新的SE(Standard Edition)8版本的Java Development Kit(JDK)来编译和运行本书中的代码。你可以从http://mng.bz/83Ct获得最新的Oracle JDK(我们推荐使用这个发行版)或者从http://openjdk.java.net获取OpenJDK。

运行下面的命令来测试Java是否被正确安装,根据你所安装的版本,屏幕中将会打印出和下面类似的结果:2.2.2 构建工具

我们使用Apache Maven(https://maven.apache.org)和Gradle(https://gradle.org)来构建项目模块。确保你已经按照网站上的指令安装了对应的工具。

运行下面的命令来测试Maven是否被正确安装:

运行下面的命令来测试Gradle是否被正确安装:2.2.3 环境变量

应用的完整功能依赖两个API密钥来访问远程资源。由于API密钥是以每个用户的个人账号来进行注册的,因此在这里我们不能将密钥公开。

本书中提供的大部分测试样例并不依赖这些密钥,但是如果你想试验一下这些代码,我们建议你获取一份密钥并设置对应的环境变量。你可以访问https://developers.google.com/youtube/v3/getting-started并根据对应的提示来获取YouTube的API密钥。若要获取互联网游戏数据库(IGDB)的API密钥,则可以参考https://igdb.github.io/api/about/welcome或者直接到https://api.igdb.com进行注册。

当你获得自己的API私钥后,需要将其加到环境变量里。在Linux系统下,在/home/profile 文件中加入下面的内容:

Windows用户需要根据下面的链接来设置环境变量:http://[1]mng.bz/1a2K。2.2.4 集成开发环境(IDE)

IDE对于查看代码来说并不是必需的,记事本程序也可以完成同样的查看功能。

你可以使用任何自己喜欢的IDE(需要支持Maven和Gradle项目)。如果你需要添加断点来跟踪程序的执行路径,那么我们建议你使用一个IDE来做这件事情。

我们测试了下面几个IDE,它们的排名不分先后:

■IntelliJ IDEA(www.jetbrains.com/idea/)

■NetBeans(https://netbeans.org)

■Eclipse(www.eclipse.org/downloads)2.3 架构

正如我们在本章开头所说的那样,Gamer应用是一个遵循微服务架构的应用。我们需要做的第一件事情就是分辨构成这个应用需要哪些服务。对于这个应用,我们根据其业务范围,将其切分成4个独立的微服务:

■游戏服务—提供所有和游戏相关的信息,包括通过特定的名字来检索游戏ID,并根据游戏ID返回对应信息的功能。

■评论服务—提供对游戏进行评分、评论的功能,以及获取游戏评分及评论的功能。

■视频服务—提供游戏最受欢迎视频的功能。

■聚合服务—提供调用上述提到的服务并将结果数据整合统一返回的功能。Gamer应用的模式如图2.1所示。图2.1 Gamer应用的模式

正如你所看到的那样,前端(通常是一个浏览器)消费Gamer API所提供的信息。聚合服务提供应用的接入点,并和游戏服务、视频服务以及评论服务通信来获取必要的游戏数据。聚合服务层将所有的数据整合成一个响应并返给前端。现在你已经了解了这个应用的架构以及为什么需要以这种方式组织服务的技术细节。

说明 首先,在通过命令行或者IDE构建应用的时候请先忽略那些测试。出于展示以及练习的目的,代码中所提供的测试并不完整。在阅读本书的过程中随着知识的不断增长,你将可以更好地理解并扩展这些样例代码。2.3.1 游戏服务

使用下面的命令来安装游戏服务:

游戏服务是一个运行在WildFly Swarm上的Java EE 7应用,它负责提供所有与游戏相关的信息。它主要提供以下两种操作方法来获取游戏信息。

■通过游戏标题(不同游戏可能包含相同的标题)获取游戏列表。该方法只提供最基本的游戏信息:游戏ID以及游戏标题。

■通过一个已知的游戏ID,返回一个游戏的详细信息。

你可能已经注意到了,我们没有提供插入游戏的功能。这是因为我们的游戏服务只是一个外部服务的代理或者缓存。外部服务指的是那些并不在我们的应用范围内,由第三方开发并维护的服务,而我们只是一个单纯的订阅者。典型的外部服务例子有搜索引擎、天气预报以及地理位置计算。

我们的游戏服务样例依赖于互联网游戏数据库(IGDB,www.igdb.com),它提供我们所需要的所有游戏数据。

互联网游戏数据库API

IGDB是一个视频游戏数据库,致力于为游戏消费者以及视频游戏专家提供服务。除了提供一个可以获取游戏信息的网站之外,其还提供了一个公有的REST API(www.igdb.com/api)帮助你来访问网站所注册的游戏信息。

为了获得REST API的授权,你需要在网站上进行注册并获得一个新的API密钥。这个密钥必须包含在每个请求的HTTP头部中。

在本书中我们会提供更多如何使用IGDB REST API的信息,包括如何进行身份验证,以及访问资源接入点所必需的数据格式。

当你需要依赖第三方服务的时候,尽可能地缓存第三方数据是十分重要的,主要有以下3个原因:

■可以避免外部网络请求,以提升响应速度。

■如果第三方对API访问数量有限制,通过这种方式可节省你的访问量配额。

■如果外部服务发生了故障,你的应用依然可以通过缓存数据对外提供服务。

警告 通常来说,我们只在外部系统数据变化不频繁或者可以在本地系统复制所有数据的情况下使用缓存。为了维护和外部数据的一致性,你需要制定缓存的刷新策略来保证数据的实时性。出于简化的目的,本书提供的样例应用中没有实现刷新策略,但是在真实世界的场景中,你需要仔细考虑缓存的刷新策略。

在本书提供的微服务中,我们使用了一个轻量级SQL数据库H2(www.h2database.com/html/main.html)来实现缓存持久化层系统。游戏服务中使用的ER (entity-relationship)模型包括了4个实体,如表2.4到表2.7所示。图2.2以图形化的形式展示了这些实体。表2.4 Game表表2.5 ReleaseDate表表2.6 Publisher表表2.7 Developer表图2.2 Gamer应用的实体关系图

图2.2所展示的ER模型展示了一个游戏包含一个标题以及一个封面,每个游戏由一个或多个开发者开发,且有一个或多个发行方,以及针对不同平台有零个或多个发行日期。

说明 还有很多其他的缓存框架可以很好地满足微服务框架的需求,例如Infinispan、Hazelcast以及Redis。它们不仅提供可以使缓存刷新的逻辑变得更简单的到期时间(TTL)功能,并且可以在分布式环境下运行,十分适合微服务的架构。出于教学的目的,本书中我们使用一个SQL数据库。它的实现方式很简单,并且相关的技术对读者来说也更为熟悉。同时这也使得我们可以引入一个重要的功能:

试读结束[说明:试读内容隐藏了图片]

下载完整电子书


相关推荐

最新文章


© 2020 txtepub下载