你一个开发还搞不懂测试?速来

2,623 阅读8分钟

一、前言

问xdm一个问题:你们有没有发现现在的测试开发,啥都会,前后端都能写!!!

经常遇到测试同学有KPI,自己用Python 或者 Java + Vue去搞一些内部平台,遇到不会的,找熟悉的开发同学看一些问题。或者这就是内卷新高度?

但是该学还是学,人家卷我,我不能不卷人家。 作为前端,过去的我不懂单测、不懂接口自动化、UI自动化,毕竟都在写业务代码,很少去关心这些东西。编程范式里面就有一种测试驱动编程的思想,搞了一段时间的测试后,个人感觉对编码的习惯,影响还是比较大的。

二、测试模式

2.1 测试驱动编程 TDD

测试驱动开发(英语:Test-driven development,缩写为TDD)是一种软件开发过程中的应用方法,由极限编程中倡导,以其倡导先写测试程序,然后编码实现其功能得名。测试驱动开发始于20世纪90年代。测试驱动开发的目的是取得快速反馈并使用“illustrate the main line”方法来构建程序。

TDD 也有三层含义:

  • Test-Driven Development,测试驱动开发。
  • Task-Driven Development,任务驱动开发,要对问题进行分析并进行任务分解。
  • Test-Driven Design,测试保护下的设计改善。TDD 并不能直接提高设计能力,它只是给你更多机会和保障去改善设计。

简单说,就是先写测试代码,再写功能

2.2 行为驱动开发 BDD

行为驱动开发(英语:Behavior-driven development,缩写BDD)是一种敏捷软件开发的技术,它鼓励软件项目中的开发者、QA和非技术人员或商业参与者之间的协作。BDD最初是由Dan North在2003年命名[1],它包括验收测试和客户测试驱动等的极限编程的实践,作为对测试驱动开发的回应。在过去数年里,它得到了很大的发展[2]

简单说,BDD就是TDD的升级版。BDD将逻辑部分简单和自然化,用自然语言来描述,让开发、测试以及客户都能看得懂,让大家的行为保持一致

2.3 领域驱动设计 DDD

领域驱动设计(英语:domain-driven design,缩写 DDD)是软件代码的结构及语言(类别名称、类方法、类变量)需符合业务领域中的习惯用法。例如处理租赁业务的软件,其类型可以命名为LoanApplication及Customer,其方法可以用AcceptOffer及Withdraw。领域驱动设计的前提是:

  • 把项目的主要重点放在核心领域(core domain)和域逻辑
  • 把复杂的设计放在有界域(bounded context)的模型上
  • 发起一个创造性的合作之间的技术和域界专家以迭代地完善的概念模式,解决特定领域的问题

简单说就是针对不同领域,独立的去设计某些架构。这个和测试相关度不大,了解下

三、测试分类

1、单元测试

计算机编程中,单元测试(英语:Unit Testing)又称为模块测试 [来源请求] ,是针对程序模块软件设计的最小单位)来进行正确性检验的测试工作。程序单元是应用的最小可测试部件。在过程化编程中,一个单元就是单个程序、函数、过程等;对于面向对象编程,最小单元就是方法,包括基类(超类)、抽象类、或者派生类(子类)中的方法。

如果你正在使用函数式编程,一个单元最有可能指的是一个函数。你的单元测试将使用不同的参数调用这个函数,并断言它返回了期待的结果。

把代码看成一个个组件,对每个组件进行单独测试,组件内每一个函数的返回结果(或者dom的结构)是不是和期望值一样。一般基础库或者组件库都是必须要写的,不然新功能的上线可能就会导致不可预期的功能异常。

2、e2e端到端测试

E2E(end to end)端到端测试是最直观可以理解的测试类型。在前端应用程序中,端到端测试可以从用户的视角通过真实浏览器自动检查应用程序是否正常工作。

E2E 把整个系统当作一个黑盒,测试人员模拟真实用户在浏览器中操作 UI,测试在真实浏览器环境运行测试,测试出的问题可能是前端也可能是后端导致的

  • 优点: 真实的测试环境,相比集成测试和单元测试,更容易获得程序的信心
  • 缺点: 端到端测试运行不够快,调试比较困难

简单说,就是UI自动化测试,反复需要人点击测试的流程,可以用脚本代替。然后针对最终的结果做断言,看是否符合要求

四、实现

4.1 实现单元测试

笔者偷懒,没动手。。。

4.2 实现e2e

  • puppeteer [star 78k]
    活跃度高,社区资料丰富,对于前端易用性很强
  • phantomJs [star 29k]
    已经停止维护,并且环境安装复杂
  • selenium[star 24k] - 元老框架 虽然依然维护 但是相比 puppeteer 上手速度较慢 文档易读性较差 - 基于HTTP协议(单向通讯)
  • playwright [star 40k]
    • 微软出品,必属XX
    • 可直接通过开发者工具和浏览器交互,测试更方便
    • 基于Websocket(双向通讯)可自动获取浏览器实际情况
    • 缺点:不兼容老的Edge和IE11,移动端使用模拟器模式,无真机环境

UI自动化

const { chromium } = require('playwright');

(async () => {
  const browser = await chromium.launch();
  const page = await browser.newPage();
  await page.goto('https://www.baidu.com/');
  await page.screenshot({ path: `example.png` });
  await browser.close();
})();

自动脚本生成

# 安装playwright库 
pip install playwright 
# 安装浏览器驱动文件 
python -m playwright install 
#再安装 
playwright install
# 一键执行,生成自动化脚本
playwright codegen --target javascript -o 'my.ts' -b chromium https://www.baidu.com

生成的代码

const { chromium } = require('playwright');

(async () => {
  const browser = await chromium.launch({
    headless: false
  });
  const context = await browser.newContext();

  // Open new page
  const page = await context.newPage();

  // Go to https://www.baidu.com/
  await page.goto('https://www.baidu.com/');

  // Click input[name="wd"]
  await page.locator('input[name="wd"]').click();

  // Fill input[name="wd"]
  await page.locator('input[name="wd"]').fill('测试e');

  // Press Enter
  await page.locator('input[name="wd"]').press('Enter');
  await page.waitForURL('https://www.baidu.com/s?ie=utf-8&f=8&rsv_bp=1&rsv_idx=1&tn=baidu&wd=%E6%B5%8B%E8%AF%95e&fenlei=256&rsv_pq=84e026d700006dfd&rsv_t=1a29OIblO8bE9AtsV2LQygiz3F2VbgmOfn4Frnxu%2BLDIRsnzAJ4Bmt%2BF1lwA&rqlang=en&rsv_enter=1&rsv_dl=tb&rsv_sug3=6&rsv_sug1=4&rsv_sug7=100&rsv_sug2=0&rsv_btype=i&prefixsug=%25E6%25B5%258B%25E8%25AF%2595e&rsp=5&inputT=3167&rsv_sug4=5382');

  // Click text=百度一下
  await page.locator('text=百度一下').click();
  await page.waitForURL('https://www.baidu.com/s?ie=utf-8&f=8&rsv_bp=1&rsv_idx=1&ch=&tn=baidu&bar=&wd=%E6%B5%8B%E8%AF%95e&fenlei=256&oq=%E6%B5%8B%E8%AF%95e&rsv_pq=8931c4b900027d13&rsv_t=f346r8rvlWcVO4rv7PR1Kx1kJDSFbGZ96r4qj9sPt4i%2F2acqT9nlPIG98OI&rqlang=cn');

  // Click text=百度一下
  await page.locator('text=百度一下').click();

  // Click input[name="wd"]
  await page.locator('input[name="wd"]').click();

  // Fill input[name="wd"]
  await page.locator('input[name="wd"]').fill('测试e2e');

  // Press Enter
  await page.locator('input[name="wd"]').press('Enter');
  await page.waitForURL('https://www.baidu.com/s?ie=utf-8&f=8&rsv_bp=1&rsv_idx=1&tn=baidu&wd=%E6%B5%8B%E8%AF%95e2e&fenlei=256&oq=%25E6%25B5%258B%25E8%25AF%2595e&rsv_pq=96ce991300020017&rsv_t=6db1rU0eGQZ0CtmFHatuznGGgJFdXBsa%2F%2Buuog0HI0QeSEzUDHonLRbilwM&rqlang=cn&rsv_enter=1&rsv_dl=tb&rsv_sug3=3&rsv_sug1=3&rsv_sug7=100&rsv_sug2=0&rsv_btype=t&inputT=2901&rsv_sug4=4038&bs=%E6%B5%8B%E8%AF%95e&rsv_jmp=fail');

  // Click text=怎么为大中型的vue.js项目编写e2e测试? - 知乎
  const [page1] = await Promise.all([
    page.waitForEvent('popup'),
    page.locator('text=怎么为大中型的vue.js项目编写e2e测试? - 知乎').click()
  ]);

  // Close page
  await page1.close();

  // Close page
  await page.close();

  // ---------------------
  await context.close();
  await browser.close();
})();

五、写or不写

  • 业务型项目,大部分同学应该是没时间写,但是可以带着方便测试的思维去写代码,如果空闲,可以把测试相关代码补上
  • 组件库 / 基础库 / 底层框架类项目 不得不写,不然随便一个breake change,可能影响到的东西,不可预估

六、往期回顾

七、参考

八、求赞、求三连

image.png

我正在参与掘金技术社区创作者签约计划招募活动,点击链接报名投稿