Mocking is a useful tool when writing unit tests. Due to limitations in the current swift version, there aren’t any real mocking frameworks like the ones you see for Java and Obj-C. That said, there are work arounds. Here’s a quick one when you need a one-off:
Method to test:
func crossDissolve(toIdentifier identifier: StoryboardIdentifier) {
let nextViewController = viewController(forIdentifier: identifier)
nextViewController.modalPresentationStyle = .fullScreen
nextViewController.modalTransitionStyle = .crossDissolve
show(nextViewController, sender: self)
}
This just performs a simple cross-dissolve between two view controllers (the one it’s on to the new one).There are four things to validate:
- The
UIViewController
passed toshow(_:sender:)
is the one expect. - The
sender
is correct - That the presentation style is
fullScreen
- The transition style is
crossDissolve
Since it doesn’t return any values I’m going to have to capture them instead. The method under test is crossDissolve(…)
so I don’t want to change that behaviour. Everything else is fair game though. In this case, if I intercept the call to show(…)
I can capture the parameters passed and validate them.
Since this is a one-off I can nest a class inside my test and capture the values appropriately. Then I can fill in the test.
func testCrossDissolve() {
class MockSut: UIViewController {
var showViewController: UIViewController?
var showSender: Any?
override func show(_ vc: UIViewController, sender: Any?) {
showViewController = vc
showSender = sender
}
}
let mockSut = MockSut()
mockSut.crossDissolve(toIdentifier: .gameViewController)
XCTAssertNotNil(mockSut.showViewController as? GameViewController)
XCTAssertEqual(mockSut.showSender as? UIViewController, mockSut)
XCTAssertEqual(mockSut.showViewController?.modalPresentationStyle, .fullScreen)
XCTAssertEqual(mockSut.showViewController?.modalTransitionStyle, .crossDissolve)
}
So, we’re creating a subclass of UIViewController
and overriding a method that is called by the method we are interested in testing. Then we can use assertions to complete our test.
Of course, this could get messy if we had a bunch of test cases which needed to handle overrides. In that case I’d move the MockSut
class out of the function and into the parent class. If I needed it outside of this specific set of tests, I’d move it into its own class so it could be used in multiple places.