Documentation/en-us/SharedExamples.md
In some cases, the same set of specifications apply to multiple objects.
For example, consider a protocol called Edible. When a dolphin
eats something Edible, the dolphin becomes happy. Mackerel and
Cod are both edible. Quick allows you to easily test that a dolphin is
happy to eat either one.
protocol Edible { }
class Dolphin {
private(set) var isHappy: Bool
init(happy: Bool) {
self.isHappy = happy
}
/// Makes Doplhin happy
func eat(_ edible: Edible) {
isHappy = true
}
}
class Mackerel: Edible { }
class Cod: Edible { }
The code below defines a Behavior object with a set of reusable
examples. It also specifies that both mackerel and cod behave
like SomethingEdible:
class SomethingEdible: Behavior<Edible> {
override class func spec(_ aContext: @escaping () -> Edible) {
var edible: Edible!
beforeEach {
edible = aContext()
}
it("makes dolphins happy") {
let dolphin = Dolphin(happy: false)
dolphin.eat(edible)
expect(dolphin.isHappy).to(beTruthy())
}
}
}
class MackerelSpec: QuickSpec {
override class func spec() {
var mackerel: Mackerel!
beforeEach {
mackerel = Mackerel()
}
itBehavesLike(SomethingEdible.self) { mackerel }
}
}
class CodSpec: QuickSpec {
override class func spec() {
var cod: Cod!
beforeEach {
cod = Cod()
}
itBehavesLike(SomethingEdible.self) { cod }
}
}
Because Behavior<Context> uses Swift generics, it's not possible to use it from Objective-C.
The example below defines a set of "shared examples" for "something edible", and specifies that both mackerel and cod behave like "something edible":
// Swift
import Quick
import Nimble
class EdibleSharedExamplesConfiguration: QuickConfiguration {
override class func configure(_ configuration: Configuration) {
sharedExamples("something edible") { (sharedExampleContext: @escaping SharedExampleContext) in
it("makes dolphins happy") {
let dolphin = Dolphin(happy: false)
let edible = sharedExampleContext()["edible"]
dolphin.eat(edible)
expect(dolphin.isHappy).to(beTruthy())
}
}
}
}
class MackerelSpec: QuickSpec {
override class func spec() {
var mackerel: Mackerel!
beforeEach {
mackerel = Mackerel()
}
itBehavesLike("something edible") { ["edible": mackerel] }
}
}
class CodSpec: QuickSpec {
override class func spec() {
var cod: Cod!
beforeEach {
cod = Cod()
}
itBehavesLike("something edible") { ["edible": cod] }
}
}
// Objective-C
@import Quick;
@import Nimble;
QuickConfigurationBegin(EdibleSharedExamplesConfiguration)
+ (void)configure:(Configuration *configuration) {
sharedExamples(@"something edible", ^(QCKDSLSharedExampleContext exampleContext) {
it(@"makes dolphins happy", ^{
Dolphin *dolphin = [[Dolphin alloc] init];
dolphin.happy = NO;
id<Edible> edible = exampleContext()[@"edible"];
[dolphin eat:edible];
expect(dolphin.isHappy).to(beTruthy())
});
});
}
QuickConfigurationEnd
QuickSpecBegin(MackerelSpec)
__block Mackerel *mackerel = nil;
beforeEach(^{
mackerel = [[Mackerel alloc] init];
});
itBehavesLike(@"something edible", ^{ return @{ @"edible": mackerel }; });
QuickSpecEnd
QuickSpecBegin(CodSpec)
__block Mackerel *cod = nil;
beforeEach(^{
cod = [[Cod alloc] init];
});
itBehavesLike(@"something edible", ^{ return @{ @"edible": cod }; });
QuickSpecEnd
Shared examples can include any number of it, context, and
describe blocks. They save a lot of typing when running
the same tests against several different kinds of objects.
In some cases, you won't need any additional context. In Swift, you can
simply use sharedExamples closures that take no parameters. This
might be useful when testing some sort of global state:
// Swift
import Quick
sharedExamples("everything under the sea") {
// ...
}
itBehavesLike("everything under the sea")
In Objective-C, you'll have to pass a block that takes a
QCKDSLSharedExampleContext, even if you don't plan on using that argument. Sorry, but that's the way the cookie crumbles! :cookie: :bomb:
You can also "focus" shared examples using the fitBehavesLike function.