In today’s followup to Veronica Ray’s talk on “Real World Mocking in Swift,” she claims that partial mocks are an antipattern. I dispute that claim, especially with Swift partial mocks. I’ve gotten a ton of value out of Swift partial mocks in my mocking experience, and I think they are critical in the path towards 100% code coverage in the framework-laden world in which we live.
Partial Mocks Defined
Veronica defines a partial mock as “any actual object which has been wrapped or changed to provide artificial responses to some methods and not others.” I agree with this definition.
In as simple as an example as I could think of, the proverbial Person
class, consider this code:
class Person {
var firstName: String? = nil
var lastName: String? = nil
func fullName() -> String {
return "\(firstName!) \(lastName!)"
}
func description() -> String {
return "Hi, I'm \(fullName())"
}
}
This class defines a Person
as a thing that can have a firstName
and a lastName
. It also defines som behavior to create a the last name for Person
.
One possible way to create a partial mock is through subclassing:
class MockPerson: Person {
override func fullName() -> String {
return "Andy Obusek"
}
}
MockPerson
extends Person
as a subclass. It preserves all the behavior of the base class, while also wrapping Person
to provide an artificial response as the fullName()
.
Now this example is really trivial, but it’s here to serve as an example to show what a partial mock is, and how you can use subclassing to create one.
Recognizing The Case Against Partial Mocks
Veronica Ray advocates that partial mocks are antipatterns for two reasons:
- They are challenging to setup.
- They decrease the comprehensibility of the test. How do you know what’s real? What’s fake?
I recognize these as valid concerns, especially for the newcomer to automated testing, or even using mock objects. That being said, I also think that the advanced engineer/team who is actually starting to investigate Swift partial mocks is up to the task for being able to comprehend “what’s real?” or “what’s fake?” in the context of the mock object.
And while subclassing has its own set of problems in production code (creates tight coupling between the parent and subclass), I’m totally fine using it in the context of my tests to gain the flexibility of using a partial mock. Swift actually makes this even easier through it’s Nested Types, let me show you. Consider the following test:
func testDescription() {
class MockPerson: Person {
override func fullName() -> String {
return "Andy Obusek"
}
}
let toTest = MockPerson()
let expectedDescription = "Hi I'm Andy Obusek"
XCTAssertEqual(expectedDescription, toTest.description())
}
This test verifies that description()
actually returns the expected string, that comprises of the person’s last name. Using a Swift Nested Type, you can actually define the partial mock WITHIN THE TEST method itself! #mindblown By doing this, in my opinion, it totally eliminates any ambiguity in my mind as to confusion around “what’s real?” or “what’s fake?” with the partial mock. Since partial mock is defined right there, I can see it for myself.
Why You Should Use Swift Partial Mocks
If you aren’t sold on Swift partial mocks yet, let me give them one last pitch. There are often cases when using 3rd party framework classes (Apple’s or otherwise) in which you’ll need to write code for singletons, such as UIApplication.sharedApplication()
or NSNotifcationCenter.defaultCenter()
. These are perfect for partial mocking. For one, these are usually things that you don’t want to trigger their actual behavior from a test. By partially mocking them, you can override the behavior you’re verifying such that the real underlying functionality is disabled. Additionally, you can also actually make assertions that the code under test did the right thing.
For example, consider the following partial mock for NSNotificationCenter
:
class MockNotificationCenter: NSNotificationCenter {
var addObserverCalled = false
override func addObserver(observer: AnyObject, selector aSelector: Selector, name aName: String?, object anObject: AnyObject?) {
addObserverCalled = true
}
}
This partial mock for NSNotificationCenter
allows you to intercept and override the behavior for adding a notification observer. How useful! Now you can swap this implementation in whereever you need to write a test to verify that observers are getting added appropriately. You can even expand upon the overriden addObserver
method to make further checks that the observer is added with the correct parameters.
I hope you see the value in Swift partial mocks. I know I’ve gotten a lot of benefit from them, especially back in the good ole OCMock days.
Happy cleaning!