Giter VIP home page Giter VIP logo

sleipnir's Introduction

Sleipnir

Sleipnir is a BDD-style framework for Swift. Sleipnir is highly inspired by Cedar. Also

In Norse mythology, Sleipnir is Odin's steed, is the child of Loki and Svaðilfari, is described as the best of all horses, and is sometimes ridden to the location of Hel.

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

Core principles

  • Sleipnir is not dependent of NSObject, it is pure Swift BDD testing framework
  • Sleipnir is not using XCTest
  • Sleipnir has nice command line output and support for custom test reporters
  • Other features, like seeded random tests invocation, focused and excluded examples/groups, etc.

Installation

Manually (preferred way)

  1. Add Sleipnir as a submodule: git submodule add https://github.com/railsware/sleipnir ThirdParty/Sleipnir
  2. Drag'n'drop Sleipnir.xcodeproj to your test target
  3. Link Sleipnir.framework
  4. Start writing specs!

Via CocoaPods

You can install statically built Sleipnir.framework into you project simply by adding it to the Podfile

pod 'Sleipnir'

Note: it is experimental way

Current build does not work on iPhone Simulator, but works for OSX and iOS Devices

It will work well with pure Swift project, but it won't work in case you mix Swift and ObjC.
Swift compiler recognizes custom built framework without any issues, but when you're including auto-generated header "ProjectName-Swift.h" it tries to include the framework into ObjC universe, which currently does not work.

Usage sample

See LibrarySpec file in Sample project.

Writing specs

All spec classes should inherit from SleipnirSpec.

Root ExampleGroups in a spec should be assigned to some void variable. This allows specs to initialize correctly:

import Sleipnir

class SomeSpec : SleipnirSpec {
 
    let someSpec : () = describe("Some spec") {
        it("should pass") {
            expect(1).to(equal(1))
        }
    }
    
}

Running specs

In order to run your specs you should invoke Runner.run() from the main.swift of your test target.

The default test runner will produce the following command line output, indicating all the failures and some meta information about each failure:

Running With Random Seed: 1234

.....F..


FAILURE Some spec should pass:
/Path/To/Your/Specs/TestSpec.swift:16 Expected <1> to equal <2>


Finished in 0.0075 seconds

8 examples, 1 failures

Seeded random specs run

All examples would run in random order with the random seed specified in output. If you would like to re-run tests in the same order you should pass a seed to run() method:

Runner.run(seed: 1234)

Examples and ExampleGroups

ExampleGroups are created with describe or context keywords. Within the block passed to ExampleGroup you can declare examples using the it keyword.

ExampleGroups can be nested in order to create clean structure of your tests.

Under the hood describe method creates an instance of ExampleGroup and evaluates the block passed to it. Blocks passed to examples are evaluated dynamically during the test execution.

Setup/Teardown code

Sleipnir supports some hooks to execute before or after examples. This allows to share some setup/teardown code between examples.

beforeEach block will be executed before each example in the current group and all nested groups.

afterEach block will be executed after each example.

class SomeSpec : SleipnirSpec {
 
    let someSpec : () = describe("Some spec") {
        var someArray: [Int]?
        beforeEach {
            someArray = [1, 2, 3]
        }
        
        afterEach {
            someArray = nil
        }
        
        it("should pass") {
            expect(someArray).toNot(beNil())
            expect(someArray).to(contain(3))
        }
    }
    
}

You can also specify global setup/teardown blocks using beforeAll and afterAll keywords. They will run once before or after all examples in the current group and all the nested groups.

Focused and excluded specs

You can specify examples and example groups to be focused by placing f letter before declaration: fdescribe, fcontext, fit. In this case the spec runner will only run focused examples/example groups and ignore all the others.

You can also mark an example or example group as pending. It won't run but will be printed along with the test results.
To mark something as pending add an x letter before declaration: xdescribe, xcontext, xit.
Example can also be marked as pending by passing PENDING instead of spec block:

it("is pending", PENDING)

Shared example groups

Sleipnir supports extracting common specs through shared example groups.
They can include any number of it, context, describe, before and after blocks.
You can pass example-specific state into the shared example group through sharedContext dictionary.

var nonNilObject : () =
    sharedExamplesFor("a non-nil object") { (sharedContext : SharedContext) in
        var object: AnyObject?
        beforeEach {
            object = sharedContext()["object"]
        }
        
        it("should not be nil") {
            expect(object).toNot(beNil())
        }
    }

var spec : () = context("A non-nil object") {
    let someObject: String = "Some Object"
    itShouldBehaveLike("a non-nil object", { ["object" : someObject] })
}

If you don't need any context, you can use closures without parameters:

sharedExamplesFor("some awesome stuff") {
    it("should be awesome") {
        //...
    }
}

describe("Some stuff") {
    itShouldBehaveLike("some awesome stuff")
}

Expectations

Sleipnir supports defining expectations using expect(someValue/expession).to(SomeMatcher) syntax.

expect(true).to(beTrue())

expect method supports passing values or a block of code in a closure:

expect {
    var x = 1
    x++
    return x
}.to(equal(2))

We are also working on should syntax support. This syntax provides should and shouldNot methods to define expectations on any object:

actual.should(equal(expected))
[1, 2, 3].shouldNot(contain(4))

Available matchers

Equal

Values must be Equatable, Comparable or derive from NSObject.

expect([1,2,3]).to(equal(1,2,3))
expect("some string").toNot(equal("another string"))
expect(1) == 1

BeNil

expect(nil).to(beNil())
expect("some string").toNot(beNil())

BeTrue/BeFalse

expect(true).to(beTrue())
expect(false).to(beFalse())

BeGreaterThan/BeLessThan

Values must be Comparable.

expect(3).to(beGreaterThan(1))
expect(3) > 1

expect(1).to(beLessThan(3))
expect(1) < 3

BeGreaterThanOrEqualTo/BeLessThanOrEqualTo

Values must be Comparable.

expect(3).to(beGreaterThanOrEqualTo(1))
expect(3) >= 1
expect(1) >= 1

expect(1).to(beLessThanOrEqualTo(3))
expect(1) <= 3
expect(1) <= 1

Collections/String matchers

Sleipnir supports some matchers on collections and strings:

Contain

Matches if all items are in the container. Supports Swift collections with Equatable elements, NSArrays, NSSets and Strings.

expect([1,2,3]).to(contain(1,3))
expect("some string").toNot(contain("another"))

BeginWith/EndWith

Matches if the container begins/ends with the specified element. Supports Swift collections with Equatable elements, NSArrays and Strings.

expect([1,2,3]).to(beginWith(1))
expect("some string").to(endWith("string"))

BeEmpty

Matches if a container is empty.

expect("").to(beEmpty())
expect([1,2,3]).toNot(beEmpty())

TODO

  • Ease of distribution (CocoaPods probably)
  • XCode templates
  • Shared examples support
  • should syntax support (In progress, see the corresponding Pull request)
  • asynchronous matchers (will, willNot, after)

Who uses Sleipnir

  • SGL - Swift Generic Library

Contributing

Please read the Contributor Guide.

License

Licensed under MIT license. See the LICENSE file for details.

sleipnir's People

Contributors

alexdenisov avatar atermenji avatar

Watchers

 avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.