Swift is a type-safe, statically typed language, which means that the compiler checks your code for type errors at compile time. Put simply, you are not able to release your application if your code contains even a single type error. That is a good thing.
Swift's type system has its pros and cons, but the compiler helping me write code that is sound is something I wouldn't want to do without. The danger is that we rely too much on the compiler, though. While it is true that the compiler helps us avoid easy to miss mistakes, such as a typo or an incomplete protocol conformance, you are responsible for the code you write and release. The compiler is powerful but limited. It isn't able to spot functional bugs in your code. That is your responsibility.
Testing
If you are serious about the code you write, then testing is an integral part of your responsibilities. Testing is a broad subject and I tend to split it up into three categories, manual testing, integration testing, and unit testing.
Manual testing has its place, but it takes time and, as the name implies, it cannot be automated. Some flows of your application cannot be automated due to technical limitations. While manual testing has its value, it should be kept to a minimum.
Integration testing focuses on flows that span multiple code paths. Integration tests should be automated and should be run often. Focus on the most critical and the most common flows of your application first, for example, signing in and signing up.
Unit testing is what this series is about. Writing unit tests should be integrated into your development workflow. It shouldn't be an afterthought. Like integration tests, your suite of unit tests should be run often to make sure you are immediately notified if something breaks.
Where to Start
In the early days of my career as a developer, I made the mistake many developers make. I didn't pay much attention to unit testing. I knew I was supposed to write them, but I wasn't sure where to start or what to unit test. Not knowing where to start is one of the reasons developers skip or ignore unit testing.
What to Unit Test
Apple's XCTest framework has evolved over the years, but it is fairly basic and has a gentle learning curve. Writing unit tests doesn't need to be difficult. If your unit tests are overly complicated, then that is usually an indication that the code you are unit testing needs to be looked at.
Writing Testable Code
One of the benefits I like most about unit testing is that it tends to improve the quality of the code you write. Because you write unit tests, the testability of your code improves and that usually means better code.
I have written a lot about the Model-View-ViewModel pattern over the years. I enjoy using it because it promotes testability. It decouples the building blocks of your project, making them easier to unit test in isolation.
The same is true for dependency injection. A carefully crafted unit test isn't impacted by the environment it runs in. For example, your unit test shouldn't fail if the device it runs on has no network connection. In other words, the outcome of the unit test should always be the same. The more a unit test depends on external conditions, the more it tends to be be flaky. Flaky unit tests are frustrating and something you need to avoid. We discuss how to avoid such unit tests later in this series.
Making It a Habit
Make unit testing part of your development flow. If you delay writing unit tests, then it is very likely they won't get written. I have seen this pattern often in companies, large and small. When a deadline needs to be met, unit testing often takes a back seat. It rarely happens that unit tests are written for a feature after it went live. There is always something more important than writing unit tests.
Don't Focus on Code Coverage
I like Xcode's built-in support for code coverage. I use it often, but I don't focus on it too much. I primarily use code coverage to show me the gaps in my test suite. Code coverage is an estimate and you should treat it as such.
That said, code coverage can be helpful if you don't have a test suite in place to track progress and set goals. I don't recommend making code coverage a key metric, though. You don't want developers to write unit tests to meet a code coverage target. That may result in unit tests that have little value.
What's Next?
This series starts with the basics, but it covers more than writing unit tests. You learn what to unit test and, equally important, what not to unit test. We discuss the qualities of a well-written unit test and we take a look at code smells in a test suite. We use code coverage to fill the gaps in a test suite and leverage mocking and stubbing to create a test suite that is fast and robust.