Web前端测试与集成:Jasmine/Selenium/Protractor/Jenkins的最佳实践
上QQ阅读APP看书,第一时间看更新

4.2 组织测试用例

Jasmine使用describe、it等全局函数组织测试用例。

4.2.1 describe

describe是Jasmine的全局函数,用于创建一个测试套件(test suite)。测试套件可以理解为一组测试用例(test case或spec)的集合。

describe函数接受两个参数(一个字符串和一个回调函数)。字符串参数是这个测试套件的名字或标题(通常描述被测试内容),回调函数是实现测试套件的代码块(称为describe块)。示例代码如下:

   describe('Player', function() {
           /* tests */
   });

describe函数可以嵌套使用(测试套件可以包含测试套件),例如:

   describe('Player', function() {
       describe('when new', function() {
           /* tests */
       });
   });

4.2.2 it

it也是Jasmine的全局函数,用来在describe块里创建一个测试用例(spec)。和describe一样,it接受两个参数(一个字符串和一个回调函数),字符串参数是测试用例的名字或标题,回调函数是实现测试用例的代码块(称为it块)。示例代码如下:

   describe('Player', function() {
       it('should be able to play a Song', function() {
           /* code and assertions */
       });
       it('should be able to pause a Song', function() {
           /* code and assertions */
       });
   });

describe和it函数的字符串参数很重要。describe创建的测试套件用来组织多个相关的测试用例。通过拼接测试套件的名字和测试用例的名字,可以完整描述一个测试场景,方便在大型项目中进行查找。如果描述得当的话,你的测试可以以自然语言的方式表达出来,形成文档。

describe块和it块都是JavaScript函数,所以JavaScript的作用域(scope)规则也适用于此处。在describe块里声明的变量可以被它内部任何it块使用。

   describe('Player', function() {
       var a;
       it('and so is a spec', function() {
           a = true;
       });
   });

4.2.3 安装和拆卸

在真正执行测试代码之前,通常需要做大量的铺垫,例如准备一些测试数据,建立测试场景,这些为了成功测试而做的准备工作称为Test Fixture。测试完毕后需要释放运行测试占用的资源。这些铺垫工作占据的代码可能随着测试复杂度的增加而增加。为了避免在每个测试用例里重复这些代码,测试框架一般都会提供安装(Setup)和拆卸(Teardown)函数。

Jasmine提供了4个全局函数用于安装和拆卸,如表4-3所示。

表4-3 安装和拆卸函数

这些函数接受一个回调函数作为参数,执行相关的安装代码和拆卸代码。例如:

   describe('Player', function() {
       var player;
       beforeEach(function() {
           player = new Player();
       });
       afterEach(function() {
           /* cleanup code */
       });
       it('should be able to play a Song', function() {
         /* code and assertions */
       });
   });

因为describe可以嵌套,所以测试用例可以定义在任何一层describe块里,如图4-7所示。

图4-7 嵌套的describe

理解安装和拆卸函数在嵌套describe情况下的执行顺序,有助于合理组织测试用例。

例如使用下面这个例子:

   describe('Jasmine Execution Sequence', function () {
       beforeAll(function () {
         console.log('outer beforeAll');
       });
       beforeEach(function () {
         console.log('outer beforeEach');
       });
       it('spec 1', function () {
         console.log('spec 1');
       });
       console.log('statement 1');
       describe('inner', function () {
           beforeAll(function () {
             console.log('inner beforeAll');
           });
           afterAll(function () {
             console.log('inner afterAll');
           });
           console.log('statement 3');
           beforeEach(function () {
             console.log('inner beforeEach');
           });
           it('spec 3', function () {
             console.log('spec 3');
           });
           afterEach(function () {
             console.log('inner afterEach');
           });
       });
       it('spec 2', function () {
           console.log('spec 2');
       });
       console.log('statement 2');
       afterEach(function () {
           console.log('outer afterEach');
       });
       afterAll(function () {
           console.log('outer afterAll');
       });
   });

输出结果如下:

   statement 1
   statement 3
   statement 2
   outer beforeAll
   outer beforeEach
   spec 1
   outer afterEach
   inner beforeAll
   outer beforeEach
   inner beforeEach
   spec 3
   inner afterEach
   outer afterEach
   inner afterAll
   outer beforeEach
   spec 2
   outer afterEach
   outer afterAll

以上示例有这样的输出结果是因为:

• Jasmine会先执行describe块的代码,然后再执行beforeAll、beforeEach和it函数。所以“statement 1”“statement 3”“statement 2”首先被输出。

• describe块的代码从上到下依次执行。尽管console.log('statement 2')在外层describe块里,但是它还是排在内层describe块的console.log('statement 3')后面执行。

• beforeAll会在它所在describe块的测试用例和beforeEach执行前执行,而且只执行一次。

• beforeEach会在它所在describe块和内层describe块里的测试用例执行前被执行。所以outer beforeEach会在外层的测试用例spec 1之前执行,也会在内层的测试用例spec 3之前执行。而inner beforeEach只会在spec 3之前执行。

• 在每个测试用例执行前,Jasmine会从最外层的describe块开始,顺序执行每个beforeEach,直到这个测试用例所在的describe块为止。所以在执行测试用例spec 3之前,Jasmine先执行outer beforeEach,然后执行inner beforeEach。

• 测试用例会从上到下依次执行。虽然spec 2在外层,但是它还是在内层的测试用例spec 3后面执行。

• 测试用例执行完后,Jasmine会执行测试用例所在describe块的afterEach,然后依次执行外层的afterEach,直至最外层describe块。例如在spec 3测试用例完成后,inner beforeEach会先被执行,然后是outer afterEach。

• afterAll会在它所在describe块的测试用例和afterEach执行后执行,而且只执行一次。

4.2.4 禁用测试套件和挂起测试用例

有时候用户希望某些测试用例在单元测试时不被执行,因为这些测试用例还未完成,或者执行时间比较长。这时可以使用Jasmine提供的xdescribe函数来禁用测试套件,屏蔽被禁用的测试套件内的所有测试用例,使这些被禁用的测试用例不会出现在最终测试报告里。示例代码如下:

   xdescribe('Player', function() {
           /* disabled tests */
   });

测试用例也可以被挂起(pending)。和禁用不同,挂起的测试用例不会被执行,但是测试报告里会显示处于挂起状态的测试用例的名字。

有3种挂起测试用例的方式。

(1)使用xit函数,例如:

       xit('can be declared "xit"', function () {
           /* code and assertions */
       });

(2)使用it函数创建测试用例时,只提供第1个字符串名字,不提供第2个回调函数。例如:

   it('can be declared with "it" but without a function');

(3)在it代码块的任意地方调用pending函数。例如:

   it('can be declared by calling "pending" in the body', function () {
       /* code and assertions */
       pending('this is why it is pending');
   });

测试报告会显示挂起的测试用例,如图4-8所示。

图4-8 挂起的测试用例