Unit Testing Time in Your App
Hi everybody, I’m Riccardo. Senior iOS Engineer at Bending Spoons, I breathe iOS development, both apps and tools and I love to share my knowledge with others.
This is the second article on a series on unit testing. You can find the first episode here.
Tests and Time
Today I’d like to talk about a tricky topic when tests are involved: the Time. Applications often need to perform computation about time: they display a timer or they have to perform computations between data acquired in two different days. App execution changes when performed on different days and at different times. How this correlates with testing?
Tests need to run fast, they need to be completely decoupled by the specific piece of data and they must be repeatable. These concepts have several implications:
- Tests should not wait any time before the assertions are evaluated;
- Tests should not rely on hardcoded values or on some static functions that return the time (e.g.: the infamous
- Tests should be runnable at any time, any day;
- Tests result must be predictable.
So… how can we achieve this?
Well, there are a few techniques that we can put in place.
Let’s take a look at this piece of (bad) code.
This code is a class that allows scheduling some notifications at precise times during the day. Now, let’s take a look at the flaws in the code:
getCurrentHour()initializes both the calendar and the date by hardcoding the parameters of these objects.
- If we write some tests to check if the morning notification has been scheduled, we should run the tests before 11 a.m. After that time, in fact, the test will fail.
How can we solve these issues? Of course, by recalling the ideas of the previous article, we can inject the dependencies by using a protocol: the
This protocol can define some basic functions that let us retrieve the dates and calendars we need. Thanks to this, we can inject the dependency into the manager and modify the time values as we need.
The code will now look like this:
Now, we are able to write all our tests! We can create the stubs for the
TimeProvider and the mock for the
NotificationScheduler to verify that the notification is properly scheduled. Also, we can write tests that can be run at any time of the day and whose results are predictable. Awesome!
How can we do that? Let see the following code, that implements some tests.
In this example, we can see how we can create a stub that we can parameterize as we want in our test cases. Also, we can see how we can create a mock that we can spy into!
By setting the right parameters into the stub, we can write all the tests we need without suffering from the time dependence of the original code and our tests will run quickly and in a deterministic way.
As usual, the example is a toy one. The test can be improved further, for example by extracting the titles of the notifications in a resource file that can be accessed from both the app and the test. However, the focus of the article is about how we can inject the time dependency in our code.
I hope that this will help you in writing better tests.