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

07811301e0ca3addad82e27595ec0b57.pngObjective-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, и т.д.) Оставляйте багрепорты и фидбек на гитхабе или в комментах и следите за обновлениями!

© Habrahabr.ru