I am the author of iOS 17 Fundamentals, Building iOS User Interfaces with SwiftUI, and eight other courses on Pluralsight.
Deepen your understanding by watching!
TDD for iOS in Swift – What’s the Goal?
Before actually doing Test Driven Development (TDD) for your iOS / Mac app, it’s really important to ask a very fundamental question: What’s the goal? What am I aiming for when I say I want to drive my iOS development with tests?
2 Goals of TDD in iOS
A recent course I watched on Pluralsight called TDD as a Design Tool gave me some insight on two goals for doing Test Driven Development:
- Make sure my code is in the right place
- Make sure my logic is correct
Let’s take them one at a time…
A place for everything, and everything in its place
Believe it or not, Test Driven Development is an extremely powerful tool for ensuring that code is written in the right place. But what do I mean by “the right place”?
In the object-oriented world, “places” are data structures, such as classes and structs, and their publicly accessible methods.
When I employ TDD in a project, I will tend to be driven to making sure my code ends up in the right place. I’ll give you an example:
Starting Places
When I create a new iOS project, Xcode sets me up with a Storyboard, a blank Scene, and a View Controller. Xcode also generates a Test target for me… BUT a what am I most aware of right from the onset? The Storyboard and the View Controller.
The natural inclination, then is to start dragging things onto the design surface and wiring them up to the controller as Outlets and Actions, and off I go!
It’s like I’m lead to the ever-common temptation to put all my code for a given screen in the application inside its corresponding View Controller.
TDD Tension
While I may be led there, and while it may seem convenient, it would seem that TDD wants to start me off in a different “place” altogether. Since TDD asserts that I should not write any code unless there’s a test requiring it to be written, I’d be driven away from my main project into my Test project. My canvas at that point is a fresh XCTestCase class.
UI at this point is not on my mind. Here, I care more about the foundations of the app itself. I begin to consider the application’s domain, and its behavior apart from its user interface. This is very important if I want to truly write decoupled, modular, maintainable code.
Rather than weigh down my View Controller with tons of responsibility, TDD drives me to try and build separate classes to steward small bits of my application. Those small classes can be tested much more easily than trying to get an enormous View Controller instantiated and configured in my test suite.
This is just one small example of how TDD can get you off on the right track to putting code in its proper place.
Your logic was impeccable, Captain
The second goal of TDD in iOS is making sure my logic is correct, or, as Spock would say, “impeccable”.
Does my application’s code do what it should do? Can I write my code in such a way that I can easily verify it? TDD, by nature, pushes me in the direction of being able to verify the accuracy of my code’s logical outcomes… that is, how it behaves.
Fascinating is a word I use for the unexpected
Testing has this way of setting expectations. One thing I’ve really enjoyed about TDD’s notorious red-green-refactor cycle is that I know certainly and immediately when I’ve messed up (ie, run across a “fascinating” situation in Spock terms).
- When I write the test, it should fail the first time. If it doesn’t, I’ve messed up.
- When I write the code to pass the test and the test fails, I also know I’ve messed up.
Next Steps
With the fundamental goals of TDD in place, I feel more prepared from a foundational standpoint to venture into actuall test-driven practices for iOS.
I am learning so much in the area of testing – it’s a technique I’m practicing regularly, both in Swift and in C# (for fun and for work, respectively), so as I grow and discover ways to optimize the TDD experience in Xcode / Swift, I’ll be sharing them. Stay tuned!