MockItYourself
is an attempt to create a mocking framework for Swift. Currently, you can't dynamically create classes in Swift and it is therefore impossible to create a powerful mocking framework like OCMock. MockItYourself
is an attempt to reduce the boilerplate needed to manually create mocks.
MockItYourself
is heavily inspired by SwiftMock and could in some ways be considered a fork of that project.
MockItYourself is available through CocoaPods. To install it, simply add the following line to your Podfile:
source 'https://github.com/CocoaPods/Specs.git'
use_frameworks!
target 'MyApp' do
end
target 'MyApp_Tests', :exclusive => true do
pod 'MockItYourself', '1.0.0'
end
The standard way of mocking out dependencies in Swift is to manually create your own mocks:
class Dependency {
func slowAndExpensiveMethod(arg1: String, arg2: Int) -> Double {
print("Do something really slow and expensive with arguments")
return 40.0
}
}
class MockDependency: Dependency {
var didCallSlowAndExpensiveMethod: Bool = false
var lastCalledArg1: String?
var lastCalledArg2: Int?
override func slowAndExpensiveMethod(arg1: String, arg2: Int) -> Double {
didCallSlowAndExpensiveMethod = true
lastCalledArg1 = arg1
lastCalledArg2 = arg2
return 50.0
}
}
struct ObjectUnderTests {
let dependency: Dependency
func method() -> Double {
let result = dependency.slowAndExpensiveMethod("A", arg2: 1)
return result / 2
}
}
class MockExampleTests: XCTestCase {
var mockDependency: MockDependency!
var objectUnderTest: ObjectUnderTests!
override func setUp() {
mockDependency = MockDependency()
objectUnderTest = ObjectUnderTests(dependency: mockDependency)
}
func test_did_call_method_on_dependency_with_correct_arguments() {
let _ = objectUnderTest.method()
XCTAssert(mockDependency.didCallSlowAndExpensiveMethod)
XCTAssertEqual(mockDependency.lastCalledArg1, "A")
XCTAssertEqual(mockDependency.lastCalledArg2, 1)
}
func test_method_returns_correct_result_given_dependency_returns_50() {
let result = objectUnderTest.method()
XCTAssertEqual(result, 25.0)
}
}
This quickly becomes tedious and MockItYourself
is an attempt to reduce the boilerplate.
class Dependency {
func slowAndExpensiveMethod(arg1: String, arg2: Int) -> Double {
print("Do something really slow and expensive with arguments")
return 40.0
}
}
class MockDependency: Dependency, MockItYourself {
let callHandler = MockCallHandler()
override func slowAndExpensiveMethod(arg1: String, arg2: Int) -> Double {
return callHandler.registerCall(args: Args2(arg(arg1), arg(arg2)), defaultReturnValue: 50) as! Double
}
}
struct ObjectUnderTests {
let dependency: Dependency
func method() -> Double {
let result = dependency.slowAndExpensiveMethod("A", arg2: 1)
return result / 2
}
}
class MockExampleTests: XCTestCase {
var mockDependency: MockDependency!
var objectUnderTest: ObjectUnderTests!
override func setUp() {
mockDependency = MockDependency()
objectUnderTest = ObjectUnderTests(dependency: mockDependency)
}
func test_did_call_method_on_dependency_with_correct_arguments() {
let _ = objectUnderTest.method()
verify(mockDependency) { self.mockDependency.slowAndExpensiveMethod("A", arg2: 1) }
}
func test_method_returns_correct_result_given_dependency_returns_50() {
stub(mockDependency, andReturnValue: 50) { mockDependency.slowAndExpensiveMethod("A", arg2: 1) }
let result = objectUnderTest.method()
XCTAssertEqual(result, 25.0)
}
}
MockItYourself
helps reduce boilerplate when manually creating mocks. Let's imagine that we have the following class:
class Dependency {
func slowAndExpensiveMethod(arg1: String, arg2: Int) -> Double {
return 40.0
}
}
Creating the mock is simple:
- Create a subclass of
Dependency
. - Implement the
MockItYourself
protocol by adding acallHandler
property. - Register the methods that you want to mock or stub.
import MockItYourself
class MockDependency: Dependency, MockItYourself {
let callHandler = MockCallHandler()
override func slowAndExpensiveMethod(arg1: String, arg2: Int) -> Double {
return callHandler.registerCall(args: Args2(arg(arg1), arg(arg2)), defaultReturnValue: 50)
}
}
MockItYourself
allows you to verify if methods on your mocks are called with the
To verify a method was called:
func test_verify() {
mock.slowAndExpensiveMethod("A", arg2: 1)
verify(mock) { self.mock.slowAndExpensiveMethod(any(), arg2: any()) }
}
To verify a method was called n number of times:
func test_verify_number_of_calls() {
mock.slowAndExpensiveMethod("A", arg2: 1)
mock.slowAndExpensiveMethod("A", arg2: 1)
verify(mock, expectedCallCount: 2) { self.mock.slowAndExpensiveMethod(any(), arg2: any()) }
}
To verify a method was called with the specific arguments:
func test_verify_with_arguments() {
mock.slowAndExpensiveMethod("A", arg2: 1)
verify(mock, checkArguments: true) { self.mock.slowAndExpensiveMethod("A", arg2: 1) }
}
To verify a method was not called:
func test_reject() {
reject(mock) { self.mock.slowAndExpensiveMethod("A", arg2: 1) }
}
To stub out the return value of a method or property:
func test_stubbing() {
stub(mock, andReturnValue: 30) { self.mock.slowAndExpensiveMethod("A", arg2: 1) }
let returnValue = mock.slowAndExpensiveMethod("A", arg2: 1)
XCTAssertEqual(returnValue, 30)
}
- Alexey Verein, alexey@plainvanillagames.com
- Jóhann Þ. Bergþórsson, johann@plainvanillagames.com
- Magnus Ó. Magnússon, magnus@plainvanillagames.com
- Alexander A. Helgason, alliannas@plainvanillagames.com
MockItYourself is available under the MIT license. See the LICENSE file for more info.