相信绝大多数从事测试行业的同志们对自动化测试有抱有一个美好的幻想,但是到底该怎么实现和落地呢? 接下来我将结合分层测试金字塔和实际案例为你们分享:html
项目背景: 案例项目是ThoughtWorks的内部招聘看板系统,主要服务于ThoughtWorks在19个国家49家办公室的招聘团队。核心功能是为HR们提供一个展现候选人的信息的面板,生成和招聘数据有关的报表。python
提及自动化测试不得不说起测试金字塔,这种三角形的结构主要为咱们展现了一个健康的自动化测试体系应该是什么样子的。如图所示,金字塔的从上往下依次是UI测试,接口测试,单元测试, 越在高层影响就越大,花费的时间和精力就越多。图示的测试金字塔只是一种形态示例,不一样项目的金字塔的实现内容可能略有区别。web
在《google软件测试之道》曾写过: 对于google产品,70%的投入为单元测试,20%为集成、接口测试,10%为UI层的自动化测试。sql
在测试金字塔中能够看到,UI层面的自动化影响大、变化多、可维护成本高,所占比例也最少。因此在咱们看来,UI层面的自动化应该是一些从高层次上验证一些happy pass,确保咱们的核心功能可用。数据库
拿案例项目来举例,咱们识别出最核心的功能就是经过过滤器筛选数据来进行展现或计算,因此咱们的用例主要会去验证操做这些过滤器可否筛选出正确的数据。 在技术选型上,咱们 主要用到BDD的思想,选用cucumber + capybara这套体系去作UI层的自动化。设计模式
Scenario: test office filter in China region Given I login to GoHire website When I select "China" region And I select "Chengdu" option Then I can only see all the application cards in chengdu office
UI层自动化的小经验api
UI层自动化的适用场景是作核心功能的回归测试和冒烟测试,因此在实施过程当中,要注意不要把全部的用例都堆砌在UI层,而是尽量放到接口测试和单元测试中去作。app
在代码层面,咱们能够遵循page object的设计模式,避免在测试代码中直接操做html元素。这样就能够减小重复的代码并提升代码的可维护性。框架
在案例项目中,接口测试主要分为两个部分。dom
在开发过程当中,测试人员会和开发合做去写接口的功能测试。
在整个功能大概完成,API已经基本肯定后,测试和开发一块儿结对写性能测试。
接口功能测试
在写功能测试的过程当中,咱们可能会和一些其余的模块或第三方API有依赖,在这种状况下,一般能够经过Mock的方法去解决。
如案例项目中有一个测试场景是:调用一个第三方的API,当这个API出错时,须要接受到API返回的错误码并对错误码进行处理。在实现测试的时候,咱们没有办法让第三方API真正的挂掉并返回错误码,因此咱们只须要模拟这个请求出错,验证咱们代码已经对这种错误进行过处理。
def should_return_error_message_when_request_failed(self): event = { "body": { "action": "update_candidate", "payload": { "candidate": { "id": 101 } } } } with requests_mock.mock() as request_mock: request_mock.get("http://api.gh.test/v1/candidates/101", text='', status_code=500) with self.assertRaises(Exception) as context: webhook.handle(event) self.assertTrue( '[request error][update_candidate] get candidate 101 from Harvest API failed' in context.exception)
接口性能测试
在功能大体完成后,咱们开始作API的性能测试。性能测试在这里的做用主要是获取咱们API现有的性能指标,造成一个对比的基线。在便于进行后期的优化的同时也有可能帮助咱们发现一些潜在的bug。
小故事:咱们在手工测试API的响应速度时,测试结果一切正常。当引入性能测试后就发现,这些接口在前期的响应时间确实很快,但是在请求了必定次数后会忽然变得很慢。通过调查咱们发现,这是由于咱们依赖了一个AWS的服务,这个服务有访问频率的限制,在最初的代码中,每次访问都会请求这些服务并读取这些服务的配置,这也就致使了测试发现的问题。后来咱们更改了方案,把读取配置这个操做放在系统初始化的时候去作,顺利的解决了这个问题。
性能测试咱们主要选用了Locust这个框架。如咱们的一个接口功能是:查询某个国家下的全部的候选人。 下面代码用例的意思就是, 同时配置50个客户端去访问API,每次随机请求某个国家的全部候选人信息,一共请求1000次,规定每次请求的时间最大不超过5s。
def get_applications_in_country(l): text = """Australia Brasil Canada China Chile Ecuador Germany India Italy Singapore Spain Turkey UK USA Uganda Thailand""" countries = text.strip().split() country = random.choice(countries) params = { "country": country, "status": "active" } with l.client.get("/getCandidates", params=params, headers={"x-api-key": API_KEY}, catch_response=True) as response: if "errorMessage" in response.text: response.failure("Error occurs in response: %s" % response.text) class UserBehavior(TaskSet): tasks = { get_applications_in_country: 1 }
function runLocustDataStoreService { validateEnvironment "HOST" "API_KEY" setupEnv CLIENTS=${CLIENTS:-"50"} HATCH_RATE=${HATCH_RATE:-"2"} NUM_REQUEST=${NUM_REQUEST:-"1000"} locust -f DataStoreService/locustfile.py --host ${HOST} --clients=${CLIENTS} --hatch-rate=${HATCH_RATE} --num-request=${NUM_REQUEST} --no-web --only-summary }
经过Locust跑完用例,咱们能够看到在console生成的report:
图片展现的是report的一部分,reqs表明对这个API的请求数目,fails记录了失败的次数,Avg、Min、Max、Mediam分别是每次请求响应时间的平均值、最小值、最大值和中位数。
单元测试是对软件中最小的测试单元进行验证,在这一层上发现问题时解决成本最低,测试用例的维护成本也不高,具备很好的投入产出比。通常状况下,咱们是须要开发人员在开发过程当中写单元测试。而做为一个QA,咱们更多的是一个单元测试的引导者:
和团队一块儿制定单元测试覆盖率的标准。
若是这是一个全新的项目,咱们能够把覆盖率设的相对高一点,如85%,这有利于咱们在前期就对代码质量作出保证。若是这是一个已经相对成熟的项目,因为前期根本没有单元测试,咱们能够先把要求设置的低一点,而后一步步的提高咱们的代码覆盖率。
为开发人员提供单元测试的用例。
咱们须要提早把须要验证的用例列在开发的任务卡片里面,这样能帮助开发更有效率的去完成咱们指望测试的用例 。
按期回顾开发人员写的单元测试。
这里并非要检查代码和具体实现,而是和开发一块儿去回顾看看单元测试的写法和角度是否是在同一认知上面。这样有助于整个团队创建一种质量保证的意识。
在具体实现上,咱们选择nose test这个工具去作单元测试,经过nose test的插件,咱们能够拿到单元测试覆盖率的报表,在第二个图中,咱们能够看到,没有被测试覆盖的代码会有红色的标记,这样就有利于咱们找到测试的遗漏点。
在写单元测试时,为了解决对数据库的依赖,咱们能够创建一个内存数据库去模拟真实数据库,便于咱们的测试用例能快速的运行。如在咱们的真实项目中,咱们的数据库选用的是亚马逊的RDS+Postgres,可是在作单元测试的时候咱们使用的sqlite+python绑定来模拟真实的数据库
只让这些自动化测试运行在本地IDE上是不够的,在咱们的项目中,咱们创建了一套持续集成部署的体系:
推送到代码到远端后会自动开始运行自动化测试和代码审查。
当单元测试经过后会自动部署到测试环境。
在部署完成后会自动生成测试报告。
小组全部成员会收到部署成功或失败的邮件提醒。
在工具的选择上,咱们的持续集成平台是ThoughtWorks的GoCD,其余相似的工具还有 jenkins,能够灵活的选用这些工具。
在实际的项目实施过程当中,咱们实际上是按照下面的步骤依次逐步实施咱们的持续集成自动化体系的:
在项目开始以前首先搭建持续集成的框架,第一次的时候先写一个最简单的单元测试,如1+1=2,确保能够在CI上运行测试,为后续的开发奠基基础。
开发在项目实现过程当中进行单元测试,每次开发推送代码时均可以自动运行单元测试和代码风格审查,当单元测试覆盖低于85%或代码风格检查不经过时,构建就会失败。
测试和开发在项目实现过程当中合做写接口层的功能测试。
功能开发大致完成后,测试和开发合做写接口的性能测试。
当项目发布以后,测试开始根据核心功能编写UI层面的自动化测试,也至关因而写项目的回归测试。
最后谈一点心得体会吧:
项目只有UI自动化测试是不够的,越低层的自动化测试反而越有意义。
自动化测试的目的是减小重复的手动测试的成本,使测试人员能够作更多有意义的事情,在实现自动化的过程当中,咱们花费的精力甚至更多。
测试并非越多越好,除了用例数量还要考虑维护代价。咱们但愿测试代码可以尽可能稳定,由于代码须要不断的被重构,若是发现重构一次代码就修改不少测试,那么这种测试可能会成为负担,也是一种坏味道。
测试人员在自动化测试落地的实践中,更多的是一个推进者而不是实现者,咱们须要帮助团队创建起一种质量保证的意识,而后共同实现自动化测试的落地。
做者简介:雲裳,ThoughtWorks质量分析师。以dev身份加入ThoughtWorks,因兴趣转型QA。 过去一年以QA身份服务于澳洲某大型电信公司,主导项目小组的质量分析保证工做,对角色转型和敏捷实践有着独特的看法。