...

воскресенье, 6 июля 2014 г.

[Перевод] BDD тестирование в Swift с помощью Sleipnir



Objective-C разработчики могут пользоваться различными фреймворками для BDD тестирования своего кода.

Некоторые из них:

С появлением языка программирования Swift мы решили реализовать фреймворк для тестирования в стиле BDD на чистом Swift, без привязки к Objective-C.

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



Sleipnir был вдохновлен фреймворком Cedar и позволяет писать BDD тесты в таком стиле:

class SampleSpec : SleipnirSpec {
var spec : () = describe("Horse") {
context("usual") {
it("is not awesome") {
let usualHorse = UsualHorse()
expect(usualHorse.legsCount).to(equal(4))
expect(usualHorse.isAwesome()).to(beFalse())
}
}

context("Sleipnir") {
it("is awesome") {
let sleipnirHorse = Sleipnir()
expect(sleipnirHorse.legsCount).to(equal(8))
expect(sleipnirHorse.isAwesome()).to(beTrue())
}
}
}
}




Основные принципы Sleipnir





  • Sleipnir не зависит от NSObject, это BDD фреймворк на чистом Swift

  • Sleipnir не использует XCTest

  • Sleipnir выводит результаты тестов в командную строку в удобном виде и позволяет расширять или дополнять вывод результатов

  • Другие возможности, такие как рандомное исполнение тестов, фокусированные/исключаемые группы тестов




Мы также нашли некоторые альтернативные фреймворки для BDD тестирования на Swift, например Quick.

Выбор между ними — вопрос личных предпочтений разработчика.

Пример использования




Определим два класса — Book и Library и напишем для них тесты.

Класс Book содержит информацию об авторе и названии книги:

class Book {
var title: String
var author: String

init(title: String, author: String) {
self.title = title
self.author = author
}
}




Класс Library — простая коллекция книг:

class Library {
var books: Book[]

init() {
self.books = Book[]()
}

func addBook(book: Book) {
books.append(book)
}

func removeLastBook() {
books.removeLast()
}

func clear() {
books.removeAll()
}

func size() -> Int {
return books.count
}

func hasBooks() -> Bool {
return size() > 0
}

func filterBy(#author: String) -> Book[] {
return books.filter { $0.author == author }
}

func filterBy(#title: String) -> Book[] {
return books.filter { !$0.title.rangeOfString(title).isEmpty }
}
}




Для начала протестируем корректность инициализации класса Book:

class LibrarySpec : SleipnirSpec {

var book : () = context("Book") {

var swiftBook: Book?
beforeAll {
swiftBook = Book(title: "Introduction to Swift", author: "Apple Inc.")
}

it("has title") {
expect(swiftBook!.title).to(equal("Introduction to Swift"))
}

it("has author") {
expect(swiftBook!.author).to(equal("Apple Inc."))
}
}
}




Мы создали класс LibrarySpec, который наследуется от класса SleipnirSpec. Он содержит в себе основной context и определяет два exampla, которые проверяют свойства созданного объекта класса Book.

Объект класса Book создается в блоке beforeAll{ }.

Sleipnir поддерживает несколько блоков инициализации и деинициализации тестов: beforeAll, afterAll, beforeEach и afterEach.


Результат вызова всех examplов (describe или context) верхнего уровня в тесте должен быть присвоен переменной для корректного инстанциирования:



var book : () = context("Book") { }




Теперь протестируем поведение класса Library:

class LibrarySpec : SleipnirSpec {

...

var library : () = context("Library") {

var swiftLibrary: Library?
beforeAll {
swiftLibrary = Library()
}

afterAll {
swiftLibrary = nil
}

describe("empty") {
it("has no books") {
expect(swiftLibrary!.hasBooks()).to(beFalse())
}
}

describe("with books") {

beforeEach {
swiftLibrary!.addBook(Book(title: "Introduction to Swift", author: "Apple Inc."))
swiftLibrary!.addBook(Book(title: "Using Swift with Cocoa", author: "Apple Inc."))
swiftLibrary!.addBook(Book(title: "Swift tutorials", author: "John Doe"))
swiftLibrary!.addBook(Book(title: "Programming iOS with Swift", author: "Vladimir Swiftin"))
}

afterEach {
swiftLibrary!.clear()
}

it("is not empty") {
expect(swiftLibrary!.hasBooks()).to(beTrue())
}

it("has correct number of books") {
expect(swiftLibrary!.size()).to(equal(4))
swiftLibrary!.removeLastBook()
expect(swiftLibrary!.size()).to(equal(3))
}

describe("filters books") {
it("by author") {
expect(swiftLibrary!.filterBy(author: "Apple Inc.").count).to(equal(2))
}

it("by title") {
expect(swiftLibrary!.filterBy(title: "tutorials").count).to(equal(1))
}
}
}
}
}




Запуск этих тестов выведет в командную строку следующую информацию:

Running With Random Seed: 657464010

.......


Finished in 0.0091 seconds

7 examples, 0 failures




В случае упавшего теста выведется детальная информация об ошибке, включая файл и номер строки:

Running With Random Seed: 2027508247

..F....

FAILURE Library with books has correct number of books:
/Users/atermenji/Coding/objc/Sleipnir/Sample/LibrarySpec.swift:64 Expected 3 to equal [2]


Finished in 0.0043 seconds

7 examples, 1 failures




Мы протестировали поведение класса Library используя простые expectaionы и matcherы.

На данный момент Sleipnir поддерживает только три типа matcherов: equal, beTrue и beFalse, однако вскоре будут добавлены новые.

Планы на будущее




Так как это первый публичный релиз, многие возможности еще не реализованы. У нас есть план имплементации на ближайшее будущее, который включает:


  • Механизм распространения фреймворка

  • Поддержка pending examples

  • Реализация фокусируемых/исключаемых групп тестов

  • Шаблоны XCode

  • Поддержка shared examples

  • Поддержка синтаксиса should (some_value should equal(some_another_value))

  • Вики документация

  • Тестирование Sleipnirа с помощью Sleipnirа

  • Дополнительные matcherы, среди которых:


    • beNil

    • beGreaterThan, beLessThan, beInRangeOf

    • асинхронные matcherы (will, willNot, after)

    • matcherы для коллекций и строк (contains, haveCount, beginWith, endWith, и т.д.)







Оставляйте багрепорты и фидбек на гитхабе или в комментах и следите за обновлениями!

This entry passed through the Full-Text RSS service — if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.


Комментариев нет:

Отправить комментарий