For the love of...Testing?

We build professional apps for Android, iOS and mobile web.
Have a look

The inevitable Introduction

At jtribe, there probably isn't a single work day that goes by where someone doesn't mention TDD at least once. For the ignorantly uninitiated, TDD stands for Test Driven Development.

In all fairness, you may wonder what TDD actually is. I'll tell you, in layman's speak: It's a kind of hell on Earth, but it's also a kind of Heaven.

On the one hand, you have tests sitting there that you can run at any time, which test (get it?) that your logic is still watertight. Not the kind of watertight that the Titanic's bulkhead doors were, but actually watertight. You can make changes to your business logic, and if your tests still pass you're golden. 🎉

On the other, you may find yourself hurriedly completing a test before submitting a PR for a JIRA/Trello/Other ticket you just completed. Let's be honest, hardly anyone remembers to write tests and chances are if you say you write tests first, you're probably telling a porky. 👍

If you use TDD or if you don't, either way you'll be maintaining something. It might not be an unwieldy and untested codebase, but what you'll have is a test suite that you must remember to run and keep updated.


Writing them

Writing the tests can either be an almost-pleasurable or an entirely off-putting experience. Any seasoned iOS/Mac developer has at some point encountered the cumbersome XCTest suite, and more-than-likely wondered aloud why Apple haven't improved it. It's a relic of Objective-C days past and is hardly "Swifty" by any means.

Which brings me to Quick/Nimble. The latter is a Matcher framework that helps make your assert statements more readable and clear, while the former is the actual testing framework. Quick and Nimble, while being terrible but brilliant puns on "Swift," bring the sheer miracle that is Spec testing to Apple's Swift.

If your only experience with testing has been XCTest, then I feel sorry for you. Compared to Spec, XCTest is positively archaic. Though I concede that on first glance, Spec testing is a little odd. You're basically using English (oh, the horror!) to describe what your tests should do.

Examples

Spec:

import Quick  
import Nimble

class BlogSpec: QuickSpec {  
  override func spec() {
    describe("the post about tests") {
      it("has everything you need to get started") {
        let prose = store.state.testArticle
        expect(prose.first).to(contain("writing them"))
        expect(prose.first).to(contain("examples"))
      }

      context("if it doesn't have what you're looking for") {
        it("needs to be re-read") {
          let you = You(awesome: false)
          expect(you.wroteGoodTest).toEventually(beTruthy())
        }
      }
    }
  }
}

XCTest:

class Tests: XCTestCase {

    var you = You(awesome: true) // For now...
    let prose = store.state.testArticle

    override func tearDown() {
        // Put teardown code here. This method is called after the invocation of each test method in the class.
        you.awesome = false
        super.tearDown()
    }

    // Source: http://nshipster.com/xctestcase/

    func testFetchRequestWithMockedManagedObjectContext() {
        class MockNSManagedObjectContext: NSManagedObjectContext {
            override func executeFetchRequest(request: NSFetchRequest!, error: AutoreleasingUnsafePointer<NSError?>) -> [AnyObject]! {
                return [["name": "Johnny Appleseed", "email": "johnny@apple.com"]]
            }
        }

        let mockContext = MockNSManagedObjectContext()
        let fetchRequest = NSFetchRequest(entityName: "User")
        fetchRequest.predicate = NSPredicate(format: "email ENDSWITH[cd] %@", "@apple.com")
        fetchRequest.resultType = .DictionaryResultType

        var error: NSError?
        let results = mockContext.executeFetchRequest(fetchRequest, error: &error)

        XCTAssertNil(error, "error should be nil")
        XCTAssertEqual(results.count, 1, "fetch request should only return 1 result")

        let result = results[0] as [String: String]
        XCTAssertEqual(result["name"] as String, "Johnny Appleseed", "name should be Johnny Appleseed")
        XCTAssertEqual(result["email"] as String, "johnny@apple.com", "email should be johnny@apple.com")
    }
}

I'll be honest, XCTest has to be the most readable I've ever seen. Not. 👎

I took the example from NSHipster to highlight how unwieldy something like that can end up being when you're using XCTest to write your tests. It only gets worse from there.

If you used Quick/Nimble, it'd be smaller and easier to read and write, and it'd be easier for devs who succeed you in the future, to pick up and fix/change as needed.


You may disagree with me, and that's fair enough. But you're wrong.

Shortly after starting here, I balked at writing tests because I found XCTest confusing and unreadable. After being introduced to Quick/Nimble I actually find writing tests an enjoyable experience!

So take from this what you will. But Quick and Nimble work with Objective-C too, so...

Sessam Eht Nioj! 😂 (10 points if you get the reference!)