/StubKit

Swift library for stubbing and mocking in unit tests

Primary LanguageSwiftMIT LicenseMIT

StubKit

Build Status codecov Carthage Compatible SPM Compatible Version License Platform

Overview:

Description

StubKit provides a set of functions that speed up a process of creation of Swift stubs/mocks. It's goal is to require minimal amount of developer's work in mock creation while leveraging Swift's type system type integrity for safety without meta programming nor any code-generation.

Stubs created using StubKit provide :

  • a full introspection of function calls
  • passed arguments
  • control to declarativly specify its return value(s)
  • verify mock's expectations (synchronous and asynchronous)

Sample

// Implementation target: Sample protocol
protocol Database {
  func addUser(name: String) -> Int
  func addAccount(givenName: String, lastName: String) throws -> Int
}

// Test target: mock creation
class DatabaseStub: Database {    
  lazy var addUserAction = stub(of: addUser)  
  func addUser(name: String) -> Int {     
    return addUserAction(name)    
  }
  
  lazy var addAccountAction = stub(of: addAccount)  
  func addAccount(givenName: String, lastName: String) throws -> Int {
    return try addAccountAction((givenName, lastName))    
  }
}


// Test target, testcase Arange
let databaseStub = DatabaseStub()

Calls and arguments verification:

let addUserSpy = spyCalls(of: &databaseStub.addUserAction)
let addAccountSpy = spyCalls(of: &databaseStub.addAccountAction)

databaseStub.addUser(name: "User1") // return 0 (Int's default)
try databaseStub.addAccount(givenName: "John", lastName: "Appleseed") // returns 0

XCTAssertEqual(addUserSpy, ["User1"])
XCTAssertEqual(addAccountSpy.count, 1)
XCTAssertEqual(addAccountSpy[0]?.0, "John")
XCTAssertEqual(addAccountSpy[0]?.1, "Appleseed")

Control return value on fly

setupStubSequence(of: &databaseStub.addUserAction).returns(11)
setupStubSequence(of: &databaseStub.addAccountAction)
    .returnsOnce(1)
    .throws(DatabseError.ioError)

databaseStub.addUser(name: "User2") // return 11
try databaseStub.addAccount(givenName: "John", lastName: "Appleseed") // returns 1
try databaseStub.addAccount(givenName: "John", lastName: "Appleseed") // throws `DatabseError.ioError`

Synchronous mock verification

let user3Squence = setupStubSequence(of: &databaseStub.addUserAction)
    .when("User3")
    .expect(.once)
let user3TwiceSquence = setupStubSequence(of: &databaseStub.addUserAction)
    .when("User3")
    .expect(.times(2))
let appleseedSquence = setupStubSequence(of: &databaseStub.addAccountAction)
    .whenSecond("Appleseed")
    .expect(.atLeastOnce)
    .returns(99)
    
databaseStub.addUser(name: "User3") // return 0
try databaseStub.addAccount(givenName: "John", lastName: "Appleseed") // returns 99
try databaseStub.addAccount(givenName: "Tim", lastName: "Appleseed") // returns 99

SKTVerify(user3Squence) // ✅
SKTVerify(user3TwiceSquence) // 🛑
SKTVerify(appleseedSquence) // ✅
XCTAssert(appleseedSquence.verify()) // ✅ - equivalent to SKTVerify(appleseedSquence)

Asynchronous mock verification

setupStubSequence(of: &databaseStub.addUserAction)
    .when("User3")
    .expect(.once)
    .attach(expectation(description: "User3 added once"))
setupStubSequence(of: &databaseStub.addAccountAction)
    .whenFirst("John")
    .expect(.once)
    .attach(expectation(description: "John added once"))
    .returns(100)
    
DispatchQueue.global(qos: .background).async {
    databaseStub.addUser(name: "User3") // return 0
    try? databaseStub.addAccount(givenName: "John", lastName: "Appleseed") // returns 100
    try? databaseStub.addAccount(givenName: "Tim", lastName: "Appleseed") // returns 99
}

waitForExpectations(timeout: 0.1) //✅

Full API Documentation

For API documentation, see external document: API Documentation

Requirements

Swift StubKit
4.2, 5.0 ~> 0.1

StubKit does not introduce any changes between Swift 4.2 and Swift 5.0

Installation

Swift Package Manager

To depend on the StubKit package, you need to declare your dependency in your Package.swift:

.package(url: "https://github.com/polac24/StubKit.git",  from: "0.1.0")

and to your application/library target, add "StubKit" to your dependencies.

Carthage

Add the following line to your Cartfile:

github "polac24/StubKit" ~> 0.1

For detailed instruction to integrate carthage dependency, see Carthage

CocoaPods

StubKit is available through CocoaPods. To install it, simply add StubKit dependency for your testing target, like:

target 'StubKitExampleTests' do
    inherit! :search_paths
    pod 'StubKit'
end

Author

Bartosz Polaczyk, polac24@gmail.com

License

StubKit is available under the MIT license. See the LICENSE file for more info.