Jasmine отчет о прохождении тестов в HTML
Данная статья является продолжением первой части Введение в Jasmine. Здесь рассматриваются дополнительные возможности тестового фреймворка Jasmine, а именно:

  1. Spy - эмуляция функций/объектов
  2. Clock - синхронизация вызовов при использовании setTimeout/setInterval
  3. Runner и Reporter - запуск тестов и оформление отчета

Для удобства, будет рассматриваться тестирование в браузере, а для лаконичности примеры приводятся с использованием CoffeeScript (примеры на JavaScript).

В Jasmine отслеживания вызова функции и параметров вызова осуществляется с помощью spyOn. Функции spyOn передается два параметра - объект, для которого осуществляется вызов функции, и имя функции, которую необходимо отслеживать:

spyOn(window, 'isNaN')

При обычном использовании spyOn вызов оригинальной функции не производится.

Примеры приведены с использованием небольшого класса Person:

class Person
  name: null
  age:  0
  constructor: (@name, @age) ->
  getName: -> @name
  setName: (value) -> @name = value
  getAge: -> @age
  addYear: -> @age += 1

При тестировании с использованием spyOn можно отслеживать количество вызовов, их параметры и каждый вызов в отдельности:

describe "Spy", ->
 person = null

 beforeEach ->
   person = new Person("Jim", 25)

 it "осуществлен вызов функции", ->
   spyOn(person, 'getName')
   person.getName()
   expect(person.getName).toHaveBeenCalled()

 it "проверка количества вызовов", ->
   spyOn(person, 'addYear')
   person.addYear()
   person.addYear()
   expect(person.addYear.calls.length).toEqual(2)

 it "проверка аргументов", ->
   spyOn(person, 'setName')
   person.setName("Ira")
   expect(person.setName).toHaveBeenCalledWith("Ira") # может быть несколько аргументов

 it "есть доступ к последнему вызову", ->
   spyOn(person, 'setName')
   person.setName("Ira")
   expect(person.setName.mostRecentCall.args[0]).toEqual("Ira")

 it "есть доступ ко всем вызовам", ->
   spyOn(person, 'setName')
   person.setName("Ira")
   expect(person.setName.calls[0].args[0]).toEqual("Ira")

При использовании spyOn вместе с andCallThrough, будет осуществлен вызов оригинальной функции:

it "вызывает оригинальную функцию", ->
   spyOn(person, 'getName').andCallThrough()
   expect(person.getName()).toEqual("Jim")
   expect(person.getName).toHaveBeenCalled()

Если необходимо возвращать из функции определенное значение, то для этого надо вызвать spyOn вместе с andReturn:

 it "возвращает указанное значение", ->
   spyOn(person, 'getName').andReturn("Dan")
   expect(person.getName()).toEqual("Dan")
   expect(person.getName).toHaveBeenCalled()

При использовании spyOn вместе с andCallFake, вместо вызова оригинальной функции, будет вызвана указанная функция:

 it "вызывает указанную функцию", ->
   spyOn(person, 'getAge').andCallFake(-> return 5 * 11)
   expect(person.getAge()).toEqual(55)
   expect(person.getAge).toHaveBeenCalled()

Для создания функции без реализации можно воспользоваться createSpy, при этом доступны все возможности для тестирования обычного spy. Единственный параметр, который принимает createSpy - это имя функции для идентификации.

 it "создает фальшивую функцию", ->
   concat = jasmine.createSpy('CONCAT')
   concat("one", "two")
   expect(concat.identity).toEqual('CONCAT') # есть имя для идентификации
   expect(concat).toHaveBeenCalledWith("one", "two")
   expect(concat.calls.length).toEqual(1)

Создания объекта заглушки осуществляется с помощью createSpyObj. В качестве параметров createSpyObj принимает имя объекта и массив строк, являющийся списком методов объекта заглушки:

 it "создает фальшивый объект", ->
   button = jasmine.createSpyObj('BUTTON', ['click', 'setTitle', 'getTitle'])
   button.click()
   button.setTitle("Help")
   expect(button.click).toBeDefined()
   expect(button.click).toHaveBeenCalled()
   expect(button.setTitle).toHaveBeenCalledWith("Help")
   expect(button.getTitle).not.toHaveBeenCalled()

Проверка типа объекта осуществляется вызовом jasmine.any, которому передается ожидаемый тип:

 it "проверяет тип", ->
   spyOn(person, 'setName')
   person.setName("Ira")
   expect(person.setName).toHaveBeenCalledWith(jasmine.any(String))

Синхронное тестирование вызовов setTimeout/setInterval осуществляется с помощью jasmine.Clock.useMock. Для перемещения времени вперед используется вызов jasmine.Clock.tick, которому передается время в миллисекундах:

describe "Время", ->
 callback = null

 beforeEach ->
   callback = jasmine.createSpy('TIMER')
   jasmine.Clock.useMock()

 it "вызывает timeout синхронно", ->
   setTimeout((-> callback()), 100) # задержка вызова в 100ms
   expect(callback).not.toHaveBeenCalled()
   jasmine.Clock.tick(101) # передвинуть время на 101ms
   expect(callback).toHaveBeenCalled()

Для запуска тестов в Jasmine, как правило, используется небольшой скрипт:

# Выполнение тестов
jasmineEnv = jasmine.getEnv()
jasmineEnv.updateInterval = 250
currentWindowOnload = window.onload

window.onload = ->
 currentWindowOnload() if currentWindowOnload
 execJasmine()

execJasmine = -> jasmineEnv.execute()

# Вид отчета
htmlReporter = new jasmine.HtmlReporter()
jasmineEnv.addReporter(htmlReporter)
jasmineEnv.specFilter = (spec) -> htmlReporter.specFilter(spec)

Jasmine поддерживает несколько типов отчетов о прохождении тестов, основными из них являются:

  1. HtmlReporter - древовидная структура с прогрессом выполнения
  2. TrivialReporter - простая древовидная структура (помечен как устаревший)
  3. ConsoleReporter - вывод результатов тестирования в консоль (для node.js)

Если у Вас есть вопросы или замечания, будем рады на них ответить.

Ссылки:

github.com/pivotal/jasmine – страница проекта на GitHub
github.com/inex-finance/blog-examples/tree/master/jasmine – примеры кода из данной статьи


Тэги dev, javascript, jasmine
Опубликовано в 17 Февраль 2013, 18:58
Изменен в 25 Февраль 2013, 19:01

comments powered by Disqus