![](http://habrastorage.org/getpro/habr/post_images/078/113/01e/07811301e0ca3addad82e27595ec0b57.png)
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, и т.д.)
Оставляйте багрепорты и фидбек на гитхабе или в комментах и следите за обновлениями!
ссылка на оригинал статьи http://habrahabr.ru/post/228811/
Добавить комментарий