
- 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 — примеры кода из данной статьи