SwiftMocks
A Swift library for easily mocking out objects.
Using Swift Macros, SwiftMocks allows you to easily create mock objects for testing. It’s as simple as adding the @Mock
attribute to your class, and then calling the generated mock
property.
The mock
property keeps track of all calls to the object, and allows you to easily verify that the object was called with the correct parameters and to effortlessly stub out return values.
Usage
Mocking
To mock an object, simply add the @Mock
attribute to your class. This will generate a mock
property on your class that you can use to verify calls to the object.
// This is the class we want to mock
class MyObject {
func doSomething() {}
}
@Mock
class MyObject {
func doSomething() {
mock.doSomething()
}
let mock = MyObjectMock() // generated
class MyObjectMock { // generated
var doSomethingCalls: Mock<Void, Void> = .init() // generated
func doSomething() { // generated
doSomethingCalls.record(()) // generated
} // generated
} // generated
}
Stubbing Return Values
To stub out return values, simply call the mockCall
method on the mock
property.
// This is the class we want to mock
class MyObject {
func doSomething() -> Int {
mock.doSomething()
}
}
@Mock
class MyObject {
func doSomething() -> Int {
mock.doSomething()
}
}
let myObject = MyObject()
myObject.mock.doSomethingCalls.mockCall { 1 }
print(myObject.doSomething()) // this prints 1
Verifying Calls
To verify that a method was called, simply call the verify
method on the mock
property.
// This is the class we want to mock
class MyObject {
func doSomething() {}
}
@Mock
class MyObject {
func doSomething(param: Int) {
mock.doSomething(param: param)
}
}
let myObject = MyObject()
myObject.doSomething(param: 999)
XCTAssertEqual(myObject.mock.doSomethingCalls.callCount, 1)
XCTAssertEqual(myObject.mock.doSomethingCalls.lastCall?.0, 999)
Taking advantage in tests
In your production code, you’d define your protocols and classes as normal. In your tests, you’d add the @Mock
to the Mock class, and then use the mock
property to verify calls.
// production code
protocol Service {
func doSomething() -> String
}
class RealService: Service {
func doSomething() -> String {
return "yes!"// production code
}
}
let service: Service = RealService()
let useCase = UseCase(service: service)
--------------------
// test code
@Mock
class MockService: Service {
func doSomething() -> String {
mock.doSomething()
}
}
func test() {
// Given
let mockService = MockService()
let sut = UseCase(service: mockService)
mockService.mock.doSomethingCalls.mockCall { "no!" }
// When
let result = sut.doSomething()
// Then
XCTAssertEqual(result, "no!")
XCTAssertEqual(mockService.mock.doSomethingCalls.callCount, 1)
}