前端测试工具 TestCafe 测试代码结构

发表时间

TestCafe 本身作为一款测试框架,有其自身特定的代码结构,本文介绍 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 , 都可以正向的选择或者反向的排除,使用 onlyskip

fixture.only('添加到运行集');
fixture.skip('排除');

test.only('添加到运行集', t => {});
test.skip('排除', t => {});

延伸阅读

本文介绍了 TestCafe 的代码基本结构,本站还有其他文章深入介绍 TestCafe 的具体用法,见下方的『相关文章』


相关文章
除非特别说明,本站文章均系原创,并采用 署名协议 CC-BY 授权。
欢迎转载,惟请保留原文链接:https://lfhacks.com/tech/testcafe-code-structure