Pull to refresh

Jasmine — дополнительные возможности

Reading time 4 min
Views 43K
Данная статья является продолжением первой части «Введение в Jasmine». Здесь рассматриваются дополнительные возможности тестового фреймворка Jasmine, а именно:

  • Spy — эмуляция функций/объектов
  • Clock — синхронизация вызовов при использовании setTimeout/setInterval
  • 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 поддерживает несколько типов отчетов о прохождении тестов, основными из них являются:

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


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

Ссылки:
github.com/pivotal/jasmine — страница проекта на GitHub
www.inexfinance.com/en/blog/2013/2/17/jasmine_additional_features — английский вариант этой статьи
github.com/inex-finance/blog-examples/tree/master/jasmine — примеры кода из данной статьи
Tags:
Hubs:
+5
Comments 1
Comments Comments 1

Articles