• 0
  • 0
分享

  前言

  在我们写端到端测试之前,我们应该明确我们是基于一个用户的角度去测试我们的页面,所以这无关我们的所有源码,我们应该只专注于浏览器所呈现给我们的资源,包括页面上的element、控制台中network中的所有的请求以及导航栏上的url信息,这是我们可以去测试和观察到的所有的点。

  语法实战

  spec基本结构

// https://docs.cypress.io/api/introduction/api.html
import { DEV_SERVER } from '../config/conf'
describe('主页', () => {
  it('Home', () => {
    cy.visit('/')
    cy.contains('h1', 'QDeploy 智能安装部署平台')
    cy.get('button').click()
    cy.url().should('eq', `${DEV_SERVER}steps/selectMode`)
  })
})


  这里举一个最简单的例子,和单元测试一样,首先要把所有的用例包裹在一个describe中。在用例中先用cy.visit()方法访问地址,这里后面只加了/是因为baseUrl已经设置过了的原因。

  使用cy.contains()或者cy.get()去抓取DOM并进行断言,Cypress中默认包含的断言库为Chai。由于设有异步等待的机制,所以我们可以毫无顾及地去写下一步的操作,包括button的点击事件和跳转之后url的判断。

  生命周期

  在一个测试集合中,我们也可以加入自身的生命周期,这些生命周期主要是针对每个测试用例来执行的,包括beforeEach、beforeAll、afterEach、afterAll。我在这个测试集合中主要用到了beforeEach这个声明周期,在每个测试用例开始之前我都对我需要的DOM进行抓取并取一个别名,这样我方便其他用例需要时就不需要再反复去寻找这个节点对象了。

  beforeEach(() => {
   cy.visit('/#/steps/selectMode')
   cy.get('.one__item__right').eq(0).find('.item__right__btn').eq(0).as('hasConfigFile')
   cy.get('.one__item__right').eq(0).find('.item__right__btn').eq(1).as('notConfigFile')
   cy.get('.one__item__right').eq(1).find('.item__right__btn').eq(0).as('hasSystem')
   cy.get('.one__item__right').eq(1).find('.item__right__btn').eq(1).as('notSystem')
   cy.get('.btn__next').as('next')
   cy.get('.item__upload__text').as('fileName')
 })

  在取了别名之后其他用例只需要调用cy.get('@name')就可以取到相应别名的DOM元素。

  模拟请求

  在我们测试的时候总是会免不了一些请求的发出,在Cypress中由于是真实的浏览器环境,所以所有的请求都会被正常发出,但是有些时候我们需要mock掉一些请求来观察DOM的反馈是否符合预期,这里就需要引入一个比较重要的概念——存根stub。

  不同于单元测试的mock,我认为在单元测试中更类似于axios中的拦截器,对整个请求的代码层面进行一个拦截后返回一个相同格式的对象骗过,而在端到端测试中因为我们无法对项目本身的源码下手,所以我们只能从浏览器层面去模拟,在这里的存根我的理解是在页面发出请求之前,先对一个API做一个标记,当浏览器触发这个方法并发送请求后使用标记后的模拟请求返回并进行后续的断言操作,我们来看一下代码。

describe('installSystem', () => {
  it('寻找节点失败', () => {
    cy.server()
    cy.route({
      method: 'DELETE',
      url: 'api/find/node',
      status: 200,
      response: {
        data: {},
        error_code: 1,
        message: 'fuck'
      }
    })
    cy.visit('/#/steps/installSystem')
    cy.wait(1000)
    cy.get('.pop_content_confirm').find('div').find('div').contains('寻找节点出错')
  })
})

  在这个例子中,由于请求在页面刚被挂载后就被触发了,也就是说整个请求是写在mounted这个声明周期中的,所以我们需要在访问页面之前就对这个需要被mock的api做一个stub。

  首先我们使用cy.server()声明一个mock的请求。

  然后使用cy.route()去描述我们需要模拟的api的具体信息,在里面可以填写很多的配置,包括请求的方法method,请求的地址url,请求返回的状态码status以及最后返回的response body,在这里由于项目本身中还定义了error_code状态码,所以对于这一个请求所具备的状态我们就需要写很多个测试用例的组合去断言是否符合我们的预期。

  然后因为请求已经被我们存根了,我们再去使用cy.visit()访问一次页面就可以看到我们所需要被模拟的请求已经被存根并且成功模拟了。

1-1.png

  到目前为止我们就已经非常成功地对一个API进行模拟请求了,对比起单元测试还是方便了不少的。

  文件上传

  当然我们的网页不仅仅只有一些点击事件,我们通常还有很多特殊的操作,比如拖拽以及文件的上传等等。这里我讲解一下我遇到过的文件上传的模拟问题。

  例如我们有一个这样的场景:

1-2.png

  我图中的这个按钮中我们所使用的是input [type="file"]这个原生的输入框,所以我们无法通过value本身来获取文件并去模拟,我们需要模拟整个真实的上传操作,而显然在我们点击按钮并选择我们本地的文件是Cypress所无法做到的,毕竟不是外挂。所以我们需要自定义一条命令去完成这一步操作,这里我参考了github中Cypress官方下的一个issue,详见Adding Ability to Submit File to Input Element From Local Filesystem #170。

  7. 首先我们需要去tests -> e2e -> support -> commands.js中添加一条自定义的指令:

// 上传文件命令
Cypress.Commands.add('upload_file', (fileName, selector) => {
 cy.get(selector).then(subject => {
   cy.fixture(fileName).then((content) => {
     const el = subject[0]
     const testFile = new File([content], fileName)
     const dataTransfer = new DataTransfer()
     dataTransfer.items.add(testFile)
     el.files = dataTransfer.files
   })
 })
})

  需要声明的是我们在这个文件中我们不仅仅可以自定义指令,并且还可以更改已经存在的api,这些在文件被创建时会在开头有备注说明,这里就不展示了。

  8. 然后我们需要去tests -> e2e -> fixtures中添加我们需要上传的文件,我这里准备了一个Excel文件:

1-3.png

  最后我们来展示一下上传文件的代码段:

  it('上传文件选择有安装系统', () => {
  cy.upload_file('test.xlsx', 'input[type=file]')
  cy.get('@fileName').contains('test.xlsx')
  cy.get('@next').should('not.be.disabled')
  cy.get('@hasSystem').eq(0).click()
  cy.get('@next').click()
  cy.get('.pop_tool').find('button').eq(1).click()
  cy.url().should('eq', `${DEV_SERVER}steps/createClusters`)
})

  我们运行一下测试用例并且观看一下快照库:

1-4.png

  到这一步为止可以看到我们的文件已经上传成功了并且文件名已经被成功渲染到了页面上。


作者:钟大灵    

来源:http://www.51testing.com/html/87/n-5099587.html

  • 【留下美好印记】
    赞赏支持
登录 后发表评论
+ 关注

热门文章

    最新讲堂

      • 推荐阅读
      • 换一换
          • 环境准备:操作系统:Windows7集成开发环境:eclipse+pydev一、编写测试用例点击返回目录可以直接使用Python自带的单元测试框架unittest来编写自动化测试用例,利用其组织测试用例,断言预期结果,以及批量执行测试用例等功能,可以很好的进行Web自动化测试的开发。可以直接查看我另外一篇博文总结:unittest单元测试框架总结里边详细总结了使用unittest框架编写测试用例思路(模板)和使用unittest框架编写测试用例实例。目录结构如下组织:如上图:test_case目录下有两个测试用例,其中test_baidu.py实例代码在博文“unittest单元测试框架总结”...
            0 0 1892
            分享
          • 看到这个问题你是不是已经笑了?当然我也做好了挨喷的准备了。我搜了一下知乎,同样的问题可以翻好几页,回答的观点也各式各样,但是没有一个统一的高赞答案,今天我姑且谈谈我的个人看法,欢迎大家一起讨论。来来来,坐好啦,先给大家说说我自己关于选择的故事。一、学习 Java 有前途么?我是 2005 年开始学习 Java 的,应该是相当早了(暴露年龄了),那时的我还没大学毕业,所以在学习前、学习中、学习后的所有阶段,「Java 是否有前途」的问题,一直让我惶惶不得终日,我当时也上网搜了很多次这个问题,看了几乎所有的观点,结果和现在一样,并没有一个统一的高赞答案。有说很有前途的,毕竟当时的 Python 还...
            3 1 1573
            分享
          •   最近,在做测试开发平台的时候,需要对测试用例的列表进行后端分页,在实际去写代码和测试的过程中,发现这里面还是有些细节的,故想复盘一下,所以有了这篇文章。  分页的目的  做个假设,加入用例库有 1W 条数据,如果想要以列表形式展示,一次性返回 1W 条数据;这样做有两个弊端:  1.这样写出来的接口会慢。想一下如果随着时间的发展,这个数量变成了 10W、100W 该怎么办?  2.对前端不友好。用前端渲染 1W 条数据,有理智的开发都不会这么做。  所以,我们做分页的目的,主要是为了性能的提高,提高使用体验。  我这边开发语言是Python,Web框架用的是 Tornado,后台数据库用的...
            0 0 997
            分享
          •   北京时间12月21日早间消息,据报道,知情人士透露,在推特老板兼CEO埃隆·马斯克(ElonMusk)发起“是否该卸任推特CEO”的投票之前,他就已主动为该公司物色一名新CEO。  马斯克今年10月以440亿美元收购推特。他之前表示,自己只会暂时担任该公司CEO。他还于11月在法庭上说:“我预计将逐渐减少在推特的时间,找另外一个人来运营。”但他上周日又发推文称:“还没有继任者。”  马斯克写道:“问题不是找到一个CEO,而是找到一个能让推特活下去的CEO。”  这位亿万富豪上周日发布了一次非正式投票,让推特用户通过这种方式决定他是否应当继续担任该公司CEO。结果显示,在1700万参与投票的...
            0 0 1119
            分享
          •   产品功能测试与系统测试有什么区别呢?这常常是让测试人员非常迷惑的地方,有的小伙伴认为系统测试应该包含功能测试在内,有的小伙伴则认为这是两个独立的测试类别。而从很多方面来说,产品功能测试与系统测试还是有很大区别的,小编就简单分析一下,两者的相似与不同之处,以及如何做好功能测试?  一、产品功能测试与系统测试分别是什么意思?  产品功能测试是指对软件产品或者程序各项功能进行验证,根据功能测试用例进行逐项检查,看各项功能是否能正常使用,并在此过程中发现软件存在的潜在问题。通常功能测试不考虑系统内部的结构及代码情况,所以也称为黑盒测试。  系统测试是指对产品的硬件软件以及应用场景进行系统的测试,模...
            0 0 1240
            分享
      • 51testing软件测试圈微信