本文从前端测试工具 TestCafe 的测试代码结构入手,介绍用法。 Mocha 的测试代码结构 首先来看较常见的 mocha 的基本代码结构,由外层的 describe 和内层的 it 组成,例如下面的代码: describe('用例集名称', function() { it('单条用例1', function() { // ... }); it('单条用例2', function() { // ... }); }); 其中 describe 相当于用例集, it 相当于单条用例,多条 it 包含在 describe 函数的第二个参数函数中依次调用。TestCafe 测试代码结构与此类似,但也有区别。 TestCafe 的测试代码结构 TestCafe 中,对应 describe 的是 fixture ,对应 it 的是 test ,而且在代码层面,取消了嵌套关系。 如下: fixture('用例集名称'); test('单条用例1', async t => { /* Test 1 Code */ }); test('单条用例2', async t => { /* Test 2 Code */ }); 在 mocha 的describe/it 结构 中,同一个 describe 下的 每个 it 是顺序执行的。 所以上一个 it 如果修改了外部测试环境,会影响到后面 it 的结果,将导致无法预测的结果。所以这种测试结构适合各个 it 不相关的情况。 与此不同的是,在 TestCafe 中,每个 fixture 下的 test 执行结束后,会重新回到 fixture 规定的状态,如图: 这就体现了 fixture 的含义,见 下节 详细介绍。 fixture 的含义 首先我们需要清楚,一般意义上的测试活动都包含三个阶段: 将被测物(DUT,Device Under Test 的缩写)置于某种状态 采取某种行动 检查被测物的新状态是否符合预期 这三个阶段中,实现第一阶段的是 fixture,中文含义是"夹具、固定装置": 在各类测试中(不仅仅是软件测试),都有 fixture 的 身影。fixture 的作用是: 预先设置被测物的运行环境 将被测物的指标、状态调节到规定的状态。 设置fixture的好处,第一是可重复,方便连续重复测试多个物件,第二是方便参数化,可以在多种参数环境下反复测试。下面列一些形象的例子便于理解: 例子1:考试中的考场,将教室中无关物品清除、设置好规定的桌椅器材,便于被测物(考生)通过测试。在下一场考试开始前,考场将会被恢复到同样的状态。标准化的考场才能让各地考生的成绩有可比性。 例子2:运动会的赛场,裁判、场地、器材共同决定了比赛的环境,标准化的赛场才能让各届运动会的运动成绩有可比性。 例子3:汽车生产线中,整车下线前的最后一步,汽车将通过道路模拟机,测试转动、振动等指标,而且可以调整多种参数,以模拟各种道路情况。标准化的道路模拟才能保证汽车产品的一致性。 从另一个角度理解 fixture 虽然代码里的 fixture 写在最前面,似乎是先有 fixture 再有 test 。其实应该这么理解: test 是测试的核心,一些有共同初始条件的test,将这些条件提取出来,成为 fixture ,起到辅助作用。 比如下图中,一些有共同初始条件的 test,用红绿颜色表示 将初始条件提取出来,放在同一个 fixture 中。 经过这样一次抽象,减少了冗余代码,方便维护和扩展代码。 再拿考场做例子,英语听力考试中,每位考生都需要听声音,于是在考场中布置音响设备,让每位考生都听到声音。而体育测验中需要跑道,就把全部考生放在运动场上测验。 TestCafe 中的 fixture 在 TestCafe 中,调用一次 fixture() 即声明了一个fixture: fixture(fixtureName) fixture 支持的方法有: .page() // 设置页面 .meta() //设置元信息 .before(async ctx => {}) // 测试钩子 .after(async ctx => {}) .beforeEach(async t => {}) .afterEach(async t => {}) TestCafe 中的 Server 和 Client 我们在 上文中提到过 ,TestCafe的特点在于新增了一个URL代理,在测试脚本和页面中起到中介作用,所以产生了服务侧(Server side) 和 客户端侧(Client side)的概念,如下图: URL代理就是服务侧,编写的测试代码都是运行在服务侧。提供服务端的好处是,可以将测试代码与测试浏览器分离,运行在不同的主机上,比如 这篇文章 里提到的远程浏览器,访问服务端,一样能执行自动测试。 TestCafe 中的 Test Controller 常规的测试工具中,比如 Puppeteer 和 selenium,页面操作都是零散的单条语句执行,如下图 观察 Puppeteer 和 selenium 提供的接口即可以看出,测试动作都是由比较底层的对象发出(比如 Puppeteer 中的 page 对象,和 selenium 中的 driver 对象): //Puppeteer browser = await puppeteer.launch() page = await browser.newPage() await page.goto("https://www.google.com") element = await page.waitForSelector('#q') //selenium WebDriver driver = new FirefoxDriver(); driver.get("https://www.google.com"); WebElement element = driver.findElement(By.name("q")); 与之相比,Test Cafe 提供了 Test Controller 的角色,可以看做是一个代理人,集中执行所有页面动作(比如寻找元素、输入文字、等待页面变化、校验数据)。在 服务侧 代码中,页面动作都封装为 Test Controller 的执行方法,而不再是零散的页面动作。 这样写出的用例就像操作者真实的操作页面,更加易读。下一节 将介绍具体的 Test Controller 的写法。 TestCafe 中的 Test TestCafe 的 单个用例使用 test 函数调用,负责执行服务侧代码、发出页面动作。而且如 上节 中的介绍,所有的动作都由 Test Controller 执行,所以 test 函数的参数是一个以 Test Controller 为参数的函数,变量名约定为 t 。 test('Test Case', t => {}); 对比 Mocha的测试代码就能看出区别,这里的执行函数是没有参数的: () //Mocha it('Test Case', () => {}); 比如,下面的例子中,测试动作统一由 Test Controller t 发出: test('My Test', async t => { await t .click('#populate') .click('#submit-button'); }); TestCafe 中筛选运行集 与 mocha 的describe/it 结构 类似,在TestCafe 中,无论是 fixture 还是 it , 都可以正向的选择或者反向的排除,使用 only 和 skip : fixture.only('添加到运行集'); fixture.skip('排除'); test.only('添加到运行集', t => {}); test.skip('排除', t => {}); 延伸阅读 本文介绍了 TestCafe 的代码基本结构,本站还有 其他文章 深入介绍 TestCafe 的具体用法