Part of the Mobile Development series
Quick + Nimble Overview
This blog post will document my initial investigation in using Quick + Nimble.
What is Quick + Nimble?
Quick is a behavior driven development (BDD) testing framework and Nimble is a matching framework that helps make tests easier to read. Coupled together, they provide the advantage of being able to be very descriptive when writing unit tests in comparison to using XCTests.
Quick + Nimble vs. XCTests
Both frameworks have their advantages and disadvantages, so I created a chart to list down the positive and negatives from my experimentation, discussing with coworkers and surfing the interwebs.
Quick + Nimble
Pros | Cons |
---|---|
Supports BBD and acceptance testing better since tests are descriptive | Third Party Dependency |
Can easily read what is being tested | Does not have performance testing |
Structure is consistent with testing done across other languages and platforms | Learning curve for developers used to XCTests |
Easier to create subsets of tests in a suite | |
Active community and open source project |
XCTests
Pros | Cons |
---|---|
Default native testing framework that is well integrated with Xcode | Hard to read and understand what is being tested |
Less of a learning curve | Assertion methods are not as thorough as the Nimble matching framework |
Can test performance | Less flexible in comparison |
Support for UI testing |
When it comes to making a decision between the two, I lean towards using XCTests as the default testing framework for my personal project. However as the project gets bigger, then I will look into using a combination of both Quick + Nimble + XCTests. For a project in which there are more contributors and stakeholders, I can see the benefits of using Quick + Nimble as its easier to perform acceptance testing, understand the value of a tests and modularize the code.
Setting Up Quick + Nimble via SPM
It was fairly easy to add the dependencies if your project is using the Swift Package Manager. Here is a snippet of what it looks like to add the frameworks in the Package.swift
file.
dependencies: [
.package(url: "https://github.com/Quick/Quick.git", from: "7.3.0"),
.package(url: "https://github.com/Quick/Nimble.git", from: 13.0.0"),
]
After adding the dependencies to the package, we can use them in a test target by adding them as products to the dependency section for the test target.
.testTarget(
name: "DiscoverKitTests",
dependencies: [
"DiscoverKit",
.product(name: "Quick", package: "Quick"),
.product(name: "Nimble", package: "Nimble")
]
)
We can test if we incorporated the dependencies properly by importing the frameworks and creating a simple test file. For example, the following file that exists in the DiscoverKitTests
target should not receive any compile errors after building. This is the start of writing a unit test.
import Quick
import Nimble
@testable import DiscoverKit
final class DiscoverKistTests: AsyncSpec {
override class func spec() {
}
}
Quick Glance at Writing Unit Tests with Quick + Nimble
Quick provides uses a single function spec()
to define a suite of test cases. spec()
is can be composed of multiple sections which are describe()
, context()
and it()
.
describe()
- acts as a container and defines the behavior or action you want to test at a high levelcontext()
- can be used if more details are needed to clarify a certain scenario that is being tested (optional)it()
- used to define individual test cases as we would with a function in XCTests; expectation checks belong here.
import Quick
import Nimble
@testable import DiscoverKit
final class DiscoverKistTests: AsyncSpec {
override class func spec() {
describe("the Discover tab") {
it("shows loading state at initial load") {
...
}
it("shows recommendation list after fetch") {
...
}
it("shows error state after fetching error") {
...
}
}
}
}
When writing a test, the above constructs are used to organize the test cases within the suite. There are also other constructs to know such as beforeEach
and afterEach
that acts similar to setting up and tear down of a tests. Although it looks like one giant test case if you are used to creating a function per test case with XCTests, you can still run the individual it
test cases by going to the test navigator and selecting on the test you want.
Within each test case, we want to validate and fulfill test expectations. Nimble provides that power through using expect()
, which replaces assertions from XCTests. There are a wide range of different matchers that can be used, some examples are:
# Checking if two values are equal
expect(value).to(equal(expectedValue))
# Checking if boolean is true
expect(isHappy).to(beTrue())
# Checking if result is nil
expect(result).to(beNil())
# Checking if collection contains a string
expect(books).to(contain("Harry Potter"))
Resources
To learn more about Quick + Nimble, here are some useful resources that dives deeper into the framework: