Keep going

Javascript测试框架jasmine

jasmine是一个javascript的测试框架,不同于qunit等其他测试框架,它可以用于很多js环境,比如web,nodejs等。它上手简单,并且官方有很详细的实例和描述,这里将大部分实用的api和用法记录下来。

一个DEMO

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
describe("this is a test suite", function() {
  var foo;

  // setup function run before every spec
  beforeEach(function() {
    foo = 0;
    foo += 1;
  });
  // teardown function run before ever spec
  afterEach(function() {
    foo = 0;
  });

  it("this is a test spec 1", function() {
    expect(foo).toEqual(1);
  });

  it("this is a test spec 2", function() {
    expect(foo).toEqual(1);
    expect(true).toEqual(true);
  });
});

jasmine提供的全局方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
describe(testSuiteName, func) //定义一个test suite,其中可以包含it定义的spec,并且describe可以嵌套
xdescribe(testSuiteName, func) //类似于JUnit的@Ignore,忽略这个test suite
it(specName, func) //定义一个test unit(spec)
xit(specName, func) //类似于@Ignore,忽略这个unit
expect(actualValue) //assert
beforeEach(func) //定义一个start function,在每个unit之前运行
afterEach(func) //定义一个deardown function, 在每个unit之后运行
spyOn(obj, funcName) //监视obj.funcName函数的调用情况

runs(fun)
waitsFor(fun, msg, timeout)

jasmine.createSpy(spyName) //定义一个spy函数,该函数可以被任意调用,主要用于mock
jasmine.createSpyObj(objName, spyFuncNames) //定义一个spy对象,并且该对象具有spyFuncNames数组包含的方法。主要用于mock
jasmine.any(construct|className) //配合expect使用,检测该对象是否是指定类型,如:expect(12).toEqual(jasmine.any(Number));
jasmine.Clock.useMock //启用模拟timer
jasmine.Clock.tick //手动触发一个timer事件

expectation api

1
2
3
4
5
6
7
8
9
10
11
12
13
expect(a).toBe(b) //a === b
expect(a).not.toBe(b) //!(a===b)
expect(a).toEqual(b) //a是否和b相等,可以比较普通类型或者对象
expect(a).toMatch(reg) //a是否符合reg的正则表达式
expect(o.a).toBeDefined() //o.a是否被定义
expect(o.a).toBeUnDefined() //o.a是否没有被定义
expect(a).toBeNull() //a === null ?
expect(a).toBeTruthy() //if (a) 是否成立
expect(a).toBeFalsy() //if (!a) 是否成立
expect(array).toContain(b) //数组array里是否有b
expect(a).toBeLessThan(b) //a < b ?
expect(a).toBeGreaterThan(b) //a > b?
expect(func).toThrow() //func被调用的时候是否抛出异常

spy

jasmine可以对指定的函数进行调用监视,相关的函数有spyOn, jasmine.createSpy(), jasmine.createSpyObj(),比如:

1
2
3
4
5
6
var obj = {
    hello: function() {
        console.log('hello world');
    };

spyOn(obj, 'hello');

那么对obj.hello的调用都会被jasmine截获,但是实际上是不会真正调用obj.hello的,除非:

1
2
3
4
5
6
7
sypOn(obj, 'hello').andCallThough() // 调用obj.hello()时,最终会真正调用console.log

spyOn(obj, 'hello').andReturn(123); obj.hello()永远返回123

spyOn(obj, 'hello').andCallFake(function() {
             console.log('welcome');
          });  // 该方法会让obj.hello()委派到console.log('welcome')上,也就是执行提供的函数内容。

从此可以看出Jasmine确实考虑的非常周到

expectation for spy

以下expect要配合spyOn, jasmine.createSpy(), jasmine.createSpyObj()来使用

1
2
3
4
5
6
7
spyOn(foo, 'setBar'); // 监视foo.setBar方法
...
expect(foo.setBar).toHaveBeenCalled(): 该方法是否被调用过
expect(foo.setBar.calls.length).toEqual(2): 该方法是否被调用了两次
expect(foo.setBar).toHaveBeenCalledWith(456, 'another param'): 该方法是否被调用过,并且调用的参数是:456'another param'
expect(foo.setBar.mostRecentCall.args[0]).toEqual(456): 最近一次调用的第一个参数是否是456
expect(foo.setBar.calls[0].args[0]).toEqual(123): 第一次调用的第一个参数是否是123

HTML Reporter

jasmine还可以控制执行以及测试结果的输出,比如下面的代码就是将测试报告输出到html页面上:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
(function() {
  var jasmineEnv = jasmine.getEnv();
  jasmineEnv.updateInterval = 250;

  var htmlReporter = new jasmine.HtmlReporter();
  jasmineEnv.addReporter(htmlReporter);

  jasmineEnv.specFilter = function(spec) {
    return htmlReporter.specFilter(spec);
  };

  var currentWindowOnload = window.onload;
  window.onload = function() {
    if (currentWindowOnload) {
      currentWindowOnload();
    }

    document.querySelector('.version').innerHTML = jasmineEnv.versionString();
    execJasmine();
  };

  function execJasmine() {
    jasmineEnv.execute();
  }
})();