How to Create Mocks and Stubs in Swift

Without 100% support for a mocking framework like OCMock, I found myself needing to get creative when building mock objects and method stubs in Swift unit tests.  The great thing about testing is that you’re…well… testing things out to see if they’ll work, and I found a solution that I’m pretty happy with for now.  I’m open to better ways, so leave a comment if you’ve had good results using a different design!

The process is essentially this (example to follow):

  1. Ensure that the class that you would like to test is designed so that you can substitute your mock for the real one that’s used in your class’ implementation
  2. Create an XCTestCase  class with a test function in your unit test project
  3. Within the function body create a nested class
  4. Make the nested class inherit from the real object you’re trying to mock / create a method stub for
  5. You can give the nested class a name such as Mock[ObjectName]
  6. Configure the mock object however you need by setting its properties or overriding its function implementations with stubbed implementations – no need to override every function… only the one(s) that your class calls during the test at hand
  7. Instantiate the class you’re testing and pass in an instance of the mock object you just nested in the test function to your class somehow (either through its initializer, by setting a property on the class, or by passing it into the method under test via parameter — however you intended to ‘inject’ the mock from step 1 is what you should do)
  8. XCTAssert…

Let’s see those 8 steps in action for those of us who are more visually inclined.

EDIT:  July 22, 2014 – I’ve added a simple Xcode Project to GitHub for those interested in seeing the setup directly in Xcode at  https://github.com/andrewcbancroft/MocksAndStubs

The scenario that I’d like to use a mock class in is this:  I have a CoreData application and I’d like to be able to mock the NSManagedObjectContext  so that instead of making actual database fetch requests, I can just provide stubs of various sorts with the kinds of responses I’d expect from the real database calls to ensure my class will do the right thing based on predictable results.  To do this I begin at step 1…

1.  Ensure that the class that you would like to test is designed so that you can substitute your mock for the real one that’s used in your class’ implementation

In the example class below, I intend to provide the NSManagedObjectContext  dependency through the class’ initializer which will set a property that is used by my class’ methods later on, but you could easily use some other way of performing “dependency injection”.  The initializer strategy just makes it super clear in my mind what the class’ dependencies are, so that’s what I’m going to do here.  Have a look:

 1import Foundation
 2import CoreData
 3
 4class MyClass {
 5    let context: NSManagedObjectContext
 6    
 7    init(managedObjectContext: NSManagedObjectContext) {
 8        self.context = managedObjectContext
 9    }
10}

Now, let’s say that my example class has a member function called databaseHasRecordsForSomeEntity  that returns a Bool  value of true if the resulting array of a fetch request contains objects, and a Bool  value of false if the result array of a fetch request is empty.  The completed class looks like this:

 1import Foundation
 2import CoreData
 3
 4class MyClass {
 5    let context: NSManagedObjectContext
 6    
 7    init(managedObjectContext: NSManagedObjectContext) {
 8        self.context = managedObjectContext
 9    }
10    
11    // If the array returned from executing a fetch request contains objects, return true; if empty, return false
12    func databaseHasRecordsForSomeEntity() -> Bool {
13        let fetchRequest = NSFetchRequest(entityName: "SomeEntity")
14        let fetchRequestResults = self.context.executeFetchRequest(fetchRequest, error: nil) // May want to do something with the error in real life...
15        return (fetchRequestResults?.count > 0)
16    }
17}

I want to test if databaseHasRecordsForSomeEntity  does what I intend it to do. So…

2.  Create an XCTestCase  class with a test function in your unit test project

Just listing this for completeness

Next comes the way to make the mock.  Read steps 3-5 and then look below for a code example of what the skeleton will look like.

3.  Within the function body create a nested class

4.  Make the nested class inherit from the real object you’re trying to mock / create a method stub for

5.  You can give the nested class a name such as Mock[ObjectName]

 1import UIKit
 2import XCTest
 3import CoreData // <-- Make sure to import CoreData or you will get errors when you try to use NSManagedObjectContext
 4
 5class MyClassTests: XCTestCase {
 6
 7    override func setUp() {
 8        super.setUp()
 9        // Put setup code here. This method is called before the invocation of each test method in the class.
10    }
11    
12    override func tearDown() {
13        // Put teardown code here. This method is called after the invocation of each test method in the class.
14        super.tearDown()
15    }
16
17    // Yay for verbose test names!  :]
18    func testDatabaseHasRecordsForSomeEntityReturnsTrueWhenFetchRequestReturnsNonEmptyArray() {
19        class MockNSManagedObjectContext: NSManagedObjectContext {
20            
21        }
22    }
23}

 6.  Configure the mock object however you need by setting its properties or overriding its function implementations with stubbed implementations – no need to override every function… only the one(s) that your class calls during the test at hand

For my example, I’m going to stub out the executeFetchRequest  method so that it returns an array with one object in it.  This is really the part where you have to determine what you’re testing and what you expect the stubbed results to be.  Whatever you decide, the way to stub a method is simply to override it in the mock you’re implementing.  Here’s how I implemented the executeFetchRequest  stub for my example:

1// Yay for verbose test names!  :]
2    func testDatabaseHasRecordsForSomeEntityReturnsTrueWhenFetchRequestReturnsNonEmptyArray() {
3        class MockNSManagedObjectContext: NSManagedObjectContext {
4            override func executeFetchRequest(request: NSFetchRequest, error: NSErrorPointer) -> [AnyObject]? {
5                return ["object 1"]
6            }
7        }
8    }

We’re ready to perform the test and assert the results.  Read steps 7-8 and take a look at the code example below step 8:

7.  Instantiate the class you’re testing and pass in an instance of the mock object you just nested in the test function to your class somehow (either through its initializer, by setting a property on the class, or by passing it into the method under test via parameter — however you intended to ‘inject’ the mock from step 1 is what you should do)

8.  XCTAssert…

From step 1, I intended to pass an NSManagedObjectContext instance to the initializer of MyClass, so that’s what I’ll do in my test.  I’ll then perform the XCTAssert on the return value of my method under test:

 

 1// Yay for verbose test names!  :]
 2    func testDatabaseHasRecordsForSomeEntityReturnsTrueWhenFetchRequestReturnsNonEmptyArray() {
 3        class MockNSManagedObjectContext: NSManagedObjectContext {
 4            override func executeFetchRequest(request: NSFetchRequest, error: NSErrorPointer) -> [AnyObject]? {
 5                return ["object 1"]
 6            }
 7        }
 8        
 9        // Instantiate mock
10        let mockContext = MockNSManagedObjectContext()
11        
12        // Instantiate class under test and pass it the mockContext object
13        let myClassInstance = MyClass(managedObjectContext: mockContext)
14        
15        // Call the method under test and store its return value for XCTAssert
16        let returnValue = myClassInstance.databaseHasRecordsForSomeEntity()
17        
18        XCTAssertTrue(returnValue == true, "The return value should be been true")
19    }

Running the tests at this point should produce a passing test using the mock object in place of a real NSManagedObjectContext that calls a database!

Now, if I wanted to test the “false” branch of my class’ method, I could simply create another test method following the same steps, only this time, I’d provide a new implementation for the overridden executeFetchRequest  method that’s appropriate:

 1func testDatabaseHasRecordsForSomeEntityReturnsFalseWhenFetchRequestReturnsEMPTYArray() {
 2        class MockNSManagedObjectContext: NSManagedObjectContext {
 3            override func executeFetchRequest(request: NSFetchRequest, error: NSErrorPointer) -> [AnyObject]? {
 4                return [] // Provided a different stub implementation to test the "false" branch of my method under test
 5            }
 6        }
 7        
 8        // Instantiate mock
 9        let mockContext = MockNSManagedObjectContext()
10        
11        // Instantiate class under test
12        let myClassInstance = MyClass(managedObjectContext: mockContext)
13        
14        // Call the method under test and store its return value for XCTAssert
15        let returnValue = myClassInstance.databaseHasRecordsForSomeEntity()
16        
17        XCTAssertTrue(returnValue == false, "The return value should be been false")
18    }

And that’s a wrap – happy mocking and stubbing in Swift!

EDIT:  July 22, 2014 – I’ve added a simple Xcode Project to GitHub for those interested in seeing the setup directly in Xcode at  https://github.com/andrewcbancroft/MocksAndStubs

comments powered by Disqus