[{"document_url": "https://www.sfsoftwareist.com/2018/06/20/wwdc-refactoring/", "unformatted_src": "/images/fulls/IMG_0087.jpg", "src": "https://www.sfsoftwareist.com/images/fulls/IMG_0087.jpg", "formatted_filename": "IMG"}, null]


There, the presenter refactors a method that might be seen in any application. The original method would be hard or impossible to unit tests due to its dependency on the UIApplication singleton. They refactor it using several techniques, including mocking a dependency, in order to make the logic testable. The result is a much more testable unit of code. Here is the original method:

And here is the refactored version:

And this is how you would test it:

This is a huge improvement over the original method. It allows us to test all the logic contained in DocumentOpener. It decouples the application singleton from the logic under test. My only objection is that the test is a little convoluted. As someone unfamiliar with the code, I need to examine the mock object to see how it works and I need to open up the DocumentOpener to understand how it interacts with the MockURLOpener. Additionally, the URLOpening protocol makes the production code harder to reason about. Was the protocol added to facilitate testing or did the writer truly intend for consumers of DocumentOpener to implement multiple URLOpening classes? What if we refactored this using some tips I’ve outlined in previous articles?

Arguably this is a little nicer; we’ve removed a few lines of code and a type declaration. The real value comes when attempting to test this unit.

As you can see, we are injecting a single method instead of a conforming object. Additionally, we are returning a value representing the action we’d like to perform. The advantage is that our test is reduced from 18 lines to 8, there is almost no arrange code, and the return value is easily verified.

This isn’t always the best approach. If DocumentOpener had a lot of interaction with the system via URLOpening, we’d end up injecting a lot of methods into DocumentOpener. At this point it might make sense to inject a URLOpening object instead.

In conclusion, by avoiding mocks and focusing on value types, it is possible to write tests that are shorter and easier to understand down the road.