TL;DR There are many kinds of tests that can be written by developers, all have their place and all can be hugely valuable. Writing tests will ensure you are being a good team player and your colleagues will appreciate it.
Why we test
Testing a project dramatically increases its maintainability and scalability, knowing your code works now is a pretty useful thing. Knowing legacy code works as you make changes to it however is a great thing. Testing helps us document our codebase and communicate our intentions for future developers. They can offer a glimpse into all those subtle little scenarios and use cases that often get lost as a project changes hands. Effective testing increases quality of the product we sell.
Types of tests for iOS engineers
The first test type is the good ‘ol fashioned basic functionality tests. These tests should be short and sweet and will make up the main body of your test suite. The main purpose is to ensure the integrity of your codebase. As mentioned previously these tests can be a great way to communicate intention to future developers (including yourself) who find them self working on this codebase. Even if other tests are not run as part of CI, there is usually no excuse for these tests not being run.
Snapshot tests take a picture of a view and diff it with a reference image.
You build the test by running it once in record mode to generate the reference image. After this you disable record mode and in future if there is a pixel difference between the image generated in your test and the reference image the test will fail. Snapshot tests run in about the same time as a series of unit tests and are extremely useful for building confidence in dynamic views. This can significantly reduce the time spend going back and forward to testers and designers.
UITests are a great way to test app interactions and behaviours. These are the slowest running tests so it is important to think about what kind of scenarios you test using them. They are best suited to running longer interactions. When looking at a piece of functionality I would always advise trying to test it using a different method first and only writing these as a last resort in order to keep your test suite quick.
Being a good team citizen
Testing is a team exercise and as part of that you should be a good team citizen and communicate properly with other members of the team, for example, never say don’t worry I've covered it with unit tests to a tester. You should explain your testing approaches to testers so they can decide what they will focus on.
Write tests that are valuable, don't waste time writing a unit test to see if that IBOutlet is connected to that view, you are going to find out pretty quickly if it is not! Writing tests for the sake of writing tests is just as bad as writing no tests at all. When you discover the reason for a bug write a test for that scenario that fails before you fix the issue
Most of us change projects, we will leave the project we are currently working on and come on to a new project. When coming onto a new project a good test suite is an invaluable source of information. A project with a strong test suite is always far easier to begin developing on. There is a strong correlation between well tested projects and projects that have been kept clean and up well refactored. This is because refactoring is so much easier on a well tested project.
Tips for writing good tests
Keep it readable, clean code and decent test names
Its ok to separate tests for one class across multiple files.
Where a mock is required think about reusability and put it somewhere sensible.
Keep your tests concise - Just because you can test everything in one go doesn't mean you should.
SweatWhatMatters - Don’t write pointless tests.
Think about your tests either before you write your code or as you are writing it.
Isolate complex areas and thoroughly test
Reduce dependency using injection
Use the right tool for the job (unit, integration, snapshot, UI)
Speak to a tester, they are better at coming up with scenarios than you
Coverage is the term given to the amount of your code that is executed during a full test run. It is NOT however, a figure of what percentage of your code is tested. Some companies demand coverage be above a certain percentage and this usually leads to pointless tests being written.
I mainly use coverage to investigate what parts of my code are dangerous and need wrapping.
TDD (Test driven development)
TDD is without a doubt a useful tool, and though some people will not like me saying it, it is not a golden rule and we don’t always have to do it.
When I write code I usually mix it up, writing some tests before some tests after. TDD is most useful to me for breaking down a huge task and it can also be used to ensure you only write the minimum required code to achieve the goal. Whilst I sometimes write my tests after my production code, I will always be thinking about them.
The most common argument against testing is I don't have time and this is rarely true. Testing is actually the art of taking a slow process and speeding it up. If your code is really that hard to test it’s a sign that it could be written better though there are obvious exceptions to this (C functions etc)
To sum up, testing makes your life better. Your job is easier and your teammates like you more. The mistakes past you made become easier to deal with and you will spend far less time awake at night on the eve of release wondering if changing that huge complicated messy class had any unwanted side affects.