Advanced Mocks in Swift
Fake the world
Responses to my article about Unit Testing in Swift often contained one question:
How do you test system classes you can not instantiate or configure?
I had this problem just a few weeks ago, while writing my current app. It uses MPMediaItems returned from MPMediaQueries. If you've ever used these, you'll know MPMediaItems can be instantiated, but all their properties are read-only. So I had to get creative!
MPMediaItem
Mocking an MPMediaItem is actually quite easy. You have to create a protocol, which contains all the properties and functions, you'll require. These have to be exactly like the ones used in MPMediaItem. For simplicity we will only use one property.
protocol MPMediaItemProtocol {
var persistentID: MPMediaEntityPersistentID { get }
}
MPMediaQuery
This is a little bit harder. Once again we'll create a protocol, with properties and functions we want to use. In our case we will add one more.
protocol MPMediaQueryProtocol {
var mappedItems: [MPMediaItemProtocol]? {
return self.items
}
}
Instead of using items, we'll use mappedItems (or however you want to name this).
Extensions
We still need to connect our new protocols with the actual classes. For this we use extensions
extension MPMediaItem: MPMediaItemProtocol {}
extension MPMediaQuery: MPMediaQueryProtocol {}
Having this in place, we can replace everything in our tests, thus making them predictable.
Conclusion
It's not difficult to create these mocks. It's just hard to understand in the beginning. Once you wrap your head around this idea, it should become second nature.
Next: Behavior Driven Development
Previous: TDD is "backwards"