6 min read

In recent years, testing has become more wide-spread. Software engineers have routinely recognized that tests have the power to assist in the elimination of bugs. At the same time, tests can help minimize the unexpected behaviors that arise when many people commit changes to the same source code.

Testing Methodologies on Various Platforms

Test Driven Development is a methodology that requires developers to write the tests before they write the actual implementation. The new tests initially fail. However, the engineer is confident that the module performs as expected when the tests pass. Test Driven Development has been expanded into Behavior-Driven Development (BDD).

BDD focuses on clear communication between all collaborators. It describes all levels of software in an effective, readable language, and focuses on the expected “behaviors” of a module. As a disclaimer, there is not a mass-consensus on unit testing methodologies. Therefore, I will detail the benefits of this methodology. The final decision rests in the comfort of the engineers and the readability of the tests.

Looking towards contemporary web development, a developer sees that testing is both accepted and expected. Frameworks like Ruby on Rails natively support and encourage testing out of the box. Node.js has also embraced testing with frameworks like Mocha and should.js. Many companies, including Twitter, list the ability to write sound unit tests as a prerequisite for job positions. The testing community is thriving with tools such as RSpec and hosted continuous-integration services like Travis CI or Circle CI.

On the other hand, unit testing in iOS Development has become an abyss. Shadowing at a known development company, I looked over their source code. I asked, “Where are your tests?” Of course, I was told that the engineers adequately test before deploying, and “they do not need unit tests.” Coming from a Ruby on Rails background, I was distraught. Surely, this must only be confined to this company.

Later, when I went to Apple’s Worldwide Development Conference, I was shocked to learn that testing is very neglected across the entire community. Apple is telling developers to write tests, and they are releasing “new” unit testing frameworks (XCTest). They are even building new tools to aid with continuous integration testing (Xcode server), and there are other teams introducing performance tests. Unfortunately, there is a fundamental problem. iOS tests are not fun, they are not descriptive, they are not well-structured, and they lack enthusiastic support. If developers refuse to test the outputs of their methods and their user interface interactions, there is no chance that they will take the time to write tests to put a metric on performance and measure baselines.

The Downfalls of the Standard Testing Framework

Ultimately, iOS developers need a new structure and enthusiasm from the web development community. Many developers, including myself, have come across the Quick testing framework on GitHub. Quick promotes BDD, and it works with Objective-C and Swift. Both old, new, and hybrid projects are supported! To dramatize some of the differences, let’s look at an XCTestCase:

// (Swift) Apple’s XCTest
import XCTest

class ArithmeticTests: XCTestCase {

    var calculator : Calculator!
    
    override func setUp() {
        calculator = Calculator()
    }
    
    func testCalculatorAddition() {
        let expected = 4
        let actual = calculator.add(2, 2)
        XCTAssertEqual(expected, actual, "2 + 2 doesn't equal 4")
    }
    
    func testCalculatorSubtraction() {
		  let expected = 4
        let actual = calculator.subtract(10, 6)
        XCTAssertEqual(expected, actual, "10 - 6 doesn't equal 4")
    }
    
    func testCalculatorMultiplication() {
		  let expected = 16
        let actual = calculator.multiply(4, 4)
        XCTAssertEqual(expected, actual, "4 * 4 doesn't equal 16")
    }
    
    func testCalculatorDivision() {
		  let expected = 10
        let actual = calculator.divide(100, 10)
        XCTAssertEqual(expected, actual, "100 / 10 dosen't equal 10")
    }
    
}

The first thing I notice is the incredible amount of redundancy. Notice, each function begins with the “test” prefix. However, developers already know that these are test cases. Just look at the name of the class and its superclass.   To minimize the amount of redundancy, one could bypass the expected and actual variable declarations. They could put everything on one line, like so:

XCTAssertEqual(10, calculator.divide(100, 10), “100 / 10 doesn’t equal 10”)

However, most developers will agree that this becomes less concise and readable when more arguments are passed into the function. An even more bothersome element is the message that is attached to each assertion. Can the error message not be implied by the two values and the name of the test? This is obviously possible, because Quick does it. At the same time, it looks like the error message is somehow part of the assertion, and any developer who is not familiar with this type of assertion would be confused without reading some guide or the XCTest documentation.

Quick and its Advantages

Let’s contrast this by applying the concepts of BDD using the Quick framework:

// (Swift) Quick
import Quick
import Nimble

class ArithmeticSpec: QuickSpec {
    override func spec() {
        
        describe("Calculator") {
            var calculator : Calculator!
            
            beforeEach {
                calculator = Calculator()
            }
            
            it("can add") {
                let value = calculator.add(2, 2)
                expect(value).to(equal(4))
                
                // expect(calculator.add(2, 2)).to(equal(4))
            }
            
            it("can subtract") {
			    let value = calculator.subtract(10, 6)
                expect(value).to(equal(4))
                
                // expect(calculator.subtract(10, 6)).to(equal(4))
            }
            
            it("can multiply") {
                let value = calculator.multiply(4, 4)
                expect(value).to(equal(16))
                
                // expect(calculator.multiply(4, 4)).to(equal(16))
            }
            
            it("can divide") {
                let value = calculator.divide(100, 10)
                expect(value).to(equal(10))
                
                // expect(calculator.divide(100, 10)).to(equal(10))
            }
        }
    }
}

We’ve done several important things here. First, we made the tests more modular. Under the Arithmetic specification, there could be other objects besides a Calculator. Notice we are describing the behaviors of each. For instance, if we had an Arithmetic Sequence Recognizer, we could describe it with its own test cases. Everything related can stay grouped under an Arithmetic “module.” Second, we simply described the behaviors of the Calculatorin English. We said things like, “it(“can add”)” which is understandable to anyone. At the same time, we replaced assertions and error messages with expectations. Once again, our test reads as a plain English sentence: “expect(value).to(equal(4))”. Third, we removed most of the redundancy. Quick will automatically generate the messages for failing tests.

For readability, I separated the calculation and the expectation. However, I included a comment with the single-line version that is very popular. To me, both appear more readable than an XCTest assertion.

In part 2, I will demonstrate how to sufficiently test an iPhone app with Quick. I’ll also include the source code, so it should be easy to follow along.

About the Author

Benjamin Reed began Computer Science classes at a nearby university in Nashville during his sophomore year in high school. Since then, he has become an advocate for open source. He is now pursing degrees in Computer Science and Mathematics fulltime. The Ruby community has intrigued him, and he openly expresses support for the Rails framework. When asked, he believes that studying Rails has led him to some of the best practices and, ultimately, has made him a better programmer. iOS development is one of his hobbies, and he enjoys scouting out new projects on GitHub. On GitHub, he’s appropriately named @codeblooded. On Twitter, he’s @benreedDev.

LEAVE A REPLY

Please enter your comment!
Please enter your name here