对于测试工作而言,微服务架构对于传统的架构引入了更多的复杂性。一方面,随着微服务数量的增长,测试的用例也会持续增长;另一方面,由于微服务之间存在着一定的依赖性,在测试过程中如何来处理这些依赖,就变得极为重要。
多端合一成都响应式网站建设公司:PC+平板+手机,同一后台修改数据多端同步更新提交您的需求,获取网站建设与营销策划方案报价,我们会在1小时内与您联系!
本节将从微服务架构的单元测试、集成测试和系统测试三个方面来展开讨论。
单元测试要求将测试范围局限在服务内部,这样可以保证测试的隔离性,将测试的影响减少到最小。在实际编码之前,TDD要求程序员先编写测试用例。当然,一开始,所有的测试用例应该是全部失败的,然后再写代码让这些测试用例逐个通过。也就是说,编写足够的测试用例使测试失败,编写足够的代码使测试成功。这样,程序员编码的目的就会更加明确。
当然,编写测试用例并非是TDD的全部。在测试成功之后,还需要对成功的代码及时进行重构,从而消除代码的“坏味道”。
1.为什么需要重构代码
所谓重构,简而言之,就是在不改变代码外部行为的前提下,对代码进行修改,以改善程序的内部结构。
重构的前提是代码的行为是正确的,也就是说,关于代码功能已经经过测试,并且测试通过了,这是重构的前提。只有正确的代码才有重构意义。
那么,既然代码都正确了,为什么还要花费时间再去改动代码、重构代码呢?
重构的原因是大部分程序员无法写出完美的代码。他们无法对自己编写的代码完全信任,这也是需要对自己所写的代码进行测试的原因,重构也是如此。归纳起来,以下几方面是软件需要重构的原因。
正是目前软件行业这些事实的存在,促使重构成为TDD中必不可少的实践之一。程序员对程序进行重构,是出于以下的目的。
2.何时应该进行重构
那么,程序员应该在何时进行重构呢?
3.代码的“坏味道”
如果一段代码是不稳定或有一些潜在问题的,那么代码往往会包含一些明显的痕迹,就好像食物要腐坏之前,经常会发出一些异味一样,这些痕迹就是代码“坏味道”。以下就是常见的代码“坏味道”。
4.减少测试的依赖
首先,我们必须承认,对象间的依赖无可避免。对象与对象之间通过协作来完成功能,任意一个对象都有可能用到另外对象的属性、方法等成员。但同时也认识到,代码中的对象过度复杂的依赖关系往往是不提倡的,因为对象之间的关联性越大,意味着代码改动一处,影响的范围就会越大,而这完全不利于系统的测试、重构和后期维护。所以在现代软件开发和测试过程中应该尽量降低代码之间的依赖。
相比于传统JavaEE的开发模式,DI(依赖注人)使代码更少地依赖容器,并削减了计算机程序的耦合问题。通过简单的new操作,构成程序员应用的 POJO对象即可在JUnit或TestNG下进行测试。即使没有Spring或其他loC容器,也可以使用mock来模拟对象进行独立测试。清晰的分层和组件化的代码将会促进单元测试的简化。例如,当运行单元测试的时候,程序员可以通过stub或mock来对DAO或资源库接口进行替代,从而实现对服务层对象的测试,这个过程中程序员无须访问持久层数据。这样就能减少对基础设施的依赖。
在测试过程中,真实对象具有不可确定的行为,有可能产生不可预测的效果(如股票行情、天气预报),同时,真实对象存在以下问题。
正是由于上面真实对象在测试的过程中存在的问题,在测试中广泛地采用mock测试来代替。
在单元测试上下文中,一个mock对象是指这样的一个对象——它能够用一些“虚构的占位符”功能来“模拟”实现一些对象接口。在测试过程中,这些虚构的占位符对象可用简单方式来模仿对于一个组件期望的行为和结果,从而让程序员专注于组件本身的彻底测试,而不用担心其他依赖性问题。
mock对象经常被用于单元测试。用mock对象来进行测试,就是在测试过程中,对于某些不容易构造(如HttpServletRequest必须在Servlet容器中才能构造出来)或不容易获取的比较复杂的对象(如JDBC中的ResultSet对象),用一个虚拟的对象( mock对象)来创建以便测试的测试方法。
mock最大的功能是把单元测试的耦合分解开,如果编写的代码对另一个类或接口有依赖,它能够模拟这些依赖,并验证所调用的依赖行为。
mock对象测试的关键步骤如下。
目前,在Java阵营中主要的mock测试工具有Mockito、JMock、EasyMock 等。
5.mock与stub的区别
mock和 stub都是为了替换外部依赖对象,mock不是stub,两者有以下区别。
现在通过一个例子来看看mock与 stub之间的区别。假如程序员要给发送mail的行为做一个测试,就可以像下面这样写一个简单的stub。
- //待测试的接口
- public interface Mailservice(){
- public void send(Message msg);
- }
- /lstub测试类
- public class MailServiceStub implements MailService i
- private List
messages = new ArrayList (); - public void send (Message msg){
- messages.add (msg);
- }
- public int numberSent( {
- return messages.size();
- }
- }
- }
也可以像下面这样在stub 上使用状态验证的测试方法。
- public class orserStateTester{
- Order order = new Order(TALISKER, 51);
- MailServiceStub mailer = new MailserviceStub();
- order.setMailer(mailer);
- order.fill (warehouse);
- //通过发送的消息数来验证
- assertEquals(1 , mailer.numberSent();}
- }
当然这是一个非常简单的测试,只会发送一条message。在这里程序员还没有测试它是否会发送给正确的人员或内容是否正确。
如果使用mock,那么这个测试看起来就不太一样了。
- lass OrderInteractionTester. ..
- public void testorderSendsMail工fUnFilled() {
- Order order =new Order (TALISKER ,51);
- Mock warehouse = mock(Warehouse.class);
- Mock mailer = mock(MailService.class);
- order.setMailer((Mailservice)mailer.proxy());
- order.expects(once()).method ("hasInventory").withAnyArgument()
- .will(returnvalue(false));
- order.fill((Warehouse) warehouse.proxy()
- }
在这两个例子中,使用了stub和mock来代替真实的MailService对象。所不同的是,stub使用的是状态确认的方法,而mock使用的是行为确认的方法。
想要在stub中使用状态确认,需要在stub中增加额外的方法来协助验证。因此stub实现了MailService但是增加了额外的测试方法。
集成测试也称组装测试或联合测试,可以说是单元测试的逻辑扩展。它最简单的形式是把两个已经测试过的单元组合成一个组件,测试它们之间的接口。从使用的基本技术上来讲,集成测试与单元测试在很多方面都很相似。程序员可以使用相同的测试运行器和构建系统的支持。集成测试和单元测试一个比较大的区别在于,集成测试使用了相对较少的mock。
例如,在涉及数据访问层的测试时,单元测试会简单地模拟从后端数据库返回的数据。而集成测试时,测试过程中则会采用一个真实的数据库。数据库是一个需要测试资源类型及能暴露问题的极好的例子。
在微服务架构的集成测试中,程序员更加关注的是服务测试。
1.服务接口
在微服务的架构中,服务接口大多以RESTfulAPI的形式加以暴露。REST是面向资源的,使用HTTP协议来完成相关通信,其主要的数据交换格式为JSON,当然也可以是XML、HTML、二进制文件等多媒体类型。资源的操作包括获取、创建、修改和删除资源,它们都可以用HTTP协议的GET、POST、PUT和DELETE方法来映射相关的操作。
在进行服务测试时,如果只想对单个服务功能进行测试,那么为了对其他相关的服务进行隔离,则需要给所有的外部服务合作者进行打桩。每一个下游合作者都需要一个打桩服务,然后在进行服务测试的时候启动它们,并确保它们是正常运行的。程序员还需要对被测试服务进行配置,保证能够在测试过程中连接到这些打桩服务。同时,为了模仿真实的服务,程序员还需要配置打桩服务,为被测试服务的请求发回响应。
下面是一个采用Spring 框架实现的关于“用户车辆信息”测试接口的例子。
- import org.junit.*;
- import org.junit.runner.*;
- import org.springframework.beans.factory.annotation.*;
- import org.springframework.boot.test.autoconfigure.web.servlet.*;
- import org.springframework.boot.test.mock.mockito.*;
- import static org.assertj.core.api.Assertions.*;
- import static org.mockito.BDDMockito.*;
- import static org.springframework.test.web.servlet.request.MockMvc
- RequestBuilders.*;
- import static org.springframework.test.web.servlet.result.MockMvc
- ResultMatchers.*;
- @RunWith(SpringRunner.class)
- @WebMvcTest(UserVehicleController.class)
- public class MyControllerTests{
- @Autowired
- private MockMvc mvc;
- @MockBean
- private UserVehicleService userVehicleService;
- @Test
- public void testExample( throws Exception {
- given(this.userVehicleService.getVehicleDetails("sboot"))
- .willReturn(new VehicleDetails("BMW","X7"));
- this.mvc.perform(get("/sboot/vehicle").accept(MediaType.TEXT_
- PLAIN))
- .andExpect(status().isok()).andExpect(content().
- string("BMW x7"));
- )
- }
- }
在该测试中,程序员用mock模拟了/sboot/vehicle接口的数据VehicleDetails("BMW","X7"),并通过MockMvc来进行测试结果的判断。
2.客户端
有非常多的客户端可以用于测试RESTful服务。可以直接通过浏览器来进行测试,如在本书前面介绍过的RESTClient、Postman等。很多应用框架本身提供了用于测试RESTful API的类库,如Java平台的像Spring的RestTemplate 和像Jersey的Client API等,.NET平台的RestSharp ( http:1restsharp.org)等。也有一些独立安装的REST测试软件,如SoapUI ( ttps:/www.soapui.org ),当然最简洁的方式莫过于使用cURL在命令行中进行测试。
下面是一个测试Elasticsearch是否启动成功的例子,可以在终端直接使用cURL来执行以下操作。
- scurl 'http://localhost:9200/?pretty'
cURL提供了一种将请求提交到Elasticsearch的便捷方式,然后可以在终端看到与下面类似的响应。
- "cluster name": "elasticsearch",
- "cluster uuid" :"uqcQAMTtTIO6CanROYgveQ",
- "version":{
- "number": "5.5.0",
- "build_hash":"260387d" ,
- "build_date":"2017-06-30T23:16:05.735Z"",
- "build_snapshot" :false,
- "lucene version":"6.6.O"
- },
- "tagline":"You Know, for Search"
- }
引入微服务架构之后,随着微服务数量的增多,测试用例也随之增多,测试工作也越来越依赖于测试的自动化。Maven或Gradle等构建工具,都会将测试纳入其生命周期内,所以,只要写好相关的单元测试用例,单元测试及集成测试就能在构建过程中自动执行,构建完成之后,也可以马上看到测试报告。
在系统测试阶段,除了自动化测试外,手工测试仍然是无法避免的。Docker等容器为自动化提供了基础设施,也为手工测试带来了新的变革。
在基于容器的持续部署流程中,软件会经历最终被打包成容器镜像,从而可以部署到任意环境而无须担心工作变量不一致所带来的问题。进入部署阶段意味着集成测试及单元测试都已经通过了。
但这显然并不是测试的全部,很多测试必须要在上线部署后才能进行,如一些非功能性的需求。
同时,用户对于需求的期望是否与最初的设计相符,这个也必须要等到产品上线后才能验证。所以,上线后的测试工作仍然是非常重要的。
1.冒烟测试
所谓冒烟测试,是指对一个新编译的软件版本在需要进行正式测试前,为了确认软件基本功能是否正常而进行的测试。软件经过冒烟测试之后,才会进行后续的正式测试工作。冒烟测试的执行者往往是版本编译人员。
由于冒烟测试耗时短,并且能够验证软件大部分主要的功能,因此在进行CI/CD每日构建过程中,都会执行冒烟测试。
⒉蓝绿部署
蓝绿部署通过部署新旧两套版本来降低发布新版本的风险。其原理是,当部署新版本后(绿部署),老版本(蓝部署)仍然需要保持在生产环境中可用一段时间。如果新版本上线,测试没有问题后,那么所有的生产负荷就会从旧版本切换到新版本中。
以下是一个蓝绿部署的例子。其中,vl代表的是服务的旧版本(蓝色),v2代表的是新版本(绿色),如图4-2所示。
这里面有以下几个注意事项。
3.A/B测试
A/B测试是一种新兴的软件测试方法。A/B测试本质上是将软件分成A、B两个不同的版本来进行分离实验。AB测试的目的在于通过科学的实验设计、采样样本、流量分割与小流量测试等方式来获得具有代表性的实验结论,并确保该结论在推广到全部流量之前是可信赖的。例如,在经过一段时间的测试后,实验结论显示,B版本的用户认可度较高,于是,线上系统就可以更新到B版本上来。
4.金丝雀发布
金丝雀发布是增量发布的一种类型,它的执行方式是在原有软件生产版本可用的情况下,同时部署一个新的版本。这样,部分生产流量就会引流到新部署的版本,从而来验证系统是否按照预期的内容执行。这些预期的内容可以是功能性的需求,也可以是非功能性的需求。例如,程序员可以验证新部署的服务的请求响应时间是否在1秒以内。
如果新版本没有达到预期的效果,那么可以迅速回溯到旧版本上去。如果达到了预期的效果,那么可以将生产流量更多地引流到新版本上去。
金丝雀发布与A/B测试非常类似,两者往往结合使用。而与蓝绿部署的差异在于,金丝雀发布新旧版本并存的时间更长久一些。
本文题目:十年架构师耗尽心血带你如何进行微服务的单元、集成和系统测试?
分享地址:http://www.gawzjz.com/qtweb2/news5/5055.html
网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等
声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联