📜 ⬆️ ⬇️

BDD testing in Swift using Sleipnir


Objective-C developers can use various BDD frameworks to test their code.
Some of them:


With the advent of the Swift programming language, we decided to implement a framework for testing in the BDD style in pure Swift, without being tied to Objective-C.
After a couple of weeks of implementation, we released the first public version of the Sleipnir framework.

Sleipnir was inspired by the Cedar framework and allows you to write BDD tests in this style:

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 Fundamentals



We also found some alternative frameworks for BDD testing on Swift, for example Quick .
The choice between them is a matter of developer’s personal preferences.
')

Usage example


We define two classes - Book and Library and write tests for them.
The Book class contains information about the author and the title of the book:
 class Book { var title: String var author: String init(title: String, author: String) { self.title = title self.author = author } } 

The Library class is a simple collection of books:
 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 } } } 

First, let's test the correctness of the initialization of the Book class:
 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.")) } } } 

We have created the LibrarySpec class, which inherits from the SleipnirSpec class. It contains the main context and defines two exampla that check the properties of the created object of the Book class.

The object of class Book is created in the beforeAll{ } block.
Sleipnir supports several blocks of initialization and deinitialization of tests: beforeAll , afterAll , beforeEach and afterEach .

The result of calling all top-level exampl ( describe or context ) in the test must be assigned to a variable for proper instantiation:
 var book : () = context("Book") { } 

Now let's test the behavior of the Library class:
 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 these tests will display the following information on the command line:
 Running With Random Seed: 657464010 ....... Finished in 0.0091 seconds 7 examples, 0 failures 

In the case of a fallen test, detailed error information is displayed, including the file and line number:
 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 

We tested the behavior of the Library class using simple expectaions and matchers.
At the moment, Sleipnir only supports three types of matcher: equal , beTrue and beFalse , but new ones will be added soon.

Future plans


Since this is the first public release, many features have not yet been implemented. We have an implementation plan for the near future, which includes:

Leave bugreports and feedback on githaba or in the comments and stay tuned !

Source: https://habr.com/ru/post/228811/


All Articles