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.



var persistentID:

} 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"