Javascript测试框架Jasmine(三):Setup、Teardown、嵌套describe等

2014-06-27 · 15 min read

上一篇把 Matcher 说了说,本来该说自定义 Matcher 的,但是发现有些七七八八的细节没讲,那就今天先把这些基础说了。

Setup and Teardown#

Setup 和 Teardown 可以帮助 Suite 执行一些重复的代码,Jasmine 提供了beforeEachafterEach 函数。很显然,beforeEach 在 describe 中的任何 spec 执行之前运行,afterEach 在任何 spec 执行之后运行。具体的逻辑看 Demo 就一目了然了:

describe('Setup和Teardown示例', function () {
  var foo

  beforeEach(function () {
    foo = 0
    foo += 1
  })

  afterEach(function () {
    foo = 0
  })

  it('测试1', function () {
    expect(foo).toEqual(1)
  })

  it('测试2', function () {
    expect(foo).toEqual(1)
    expect(true).toEqual(true)
  })
})

在线演示

前面说过 javascript 的作用域规则在 Jasmine 中是适用的,但是这里可以通过thisbeforeEachitafterEach 中间共享变量。每个 spec 的this 默认指向空的 object。

describe('this用法示例', function () {
  beforeEach(function () {
    this.foo = 0
  })

  it('使用this共享状态', function () {
    expect(this.foo).toEqual(0)
    this.bar = 'test pollution?'
  })

  it('下个Spec执行前this会被重置为空Object', function () {
    expect(this.foo).toEqual(0)
    expect(this.bar).toBe(undefined)
  })
})

在线演示

嵌套 describe#

可以嵌套使用 describe,形成一个 Suite 树,在一个 spec 执行之前,Jasmine 会顺序执行这颗树上的所有beforeEach 。同样的,执行完 spec 后,也会顺序执行树上的所有afterEach

describe('测试嵌套describe:level1', function () {
  var foo

  beforeEach(function () {
    alert('level1:Setup')
  })

  afterEach(function () {
    alert('level1:Teardown')
  })

  it('level1:测试', function () {
    alert('level1:测试')
  })

  describe('测试嵌套describe:level2', function () {
    beforeEach(function () {
      alert('level2:Setup')
    })

    afterEach(function () {
      alert('level2:Teardown')
    })

    it('level2:测试', function () {
      alert('level2:测试')
    })
  })
})

具体的执行顺序,点击这里查看上例的效果,你就全明白了。

禁用 Suites 和挂起 Specs#

可以使用xdescribexit 函数禁用 Suites 和 specs。当 Suites 被禁用后,将不显示在报表中。

xdescribe('A spec', function () {
  var foo

  beforeEach(function () {
    foo = 0
    foo += 1
  })

  it('is just a function, so it can contain any code', function () {
    expect(foo).toEqual(1)
  })
})

另外可以使用pending 函数将 specs 挂起,挂起 specs 和使用xit 函数定义的 spec 一样,spec 不会被执行,但是名字会出现在报表中。另外只有名字,没有实际代码的 spec 也会在结果中显示为挂起的状态。pending 可以在 spec 函数体的任何地方调用,不管函数体内有没有 expectations,还是看个例子:

describe('Pending specs', function () {
  xit("can be declared 'xit'", function () {
    expect(true).toBe(false)
  })

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

  it("can be declared by calling 'pending' in the spec body", function () {
    expect(true).toBe(false)
    pending()
  })
})

jasmine.any#

jasmine.any 接受构造函数或者“Class”名作为期望值,如果这个构造函数(Class)和实际值的构造函数(Class)匹配,则返回true 。继续看例子:

describe('jasmine.any', function () {
  it('matches any value', function () {
    expect({}).toEqual(jasmine.any(Object))
    expect(12).toEqual(jasmine.any(Number))
  })

  describe('when used with a spy', function () {
    it('is useful for comparing arguments', function () {
      var foo = jasmine.createSpy('foo')
      foo(12, function () {
        return true
      })

      expect(foo).toHaveBeenCalledWith(jasmine.any(Number), jasmine.any(Function))
    })
  })
})

jasmine.objectContaining#

当一个你只关心实际值是否包含某个键值对的时候,可以使用jasmine.objectContaining

describe('jasmine.objectContaining', function () {
  var foo

  beforeEach(function () {
    foo = {
      a: 1,
      b: 2,
      bar: 'baz',
    }
  })

  it('matches objects with the expect key/value pairs', function () {
    expect(foo).toEqual(
      jasmine.objectContaining({
        bar: 'baz',
      }),
    )
    expect(foo).not.toEqual(
      jasmine.objectContaining({
        c: 37,
      }),
    )
  })

  describe('when used with a spy', function () {
    it('is useful for comparing arguments', function () {
      var callback = jasmine.createSpy('callback')

      callback({
        bar: 'baz',
      })

      expect(callback).toHaveBeenCalledWith(
        jasmine.objectContaining({
          bar: 'baz',
        }),
      )
      expect(callback).not.toHaveBeenCalledWith(
        jasmine.objectContaining({
          c: 37,
        }),
      )
    })
  })
})

下一篇讲一下自定义 Matcher –