Documentation/en-us/QuickExamplesAndGroups.md
Quick uses a special syntax to define examples and example groups.
In Effective Tests Using XCTest: Arrange, Act, and Assert, we learned that a good test method name is crucial--when a test starts failing, it's the best way to determine whether we have to fix the application code or update the test.
Quick examples and example groups serve two purposes:
itExamples, defined with the it function, use assertions to demonstrate
how code should behave. These are like test methods in XCTest.
it takes two parameters: the name of the example, and a closure.
The examples below specify how the Sea.Dolphin class should behave.
A new dolphin should be smart and friendly:
// Swift
import Quick
import Nimble
import Sea
class DolphinSpec: QuickSpec {
override class func spec() {
it("is friendly") {
expect(Dolphin().isFriendly).to(beTruthy())
}
it("is smart") {
expect(Dolphin().isSmart).to(beTruthy())
}
}
}
// Objective-C
@import Quick;
@import Nimble;
QuickSpecBegin(DolphinSpec)
it(@"is friendly", ^{
expect(@([[Dolphin new] isFriendly])).to(beTruthy());
});
it(@"is smart", ^{
expect(@([[Dolphin new] isSmart])).to(beTruthy());
});
QuickSpecEnd
Use descriptions to make it clear what your examples are testing. Descriptions can be of any length and use any character, including characters from languages besides English, or even emoji! :v: :sunglasses:
describe and contextExample groups are logical groupings of examples. Example groups can share setup and teardown code.
describeTo specify the behavior of the Dolphin class's click method--in
other words, to test the method works--several it examples can be
grouped together using the describe function. Grouping similar
examples together makes the spec easier to read:
// Swift
import Quick
import Nimble
class DolphinSpec: QuickSpec {
override class func spec() {
describe("a dolphin") {
describe("its click") {
it("is loud") {
let click = Dolphin().click()
expect(click.isLoud).to(beTruthy())
}
it("has a high frequency") {
let click = Dolphin().click()
expect(click.hasHighFrequency).to(beTruthy())
}
}
}
}
}
// Objective-C
@import Quick;
@import Nimble;
QuickSpecBegin(DolphinSpec)
describe(@"a dolphin", ^{
describe(@"its click", ^{
it(@"is loud", ^{
Click *click = [[Dolphin new] click];
expect(@(click.isLoud)).to(beTruthy());
});
it(@"has a high frequency", ^{
Click *click = [[Dolphin new] click];
expect(@(click.hasHighFrequency)).to(beTruthy());
});
});
});
QuickSpecEnd
When these two examples are run in Xcode, they'll display the
description from the describe and it functions:
DolphinSpec.a_dolphin_its_click_is_loudDolphinSpec.a_dolphin_its_click_has_a_high_frequencyAgain, it's clear what each of these examples is testing.
beforeEach and afterEachExample groups don't just make the examples clearer, they're also useful for sharing setup and teardown code among examples in a group.
In the example below, the beforeEach function is used to create a brand
new instance of a dolphin and its click before each example in the group.
This ensures that both are in a "fresh" state for every example:
// Swift
import Quick
import Nimble
class DolphinSpec: QuickSpec {
override class func spec() {
describe("a dolphin") {
var dolphin: Dolphin!
beforeEach {
dolphin = Dolphin()
}
describe("its click") {
var click: Click!
beforeEach {
click = dolphin.click()
}
it("is loud") {
expect(click.isLoud).to(beTruthy())
}
it("has a high frequency") {
expect(click.hasHighFrequency).to(beTruthy())
}
}
}
}
}
// Objective-C
@import Quick;
@import Nimble;
QuickSpecBegin(DolphinSpec)
describe(@"a dolphin", ^{
__block Dolphin *dolphin = nil;
beforeEach(^{
dolphin = [Dolphin new];
});
describe(@"its click", ^{
__block Click *click = nil;
beforeEach(^{
click = [dolphin click];
});
it(@"is loud", ^{
expect(@(click.isLoud)).to(beTruthy());
});
it(@"has a high frequency", ^{
expect(@(click.hasHighFrequency)).to(beTruthy());
});
});
});
QuickSpecEnd
Sharing setup like this might not seem like a big deal with the dolphin example, but for more complicated objects, it saves a lot of typing!
To execute code after each example, use afterEach.
justBeforeEachIn some cases you will want to have a group of tests with a common test setup but different configuration of that setup code in the individual tests. This makes the most sense when you have an API that you are mocking, and logic on top of that API that will make different decisions based on the result.
// Swift
describe("a sleeping dolphin") {
var sleeping: Bool!
var click: Click!
justBeforeEach {
dolphin = Dolphin(sleeping: sleeping)
click = dolphin.click()
}
context("not sleeping") {
beforeEach {
sleeping = false
}
it("then it makes clicks that are loud") {
expect(click.isLoud).to(beTrue())
}
}
context("sleeping") {
beforeEach {
sleeping = true
}
it("then it does not make clicks that are loud") {
expect(click.isLoud).to(beFalse())
}
}
}
In this example, a sleeping dolphin will not make Clicks. We wish to build our tests so that we only construct one Dolphin object, and only invoke the .click() method once. This requires the use of justBeforeEach, which invokes our constructor and method after the two beforeEach blocks have been invoked. Without justBeforeEach, we would have to trigger the API call twice in our tests, with a different argument each time. For tests that have a lot of boilerplate or setup required, this can significantly reduce lines of code and complexity.
beforeEach, or justBeforeEach, or afterEach.Sometimes, in your setup and teardown code, you want to cancel executing the
remainder of the test if the setup/teardown runs into an error. You can do that
in Swift by throwing any error. This will fail the test, and prevent any
setup/teardown nested in the test tree from running. Similar to if you throw
an error during an XCTestCase's setupWithError() method.
Because of Quick's tree-structure for Tests, this prevents setup/teardown defined in nested example groups from running. Setup and teardown blocks from parent example groups will be run.
For example, with the following test:
// Swift
import Quick
import Nimble
class DolphinSpec: QuickSpec {
override class func spec() {
describe("a dolphin") {
var dolphin: Dolphin!
beforeEach {
dolphin = Dolphin()
throw DolphinError()
}
afterEach {
dolphin.rest()
}
describe("its click") {
var click: Click!
beforeEach {
click = dolphin.click()
}
it("is loud") {
expect(click.isLoud).to(beTruthy())
}
it("has a high frequency") {
expect(click.hasHighFrequency).to(beTruthy())
}
}
}
}
}
None of the setup or tests in the "its click" example group are run. Once the
DolphinError() is thrown, the test it's running in will be marked as failing.
Additionally, the dolphin.rest() function in the afterEach is still called.
It's important to note that, when a beforeEach throws, none of the
justBeforeEach blocks in the test will run.
contextDolphins use clicks for echolocation. When they approach something particularly interesting to them, they release a series of clicks in order to get a better idea of what it is.
The tests need to show that the click method behaves differently in
different circumstances. Normally, the dolphin just clicks once. But when
the dolphin is close to something interesting, it clicks several times.
This can be expressed using context functions: one context for the
normal case, and one context for when the dolphin is close to
something interesting:
// Swift
import Quick
import Nimble
class DolphinSpec: QuickSpec {
override class func spec() {
describe("a dolphin") {
var dolphin: Dolphin!
beforeEach { dolphin = Dolphin() }
describe("its click") {
context("when the dolphin is not near anything interesting") {
it("is only emitted once") {
expect(dolphin.click().count).to(equal(1))
}
}
context("when the dolphin is near something interesting") {
beforeEach {
let ship = SunkenShip()
Jamaica.dolphinCove.add(ship)
Jamaica.dolphinCove.add(dolphin)
}
it("is emitted three times") {
expect(dolphin.click().count).to(equal(3))
}
}
}
}
}
}
// Objective-C
@import Quick;
@import Nimble;
QuickSpecBegin(DolphinSpec)
describe(@"a dolphin", ^{
__block Dolphin *dolphin = nil;
beforeEach(^{ dolphin = [Dolphin new]; });
describe(@"its click", ^{
context(@"when the dolphin is not near anything interesting", ^{
it(@"is only emitted once", ^{
expect(@([[dolphin click] count])).to(equal(@1));
});
});
context(@"when the dolphin is near something interesting", ^{
beforeEach(^{
[[Jamaica dolphinCove] add:[SunkenShip new]];
[[Jamaica dolphinCove] add:dolphin];
});
it(@"is emitted three times", ^{
expect(@([[dolphin click] count])).to(equal(@3));
});
});
});
});
QuickSpecEnd
Strictly speaking, the context keyword is a synonym for describe,
but thoughtful use will make your spec easier to understand.
In Effective Tests Using XCTest: Arrange, Act, and Assert, we looked at how one test per condition was a great way to organize test code. In XCTest, that leads to long test method names:
func testDolphin_click_whenTheDolphinIsNearSomethingInteresting_isEmittedThreeTimes() {
// ...
}
Using Quick, the conditions are much easier to read, and we can perform setup for each example group:
describe("a dolphin") {
describe("its click") {
context("when the dolphin is near something interesting") {
it("is emitted three times") {
// ...
}
}
}
}
You can temporarily disable examples or example groups that don't pass yet. The names of the examples will be printed out along with the test results, but they won't be run.
You can disable an example or group by prepending x:
// Swift
xdescribe("its click") {
// ...none of the code in this closure will be run.
}
xcontext("when the dolphin is not near anything interesting") {
// ...none of the code in this closure will be run.
}
xit("is only emitted once") {
// ...none of the code in this closure will be run.
}
// Objective-C
xdescribe(@"its click", ^{
// ...none of the code in this closure will be run.
});
xcontext(@"when the dolphin is not near anything interesting", ^{
// ...none of the code in this closure will be run.
});
xit(@"is only emitted once", ^{
// ...none of the code in this closure will be run.
});
Sometimes it helps to focus on only one or a few examples. Running one
or two examples is faster than the entire suite, after all. You can
run only one or two by using the fit function. You can also focus a
group of examples using fdescribe or fcontext:
fit("is loud") {
// ...only this focused example will be run.
}
it("has a high frequency") {
// ...this example is not focused, and will not be run.
}
fcontext("when the dolphin is near something interesting") {
// ...examples in this group are also focused, so they'll be run.
}
fit(@"is loud", {
// ...only this focused example will be run.
});
it(@"has a high frequency", ^{
// ...this example is not focused, and will not be run.
});
fcontext(@"when the dolphin is near something interesting", ^{
// ...examples in this group are also focused, so they'll be run.
});
beforeSuite and afterSuiteSome test setup needs to be performed before any examples are
run. For these cases, use beforeSuite and afterSuite.
In the example below, a database of all the creatures in the ocean is created before any examples are run. That database is torn down once all the examples have finished:
// Swift
import Quick
class DolphinSpec: QuickSpec {
override class func spec() {
beforeSuite {
OceanDatabase.createDatabase(name: "test.db")
OceanDatabase.connectToDatabase(name: "test.db")
}
afterSuite {
OceanDatabase.teardownDatabase(name: "test.db")
}
describe("a dolphin") {
// ...
}
}
}
// Objective-C
@import Quick;
QuickSpecBegin(DolphinSpec)
beforeSuite(^{
[OceanDatabase createDatabase:@"test.db"];
[OceanDatabase connectToDatabase:@"test.db"];
});
afterSuite(^{
[OceanDatabase teardownDatabase:@"test.db"];
});
describe(@"a dolphin", ^{
// ...
});
QuickSpecEnd
You can specify as many beforeSuite and afterSuite as you like. All
beforeSuite closures will be executed before any tests run, and all
afterSuite closures will be executed after all the tests are finished.
There is no guarantee as to what order these closures will be executed in.
There may be some cases in which you'd like the know the name of the example
that is currently being run, or how many have been run so far. Quick provides
access to this metadata in beforeEach and afterEach closures.
beforeEach { exampleMetadata in
println("Example number \(exampleMetadata.exampleIndex) is about to be run.")
}
afterEach { exampleMetadata in
println("Example number \(exampleMetadata.exampleIndex) has run.")
}
beforeEachWithMetadata(^(ExampleMetadata *exampleMetadata){
NSLog(@"Example number %l is about to be run.", (long)exampleMetadata.exampleIndex);
});
afterEachWithMetadata(^(ExampleMetadata *exampleMetadata){
NSLog(@"Example number %l has run.", (long)exampleMetadata.exampleIndex);
});