Moocher
A set of "rspec-like" test matchers that "mooch" off of XCTest
.
Adding Moocher to your project
CocoaPods
CocoaPods is the recommended way to add Moocher
to your project.
- Add
Moocher
to your Podfilepod 'Moocher'
. - Install the pod(s) by running
pod install
. - Add
Moocher
to your files withimport Moocher
.
Swift Package Manager
Swift Package Manager can be used to add Moocher
the to your project:
- Add
.package(url: "https://github.com/rbaumbach/Moocher", from: "0.4.0")
- Follow intructions to add the
Moocher
package to your project.
Note: Since Moocher
requires the library XCTest
, when you add Moocher
to your project, be sure that it's added to your unit testing target.
Clone from Github
- Clone repository from github and copy files directly, or add it as a git submodule.
- Add all files from
Sources/Moocher
directory to your project.
Usage
Moocher matchers are inspired by RSpec:
expect(AnswerToUltimateQuestion.value).to.equal(42)
Equivalence and Identity
equal
This matcher works for types that conform to Equatable
, but also works for types that conform to FloatingPoint
as well.
expect(99).to.equal(99)
expect(99).toNot.equal(100)
expect(9.9).to.equal(9.9, within: 0.1)
expect(9.9).toNot.equal(10.9, within: 0.1)
Types
beInstanceOf
class Dog { }
let chihuahua = Dog()
let pancho = chihuahua
let miniPinscher = Dog()
expect(chihuahua).to.beInstanceOf(pancho)
expect(chihuahua).toNot.beInstanceOf(miniPinscher)
beKindOf
class Dog { }
struct Fish { }
let dog = Dog()
expect(dog).to.beKindOf(Dog.self)
expect(dog).toNot.beKindOf(Fish.self)
conformTo
protocol Wolf { }
class Dog: Wolf { }
protocol Goat { }
let dog = Dog()
expect(dog).to.conformTo(Wolf.self)
expect(dog).toNot.conformTo(Goat.self)
Comparisons
beLessThan
expect(9).to.beLessThan(10)
expect(9).toNot.beLessThan(8)
beLessThanOrEqualTo
expect(9).to.beLessThanOrEqualTo(9)
expect(11).toNot.beLessThanOrEqualTo(10)
beGreaterThan
expect(9).to.beGreaterThan(8)
expect(7).toNot.beGreaterThan(8)
beGreaterThanOrEqualTo
expect(9).to.beGreaterThanOrEqualTo(8)
expect(7).toNot.beGreaterThanOrEqualTo(8)
Truthiness
beTruthy
expect(true).to.beTruthy()
expect(false).toNot.beTruthy()
beFalsy
expect(false).to.beFalsy()
expect(true).toNot.beFalsy()
beNil
var number: Int?
expect(number).to.beNil()
number = 99
expect(number).toNot.beNil()
Error Throwing
throwError
The throwError
matcher can be used in 4 different ways:
throwError(block:)
expect({ try functionThatThrowsAnError() })
.to.throwError()
expect({ try functionThatDoesNotThrowAnError() })
.toNot.throwError()
throwError(block:errorHandler:)
expect({ try functionThatThrowsAnError() })
.to.throwError { error in
// Do something with error
}
throwError(block:specificError:)
expect({ try functionThatThrowsABurritoError() })
.to.throwError(specificError: BurritoError.beansMissing))
expect({ try functionThatThrowsABurritoError() })
.toNot.throwError(specificError: TacoError.salsaMissing))
throwError(block:errorType:)
expect({ try functionThatThrowsABurritoError() })
.to.throwError(errorType: BurritoError.self))
expect({ try functionThatThrowsABurritoError() })
.toNot.throwError(errorType: TacoError.self))
Collection
beEmpty
This matcher works for types that conform to Collection
.
let emptyArray: [Int] = []
expect(emptyArray).to.beEmpty()
expect([1, 2, 3]).toNot.beEmpty()
This matcher also works for String
s as well.
expect("").to.beEmpty()
expect("Taco").toNot.beEmpty()
startWith
expect(["a", "b", "c"]).to.startWith("a")
expect([2, 4, 8]).toNot.startWith(1)
endWith
expect(["a", "b", "c"]).to.endWith("c")
expect([2, 4, 8]).toNot.endWith(7)
haveSizeOf
expect(["taco", "burrito"]).to.haveSizeOf(2)
expect([2, 4, 8]).toNot.haveSizeOf(7)
Sequence
contain
This matcher works for types that conform to Sequence
.
expect(["uno", "dos", "tres"]).to.contain("tres")
expect("Billy Goat").toNot.contain("$")
This matcher also works for finding a substring within a String
.
expect("Corey and Trevor").to.contain("and")
expect("Richard LaFleur").toNot.contain("Smart")
Compound matchers
Some of the matchers allow for compounding assertions:
expect([1, 2, 3]).to.contain(1).and.startWith(1).and.endWith(3)
expect([1, 2, 3]]).toNot.haveSizeOf(4).and.contain(7)
The following matchers can be used compoundly:
startWith
endWith
contain
haveSizeOf
haveMinValueOf
haveMaxValueOf
Moocher Polling
This repo also contains an additional library for polling values when writing integration tests that have to "wait" for assertions.
This can be added to your project:
CocoaPods
- Add
MoocherPolling
to your Podfilepod 'Moocher/Polling'
. - Install the pod(s) by running
pod install
. - Add
MoocherPolling
to your files withimport MoocherPolling
.
Swift Package Manager
- Add
MoocherPolling
to your target:Build Phases
->Link Binary With Libraries
. - Add
MoocherPolling
to your files withimport MoocherPolling
.
HangOn
HangOn
is a function that allows block input that will wait for an assertion given a specific timeframe.
hangOn(for: .seconds(10)) { complete in
var number = 100
DispatchQueue.global(qos: .background).async { [weak self] in
sleep(3)
number = 99
DispatchQueue.main.async {
expect(number).to.equal(99)
complete()
}
}
}
toSomeday and toNever
Values can be constantly polled using familiar expect
syntax using toSomeday
and toNever
.
var dogArray = ["Chihuhahua", "Miniature Pinscher", "Border Collie"]
DispatchQueue.global(qos: .background).async {
sleep(3)
dogArray.append("German Shepard")
}
expect(dogArray).toSomeday.contain("German Shepard")
var dogArray = ["Chihuhahua", "Miniature Pinscher", "Border Collie"]
DispatchQueue.global(qos: .background).async {
sleep(3)
dogArray.append("German Shepard")
}
expect(dogArray).toNever.contain("Poodle")
The expect
clause will block tests from running until either a timeout has been hit, or the assertion you expect is true. The default timeout is 5
seconds. This can be set by providing a timeout
value.
The interval in which the polling is done on the value being asserted has a default interval of 100
milliseconds. This can be set by providing a pollingInterval
value.
expect(dogArray, timeout: .seconds(10), pollingInterval: .miliseconds(500)).toSomeday.contain("German Shepard")
The following matchers can be used for toSomeday
and toNever
:
beNil
equal
contain