Repository files navigation

Mokka

Bitrise build statusCode coverageCocoaPodsSwift VersionLicenseTwitter

A collection of helpers to make it easier to write testing mocks in Swift.

Motivation

Due to Swift's very static nature, mocking and stubbing is much harder to do than in other languages. There are no dynamic mocking framework like OCMock or Mockito. The usual approach is to just write your mock objects manually, like so:

protocolFoo{func doSomething(arg:String)->Int}classFooMock:Foo{vardoSomethingHasBeenCalled=falsevardoSomethingArgument= String?vardoSomethingReturnValue:Int!func doSomething(arg:String)->Int{
doSomethingHasBeenCalled =true
doSomethingArgument = arg
return doSomethingReturnValue
}}

This is a lot of boilerplate (and this is just a simple example, which doesn't allow for conditional stubbing, for example). This is where Mokka comes in.

Overview

Mokka provides a testing helper class called FunctionMock<Args> (and a variant for returning functions called ReturningFunctionMock<Args, ReturnValue>) that takes care of:

  • Recording function/method calls for verification (Has the method been called?, How often has the method been called?)
  • Capturing the arguments for verification (With which arguments has the method been called?)
  • Stubbing return values (also conditionally) (This method should return 42 if called with argument "x")

With these helpers it gets much more convenient to define your mock objects:

classFooMock:Foo{letdoSomethingFunc=ReturningFunctionMock<String,Int>()func doSomething(arg:String)->Int{return doSomethingFunc.recordCallAndReturn(arg)}}

You can now use the function mock object for verification:

func testSomething(){
// ...
XCTAssertEqual(myMock.doSomethingFunc.callCount,2)XCTAssertEqual(myMock.doSomethingFunc.argument,"lorem ipsum")
// ...
}

and for faking the return value:

func testSomething(){
// static return value
myMock.doSomethingFunc.returns(100)
// dynamic return value
myMock.doSomethingFunc.returns{ $0 +200} // $0 is the argument(s) passed to the method
// conditional return value
myMock.doSomethingFunc.returns(123, when:{ $0 =="foo"})
myMock.doSomethingFunc.returns(456, when:{ $0 =="bar"})
myMock.doSomethingFunc.returns(789)}

Requirements

  • Xcode 10.2
  • Swift 5.0

Installation

CocoaPods

To install Mokka via CocoaPods, just add the Mokka pod for your test target to the Podfile:

pod'Mokka'

Swift Package Manager

You can install Mokka using Swift Package Manager. Just add this repository as a dependency to your Package.swift file (and don't forget to also add "Mokka" as a dependency in your test target):

dependencies:[.package(url:"https://github.com/danielr/Mokka", from:"1.0.0")
// ...
]

Carthage

To install Mokka with Carthage, add this to your Cartfile:

github "danielr/Mokka"

Then drag the built Mokka.framework to your project and make sure it's added to the unit test target, not the main app target. If you see issues errors like "The bundle xxx couldn’t be loaded because it is damaged or missing necessary resources.", then you might need to tweak your test target's Runtime Search Paths.

Documentation

Types of mocks

There are currently three types of mock helpers available:

  • FunctionMock: Allows to record the calls to a function (the call count and the arguments), as well as to optionally stub the function's behavior. Use this for functions that have a Void return value.
  • ReturningFunctionMock: Provides the same functionality as FunctionMock, but adds the ability to fake the returned value. Use this for functions that have a non-void return value.
  • PropertyMock: Allows to provide fake values for a property and record whether a property has been read or set.

How to implement your mocks

The first step is to implement your mocks using Mokka's helpers. The general approach is the same for all types of mocks: You declare a property for the mock and use that mock object inside your function implementations to record the calls to that function.

For the examples below, let's assume we want to mock the following protocol:

protocolEngine{func turnOn()func turnOff()varisOn:Bool{get}func setSpeed(to value:Float) // kilometers per hour
func setSpeed(to value:Float, in unit:UnitSpeed)func currentSpeed(in unit:UnitSpeed)->Double}

Functions without return value

For functions that don't return a value, use FunctionMock<Args>. This class has one generic parameter which defines the type(s) of the argument(s).

For functions with no arguments, that should be Void:

classEngineMock:Engine{letturnOnFunc=FunctionMock<Void>(name:"turnOn()")func turnOn(){
turnOnFunc.recordCall()}
// ...
}

Note: The name parameter in the mock initializers is optional. It is purely informational and might be useful for error messages (e.g. better assertion error messages). It is good practice to provide the names in the standard Swift #selector syntax.

For functions with a single argument, just use that argument's type:

classEngineMock:Engine{letsetSpeedFunc=FunctionMock<Float>(name:"setSpeed(to:)")func setSpeed(to value:Float){
setSpeedFunc.recordCall(value)}
// ...
}

For functions with more than one argument, you need to use a tuple to represent the arguments (because Swift does not (yet?) support variadic generic parameters). Although you don't have to, it is a good practice to name the tuple elements, which makes it much clearer when referring to them in your testing code.

classEngineMock:Engine{letsetSpeedInUnitFunc=FunctionMock<(value:Float, unit:UnitSpeed)>(name:"setSpeed(to:unit:)")func setSpeed(to value:Float, in unit:UnitSpeed){
setSpeedInUnitFunc.recordCall((value: value, unit: unit))}
// ...
}

Functions with return value

For functions with a return value, use ReturningFunctionMock<Args, ReturnValue>. This works very much the same way as FunctionMock, but adds a second generic parameter for the return value type. It also provides a recordCallAndReturn() method, instead of the recordCall() method.

classEngineMock:Engine{letcurrentSpeedFunc=ReturningFunctionMock<UnitSpeed,Double>(name:"currentSpeed(in:)")func currentSpeed(in unit:UnitSpeed)->Double{return currentSpeedFunc.recordCallAndReturn(unit)}
// ...
}

For the arguments of returning methods, the same rules apply as for non-returning functions (see above). For example:

  • A mock for a function that has no arguments and returns a Bool would be declared as ReturningFunctionMock<Void, Bool>
  • A mock for a function that has two arguments of type Int and String? and returns a Double would be declared as ReturningFunctionMock<(arg1: Int, arg2: String?), Double>

Properties

In many cases it is enough to just implement property requirements of the mocked protocol by declaring a stored property with a default value in your mock. However, when you want to be able to explicitly track whether a property has been read or written, Mokka's PropertyMock can be helpful. It is generic over the type of the property and its use in the mock implementation is quite self-explanatory: Instead of using a stored property, declare a computed property and delegate the getter and setter (if it's settable property) to the get() and set(_:) methods of the PropertyMock object:

classEngineMock:Engine{letisOnProperty=PropertyMock<Bool>(name:"isOn")varisOn:Bool{get{return isOnProperty.get()}set{ isOnProperty.set(newValue)}}
// ...
}

Note that you don't need to provide a default value for the property (the get() method fails with a preconditionFailure if there is no value).

Call count verification

A common use case for mocks is to verify if a method has been called, and sometimes specifically how often it has been called. For that, both FunctionMock and ReturningFunctionMock provide some properties:

  • called: Bool Returns whether the method has been called (once or more).
  • calledOnce: Bool Returns whether the method has been called exactly once.
  • callCount: Int The number of times the method has been called.
XCTAssertTrue(engineMock.setSpeedFunc.called)XCTAssertTrue(engineMock.setSpeedFunc.calledOnce)XCTAssertEqual(engineMock.setSpeedFunc.callCount,3)

Argument verification

In addition to verifying if a function has been called, you often also want to check the argument(s) with which the function has been called. You can do that via the arguments property:

XCTAssertEqual(engineMock.setSpeedInUnitFunc.arguments.value,100.0)XCTAssertEqual(engineMock.setSpeedInUnitFunc.arguments.unit,.kilometersPerHour)

(This requires that you follow the recommended practice of naming the tuple members, see above. If you don't, you have to access the arguments by their index, e.g. arguments.0.)

For single-argument functions (where there's no arguments tuple) you can also use the argument property, which looks a bit nicer:

XCTAssertEqual(engineMock.setSpeedFunc.argument,100.0)

Stubbing

Sometimes it's necessary to stub the behavior of a function, for example to introduce some important side-effects. One common example for this is calling a delegate method. You can do that by providing a closure that will be executed when the function is called. The closure will be provided with the function arguments:

letdelegate=FooDelegateMock()
someMock.myFunction.stub{ arg in
delegate.somethingHappened(with: arg)}

Faking the return value

For returning functions it's crucial to be able to fake the return value. Mokka provides 3 ways of doing that: Static return values, dynamic return values and conditional return values. Let's have a look at each of those.

Providing a static return value

For most cases it's sufficient to provide a simple static value that should be returned by the mock implementation:

engineMock.currentSpeedFunc.returns(100.0)

Providing a return value dynamically

Sometimes it's convenient to provide a return value that is dynamically generated, often depending on the function's arguments. You can do that by providing a closure:

engineMock.currentSpeedFunc.returns{ unit in
// always return 100 km/h, converted to the requested target unit
letkmhValue=Measurement(value:100, unit:UnitSpeed.kilometersPerHour)return kmhValue.converted(to: unit).value
}

Providing return values conditionally

Both, static and dynamic return values can also be provided conditionally:

engineMock.currentSpeedFunc.returns(100.00, when:{ $0 ==.kilometersPerHour })
engineMock.currentSpeedFunc.returns(62.137, when:{ $0 ==.milesPerHour })
engineMock.currentSpeedFunc.returns(0) // otherwise

Mocking properties

This is how you use properties that are backed by PropertyMock in your testing code:

someMock.fooProperty.value =10	// use value to access the underlying property value
// do something
XCTAssertTrue(someMock.fooProperty.hasBeenRead)XCTAssertFalse(someMock.fooProperty.hasBeenSet

Example

You can find a simple example project in MokkaExample.

It includes

  • a subject under test (Car)
  • two mocked protocols (Engine and Battery)

It is a minimal example, but it should be enough to get you started with the concepts of Mokka.

Author

Mokka has been created and is maintained by Daniel Rinser, @danielrinser.

License

Mokka is available under the MIT License.

About

A collection of helpers to make it easier to write testing mocks in Swift.

Topics

Resources

Stars

6 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Repository files navigation

Mokka

Bitrise build statusCode coverageCocoaPodsSwift VersionLicenseTwitter

A collection of helpers to make it easier to write testing mocks in Swift.

Motivation

Due to Swift's very static nature, mocking and stubbing is much harder to do than in other languages. There are no dynamic mocking framework like OCMock or Mockito. The usual approach is to just write your mock objects manually, like so:

protocolFoo{func doSomething(arg:String)->Int}classFooMock:Foo{vardoSomethingHasBeenCalled=falsevardoSomethingArgument= String?vardoSomethingReturnValue:Int!func doSomething(arg:String)->Int{
doSomethingHasBeenCalled =true
doSomethingArgument = arg
return doSomethingReturnValue
}}

This is a lot of boilerplate (and this is just a simple example, which doesn't allow for conditional stubbing, for example). This is where Mokka comes in.

Overview

Mokka provides a testing helper class called FunctionMock<Args> (and a variant for returning functions called ReturningFunctionMock<Args, ReturnValue>) that takes care of:

  • Recording function/method calls for verification (Has the method been called?, How often has the method been called?)
  • Capturing the arguments for verification (With which arguments has the method been called?)
  • Stubbing return values (also conditionally) (This method should return 42 if called with argument "x")

With these helpers it gets much more convenient to define your mock objects:

classFooMock:Foo{letdoSomethingFunc=ReturningFunctionMock<String,Int>()func doSomething(arg:String)->Int{return doSomethingFunc.recordCallAndReturn(arg)}}

You can now use the function mock object for verification:

func testSomething(){
// ...
XCTAssertEqual(myMock.doSomethingFunc.callCount,2)XCTAssertEqual(myMock.doSomethingFunc.argument,"lorem ipsum")
// ...
}

and for faking the return value:

func testSomething(){
// static return value
myMock.doSomethingFunc.returns(100)
// dynamic return value
myMock.doSomethingFunc.returns{ $0 +200} // $0 is the argument(s) passed to the method
// conditional return value
myMock.doSomethingFunc.returns(123, when:{ $0 =="foo"})
myMock.doSomethingFunc.returns(456, when:{ $0 =="bar"})
myMock.doSomethingFunc.returns(789)}

Requirements

  • Xcode 10.2
  • Swift 5.0

Installation

CocoaPods

To install Mokka via CocoaPods, just add the Mokka pod for your test target to the Podfile:

pod'Mokka'

Swift Package Manager

You can install Mokka using Swift Package Manager. Just add this repository as a dependency to your Package.swift file (and don't forget to also add "Mokka" as a dependency in your test target):

dependencies:[.package(url:"https://github.com/danielr/Mokka", from:"1.0.0")
// ...
]

Carthage

To install Mokka with Carthage, add this to your Cartfile:

github "danielr/Mokka"

Then drag the built Mokka.framework to your project and make sure it's added to the unit test target, not the main app target. If you see issues errors like "The bundle xxx couldn’t be loaded because it is damaged or missing necessary resources.", then you might need to tweak your test target's Runtime Search Paths.

Documentation

Types of mocks

There are currently three types of mock helpers available:

  • FunctionMock: Allows to record the calls to a function (the call count and the arguments), as well as to optionally stub the function's behavior. Use this for functions that have a Void return value.
  • ReturningFunctionMock: Provides the same functionality as FunctionMock, but adds the ability to fake the returned value. Use this for functions that have a non-void return value.
  • PropertyMock: Allows to provide fake values for a property and record whether a property has been read or set.

How to implement your mocks

The first step is to implement your mocks using Mokka's helpers. The general approach is the same for all types of mocks: You declare a property for the mock and use that mock object inside your function implementations to record the calls to that function.

For the examples below, let's assume we want to mock the following protocol:

protocolEngine{func turnOn()func turnOff()varisOn:Bool{get}func setSpeed(to value:Float) // kilometers per hour
func setSpeed(to value:Float, in unit:UnitSpeed)func currentSpeed(in unit:UnitSpeed)->Double}

Functions without return value

For functions that don't return a value, use FunctionMock<Args>. This class has one generic parameter which defines the type(s) of the argument(s).

For functions with no arguments, that should be Void:

classEngineMock:Engine{letturnOnFunc=FunctionMock<Void>(name:"turnOn()")func turnOn(){
turnOnFunc.recordCall()}
// ...
}

Note: The name parameter in the mock initializers is optional. It is purely informational and might be useful for error messages (e.g. better assertion error messages). It is good practice to provide the names in the standard Swift #selector syntax.

For functions with a single argument, just use that argument's type:

classEngineMock:Engine{letsetSpeedFunc=FunctionMock<Float>(name:"setSpeed(to:)")func setSpeed(to value:Float){
setSpeedFunc.recordCall(value)}
// ...
}

For functions with more than one argument, you need to use a tuple to represent the arguments (because Swift does not (yet?) support variadic generic parameters). Although you don't have to, it is a good practice to name the tuple elements, which makes it much clearer when referring to them in your testing code.

classEngineMock:Engine{letsetSpeedInUnitFunc=FunctionMock<(value:Float, unit:UnitSpeed)>(name:"setSpeed(to:unit:)")func setSpeed(to value:Float, in unit:UnitSpeed){
setSpeedInUnitFunc.recordCall((value: value, unit: unit))}
// ...
}

Functions with return value

For functions with a return value, use ReturningFunctionMock<Args, ReturnValue>. This works very much the same way as FunctionMock, but adds a second generic parameter for the return value type. It also provides a recordCallAndReturn() method, instead of the recordCall() method.

classEngineMock:Engine{letcurrentSpeedFunc=ReturningFunctionMock<UnitSpeed,Double>(name:"currentSpeed(in:)")func currentSpeed(in unit:UnitSpeed)->Double{return currentSpeedFunc.recordCallAndReturn(unit)}
// ...
}

For the arguments of returning methods, the same rules apply as for non-returning functions (see above). For example:

  • A mock for a function that has no arguments and returns a Bool would be declared as ReturningFunctionMock<Void, Bool>
  • A mock for a function that has two arguments of type Int and String? and returns a Double would be declared as ReturningFunctionMock<(arg1: Int, arg2: String?), Double>

Properties

In many cases it is enough to just implement property requirements of the mocked protocol by declaring a stored property with a default value in your mock. However, when you want to be able to explicitly track whether a property has been read or written, Mokka's PropertyMock can be helpful. It is generic over the type of the property and its use in the mock implementation is quite self-explanatory: Instead of using a stored property, declare a computed property and delegate the getter and setter (if it's settable property) to the get() and set(_:) methods of the PropertyMock object:

classEngineMock:Engine{letisOnProperty=PropertyMock<Bool>(name:"isOn")varisOn:Bool{get{return isOnProperty.get()}set{ isOnProperty.set(newValue)}}
// ...
}

Note that you don't need to provide a default value for the property (the get() method fails with a preconditionFailure if there is no value).

Call count verification

A common use case for mocks is to verify if a method has been called, and sometimes specifically how often it has been called. For that, both FunctionMock and ReturningFunctionMock provide some properties:

  • called: Bool Returns whether the method has been called (once or more).
  • calledOnce: Bool Returns whether the method has been called exactly once.
  • callCount: Int The number of times the method has been called.
XCTAssertTrue(engineMock.setSpeedFunc.called)XCTAssertTrue(engineMock.setSpeedFunc.calledOnce)XCTAssertEqual(engineMock.setSpeedFunc.callCount,3)

Argument verification

In addition to verifying if a function has been called, you often also want to check the argument(s) with which the function has been called. You can do that via the arguments property:

XCTAssertEqual(engineMock.setSpeedInUnitFunc.arguments.value,100.0)XCTAssertEqual(engineMock.setSpeedInUnitFunc.arguments.unit,.kilometersPerHour)

(This requires that you follow the recommended practice of naming the tuple members, see above. If you don't, you have to access the arguments by their index, e.g. arguments.0.)

For single-argument functions (where there's no arguments tuple) you can also use the argument property, which looks a bit nicer:

XCTAssertEqual(engineMock.setSpeedFunc.argument,100.0)

Stubbing

Sometimes it's necessary to stub the behavior of a function, for example to introduce some important side-effects. One common example for this is calling a delegate method. You can do that by providing a closure that will be executed when the function is called. The closure will be provided with the function arguments:

letdelegate=FooDelegateMock()
someMock.myFunction.stub{ arg in
delegate.somethingHappened(with: arg)}

Faking the return value

For returning functions it's crucial to be able to fake the return value. Mokka provides 3 ways of doing that: Static return values, dynamic return values and conditional return values. Let's have a look at each of those.

Providing a static return value

For most cases it's sufficient to provide a simple static value that should be returned by the mock implementation:

engineMock.currentSpeedFunc.returns(100.0)

Providing a return value dynamically

Sometimes it's convenient to provide a return value that is dynamically generated, often depending on the function's arguments. You can do that by providing a closure:

engineMock.currentSpeedFunc.returns{ unit in
// always return 100 km/h, converted to the requested target unit
letkmhValue=Measurement(value:100, unit:UnitSpeed.kilometersPerHour)return kmhValue.converted(to: unit).value
}

Providing return values conditionally

Both, static and dynamic return values can also be provided conditionally:

engineMock.currentSpeedFunc.returns(100.00, when:{ $0 ==.kilometersPerHour })
engineMock.currentSpeedFunc.returns(62.137, when:{ $0 ==.milesPerHour })
engineMock.currentSpeedFunc.returns(0) // otherwise

Mocking properties

This is how you use properties that are backed by PropertyMock in your testing code:

someMock.fooProperty.value =10	// use value to access the underlying property value
// do something
XCTAssertTrue(someMock.fooProperty.hasBeenRead)XCTAssertFalse(someMock.fooProperty.hasBeenSet

Example

You can find a simple example project in MokkaExample.

It includes

  • a subject under test (Car)
  • two mocked protocols (Engine and Battery)

It is a minimal example, but it should be enough to get you started with the concepts of Mokka.

Author

Mokka has been created and is maintained by Daniel Rinser, @danielrinser.

License

Mokka is available under the MIT License.

About

A collection of helpers to make it easier to write testing mocks in Swift.

Topics

Resources

Stars

6 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Mokka

Bitrise build statusCode coverageCocoaPodsSwift VersionLicenseTwitter

A collection of helpers to make it easier to write testing mocks in Swift.

Motivation

Due to Swift's very static nature, mocking and stubbing is much harder to do than in other languages. There are no dynamic mocking framework like OCMock or Mockito. The usual approach is to just write your mock objects manually, like so:

protocolFoo{func doSomething(arg:String)->Int}classFooMock:Foo{vardoSomethingHasBeenCalled=falsevardoSomethingArgument= String?vardoSomethingReturnValue:Int!func doSomething(arg:String)->Int{
doSomethingHasBeenCalled =true
doSomethingArgument = arg
return doSomethingReturnValue
}}

This is a lot of boilerplate (and this is just a simple example, which doesn't allow for conditional stubbing, for example). This is where Mokka comes in.

Overview

Mokka provides a testing helper class called FunctionMock<Args> (and a variant for returning functions called ReturningFunctionMock<Args, ReturnValue>) that takes care of:

  • Recording function/method calls for verification (Has the method been called?, How often has the method been called?)
  • Capturing the arguments for verification (With which arguments has the method been called?)
  • Stubbing return values (also conditionally) (This method should return 42 if called with argument "x")

With these helpers it gets much more convenient to define your mock objects:

classFooMock:Foo{letdoSomethingFunc=ReturningFunctionMock<String,Int>()func doSomething(arg:String)->Int{return doSomethingFunc.recordCallAndReturn(arg)}}

You can now use the function mock object for verification:

func testSomething(){
// ...
XCTAssertEqual(myMock.doSomethingFunc.callCount,2)XCTAssertEqual(myMock.doSomethingFunc.argument,"lorem ipsum")
// ...
}

and for faking the return value:

func testSomething(){
// static return value
myMock.doSomethingFunc.returns(100)
// dynamic return value
myMock.doSomethingFunc.returns{ $0 +200} // $0 is the argument(s) passed to the method
// conditional return value
myMock.doSomethingFunc.returns(123, when:{ $0 =="foo"})
myMock.doSomethingFunc.returns(456, when:{ $0 =="bar"})
myMock.doSomethingFunc.returns(789)}

Requirements

  • Xcode 10.2
  • Swift 5.0

Installation

CocoaPods

To install Mokka via CocoaPods, just add the Mokka pod for your test target to the Podfile:

pod'Mokka'

Swift Package Manager

You can install Mokka using Swift Package Manager. Just add this repository as a dependency to your Package.swift file (and don't forget to also add "Mokka" as a dependency in your test target):

dependencies:[.package(url:"https://github.com/danielr/Mokka", from:"1.0.0")
// ...
]

Carthage

To install Mokka with Carthage, add this to your Cartfile:

github "danielr/Mokka"

Then drag the built Mokka.framework to your project and make sure it's added to the unit test target, not the main app target. If you see issues errors like "The bundle xxx couldn’t be loaded because it is damaged or missing necessary resources.", then you might need to tweak your test target's Runtime Search Paths.

Documentation

Types of mocks

There are currently three types of mock helpers available:

  • FunctionMock: Allows to record the calls to a function (the call count and the arguments), as well as to optionally stub the function's behavior. Use this for functions that have a Void return value.
  • ReturningFunctionMock: Provides the same functionality as FunctionMock, but adds the ability to fake the returned value. Use this for functions that have a non-void return value.
  • PropertyMock: Allows to provide fake values for a property and record whether a property has been read or set.

How to implement your mocks

The first step is to implement your mocks using Mokka's helpers. The general approach is the same for all types of mocks: You declare a property for the mock and use that mock object inside your function implementations to record the calls to that function.

For the examples below, let's assume we want to mock the following protocol:

protocolEngine{func turnOn()func turnOff()varisOn:Bool{get}func setSpeed(to value:Float) // kilometers per hour
func setSpeed(to value:Float, in unit:UnitSpeed)func currentSpeed(in unit:UnitSpeed)->Double}

Functions without return value

For functions that don't return a value, use FunctionMock<Args>. This class has one generic parameter which defines the type(s) of the argument(s).

For functions with no arguments, that should be Void:

classEngineMock:Engine{letturnOnFunc=FunctionMock<Void>(name:"turnOn()")func turnOn(){
turnOnFunc.recordCall()}
// ...
}

Note: The name parameter in the mock initializers is optional. It is purely informational and might be useful for error messages (e.g. better assertion error messages). It is good practice to provide the names in the standard Swift #selector syntax.

For functions with a single argument, just use that argument's type:

classEngineMock:Engine{letsetSpeedFunc=FunctionMock<Float>(name:"setSpeed(to:)")func setSpeed(to value:Float){
setSpeedFunc.recordCall(value)}
// ...
}

For functions with more than one argument, you need to use a tuple to represent the arguments (because Swift does not (yet?) support variadic generic parameters). Although you don't have to, it is a good practice to name the tuple elements, which makes it much clearer when referring to them in your testing code.

classEngineMock:Engine{letsetSpeedInUnitFunc=FunctionMock<(value:Float, unit:UnitSpeed)>(name:"setSpeed(to:unit:)")func setSpeed(to value:Float, in unit:UnitSpeed){
setSpeedInUnitFunc.recordCall((value: value, unit: unit))}
// ...
}

Functions with return value

For functions with a return value, use ReturningFunctionMock<Args, ReturnValue>. This works very much the same way as FunctionMock, but adds a second generic parameter for the return value type. It also provides a recordCallAndReturn() method, instead of the recordCall() method.

classEngineMock:Engine{letcurrentSpeedFunc=ReturningFunctionMock<UnitSpeed,Double>(name:"currentSpeed(in:)")func currentSpeed(in unit:UnitSpeed)->Double{return currentSpeedFunc.recordCallAndReturn(unit)}
// ...
}

For the arguments of returning methods, the same rules apply as for non-returning functions (see above). For example:

  • A mock for a function that has no arguments and returns a Bool would be declared as ReturningFunctionMock<Void, Bool>
  • A mock for a function that has two arguments of type Int and String? and returns a Double would be declared as ReturningFunctionMock<(arg1: Int, arg2: String?), Double>

Properties

In many cases it is enough to just implement property requirements of the mocked protocol by declaring a stored property with a default value in your mock. However, when you want to be able to explicitly track whether a property has been read or written, Mokka's PropertyMock can be helpful. It is generic over the type of the property and its use in the mock implementation is quite self-explanatory: Instead of using a stored property, declare a computed property and delegate the getter and setter (if it's settable property) to the get() and set(_:) methods of the PropertyMock object:

classEngineMock:Engine{letisOnProperty=PropertyMock<Bool>(name:"isOn")varisOn:Bool{get{return isOnProperty.get()}set{ isOnProperty.set(newValue)}}
// ...
}

Note that you don't need to provide a default value for the property (the get() method fails with a preconditionFailure if there is no value).

Call count verification

A common use case for mocks is to verify if a method has been called, and sometimes specifically how often it has been called. For that, both FunctionMock and ReturningFunctionMock provide some properties:

  • called: Bool Returns whether the method has been called (once or more).
  • calledOnce: Bool Returns whether the method has been called exactly once.
  • callCount: Int The number of times the method has been called.
XCTAssertTrue(engineMock.setSpeedFunc.called)XCTAssertTrue(engineMock.setSpeedFunc.calledOnce)XCTAssertEqual(engineMock.setSpeedFunc.callCount,3)

Argument verification

In addition to verifying if a function has been called, you often also want to check the argument(s) with which the function has been called. You can do that via the arguments property:

XCTAssertEqual(engineMock.setSpeedInUnitFunc.arguments.value,100.0)XCTAssertEqual(engineMock.setSpeedInUnitFunc.arguments.unit,.kilometersPerHour)

(This requires that you follow the recommended practice of naming the tuple members, see above. If you don't, you have to access the arguments by their index, e.g. arguments.0.)

For single-argument functions (where there's no arguments tuple) you can also use the argument property, which looks a bit nicer:

XCTAssertEqual(engineMock.setSpeedFunc.argument,100.0)

Stubbing

Sometimes it's necessary to stub the behavior of a function, for example to introduce some important side-effects. One common example for this is calling a delegate method. You can do that by providing a closure that will be executed when the function is called. The closure will be provided with the function arguments:

letdelegate=FooDelegateMock()
someMock.myFunction.stub{ arg in
delegate.somethingHappened(with: arg)}

Faking the return value

For returning functions it's crucial to be able to fake the return value. Mokka provides 3 ways of doing that: Static return values, dynamic return values and conditional return values. Let's have a look at each of those.

Providing a static return value

For most cases it's sufficient to provide a simple static value that should be returned by the mock implementation:

engineMock.currentSpeedFunc.returns(100.0)

Providing a return value dynamically

Sometimes it's convenient to provide a return value that is dynamically generated, often depending on the function's arguments. You can do that by providing a closure:

engineMock.currentSpeedFunc.returns{ unit in
// always return 100 km/h, converted to the requested target unit
letkmhValue=Measurement(value:100, unit:UnitSpeed.kilometersPerHour)return kmhValue.converted(to: unit).value
}

Providing return values conditionally

Both, static and dynamic return values can also be provided conditionally:

engineMock.currentSpeedFunc.returns(100.00, when:{ $0 ==.kilometersPerHour })
engineMock.currentSpeedFunc.returns(62.137, when:{ $0 ==.milesPerHour })
engineMock.currentSpeedFunc.returns(0) // otherwise

Mocking properties

This is how you use properties that are backed by PropertyMock in your testing code:

someMock.fooProperty.value =10	// use value to access the underlying property value
// do something
XCTAssertTrue(someMock.fooProperty.hasBeenRead)XCTAssertFalse(someMock.fooProperty.hasBeenSet

Example

You can find a simple example project in MokkaExample.

It includes

  • a subject under test (Car)
  • two mocked protocols (Engine and Battery)

It is a minimal example, but it should be enough to get you started with the concepts of Mokka.

Author

Mokka has been created and is maintained by Daniel Rinser, @danielrinser.

License

Mokka is available under the MIT License.

About

A collection of helpers to make it easier to write testing mocks in Swift.

Topics

Resources

Stars

6 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Mokka

Bitrise build statusCode coverageCocoaPodsSwift VersionLicenseTwitter

A collection of helpers to make it easier to write testing mocks in Swift.

Motivation

Due to Swift's very static nature, mocking and stubbing is much harder to do than in other languages. There are no dynamic mocking framework like OCMock or Mockito. The usual approach is to just write your mock objects manually, like so:

protocolFoo{func doSomething(arg:String)->Int}classFooMock:Foo{vardoSomethingHasBeenCalled=falsevardoSomethingArgument= String?vardoSomethingReturnValue:Int!func doSomething(arg:String)->Int{
doSomethingHasBeenCalled =true
doSomethingArgument = arg
return doSomethingReturnValue
}}

This is a lot of boilerplate (and this is just a simple example, which doesn't allow for conditional stubbing, for example). This is where Mokka comes in.

Overview

Mokka provides a testing helper class called FunctionMock<Args> (and a variant for returning functions called ReturningFunctionMock<Args, ReturnValue>) that takes care of:

  • Recording function/method calls for verification (Has the method been called?, How often has the method been called?)
  • Capturing the arguments for verification (With which arguments has the method been called?)
  • Stubbing return values (also conditionally) (This method should return 42 if called with argument "x")

With these helpers it gets much more convenient to define your mock objects:

classFooMock:Foo{letdoSomethingFunc=ReturningFunctionMock<String,Int>()func doSomething(arg:String)->Int{return doSomethingFunc.recordCallAndReturn(arg)}}

You can now use the function mock object for verification:

func testSomething(){
// ...
XCTAssertEqual(myMock.doSomethingFunc.callCount,2)XCTAssertEqual(myMock.doSomethingFunc.argument,"lorem ipsum")
// ...
}

and for faking the return value:

func testSomething(){
// static return value
myMock.doSomethingFunc.returns(100)
// dynamic return value
myMock.doSomethingFunc.returns{ $0 +200} // $0 is the argument(s) passed to the method
// conditional return value
myMock.doSomethingFunc.returns(123, when:{ $0 =="foo"})
myMock.doSomethingFunc.returns(456, when:{ $0 =="bar"})
myMock.doSomethingFunc.returns(789)}

Requirements

  • Xcode 10.2
  • Swift 5.0

Installation

CocoaPods

To install Mokka via CocoaPods, just add the Mokka pod for your test target to the Podfile:

pod'Mokka'

Swift Package Manager

You can install Mokka using Swift Package Manager. Just add this repository as a dependency to your Package.swift file (and don't forget to also add "Mokka" as a dependency in your test target):

dependencies:[.package(url:"https://github.com/danielr/Mokka", from:"1.0.0")
// ...
]

Carthage

To install Mokka with Carthage, add this to your Cartfile:

github "danielr/Mokka"

Then drag the built Mokka.framework to your project and make sure it's added to the unit test target, not the main app target. If you see issues errors like "The bundle xxx couldn’t be loaded because it is damaged or missing necessary resources.", then you might need to tweak your test target's Runtime Search Paths.

Documentation

Types of mocks

There are currently three types of mock helpers available:

  • FunctionMock: Allows to record the calls to a function (the call count and the arguments), as well as to optionally stub the function's behavior. Use this for functions that have a Void return value.
  • ReturningFunctionMock: Provides the same functionality as FunctionMock, but adds the ability to fake the returned value. Use this for functions that have a non-void return value.
  • PropertyMock: Allows to provide fake values for a property and record whether a property has been read or set.

How to implement your mocks

The first step is to implement your mocks using Mokka's helpers. The general approach is the same for all types of mocks: You declare a property for the mock and use that mock object inside your function implementations to record the calls to that function.

For the examples below, let's assume we want to mock the following protocol:

protocolEngine{func turnOn()func turnOff()varisOn:Bool{get}func setSpeed(to value:Float) // kilometers per hour
func setSpeed(to value:Float, in unit:UnitSpeed)func currentSpeed(in unit:UnitSpeed)->Double}

Functions without return value

For functions that don't return a value, use FunctionMock<Args>. This class has one generic parameter which defines the type(s) of the argument(s).

For functions with no arguments, that should be Void:

classEngineMock:Engine{letturnOnFunc=FunctionMock<Void>(name:"turnOn()")func turnOn(){
turnOnFunc.recordCall()}
// ...
}

Note: The name parameter in the mock initializers is optional. It is purely informational and might be useful for error messages (e.g. better assertion error messages). It is good practice to provide the names in the standard Swift #selector syntax.

For functions with a single argument, just use that argument's type:

classEngineMock:Engine{letsetSpeedFunc=FunctionMock<Float>(name:"setSpeed(to:)")func setSpeed(to value:Float){
setSpeedFunc.recordCall(value)}
// ...
}

For functions with more than one argument, you need to use a tuple to represent the arguments (because Swift does not (yet?) support variadic generic parameters). Although you don't have to, it is a good practice to name the tuple elements, which makes it much clearer when referring to them in your testing code.

classEngineMock:Engine{letsetSpeedInUnitFunc=FunctionMock<(value:Float, unit:UnitSpeed)>(name:"setSpeed(to:unit:)")func setSpeed(to value:Float, in unit:UnitSpeed){
setSpeedInUnitFunc.recordCall((value: value, unit: unit))}
// ...
}

Functions with return value

For functions with a return value, use ReturningFunctionMock<Args, ReturnValue>. This works very much the same way as FunctionMock, but adds a second generic parameter for the return value type. It also provides a recordCallAndReturn() method, instead of the recordCall() method.

classEngineMock:Engine{letcurrentSpeedFunc=ReturningFunctionMock<UnitSpeed,Double>(name:"currentSpeed(in:)")func currentSpeed(in unit:UnitSpeed)->Double{return currentSpeedFunc.recordCallAndReturn(unit)}
// ...
}

For the arguments of returning methods, the same rules apply as for non-returning functions (see above). For example:

  • A mock for a function that has no arguments and returns a Bool would be declared as ReturningFunctionMock<Void, Bool>
  • A mock for a function that has two arguments of type Int and String? and returns a Double would be declared as ReturningFunctionMock<(arg1: Int, arg2: String?), Double>

Properties

In many cases it is enough to just implement property requirements of the mocked protocol by declaring a stored property with a default value in your mock. However, when you want to be able to explicitly track whether a property has been read or written, Mokka's PropertyMock can be helpful. It is generic over the type of the property and its use in the mock implementation is quite self-explanatory: Instead of using a stored property, declare a computed property and delegate the getter and setter (if it's settable property) to the get() and set(_:) methods of the PropertyMock object:

classEngineMock:Engine{letisOnProperty=PropertyMock<Bool>(name:"isOn")varisOn:Bool{get{return isOnProperty.get()}set{ isOnProperty.set(newValue)}}
// ...
}

Note that you don't need to provide a default value for the property (the get() method fails with a preconditionFailure if there is no value).

Call count verification

A common use case for mocks is to verify if a method has been called, and sometimes specifically how often it has been called. For that, both FunctionMock and ReturningFunctionMock provide some properties:

  • called: Bool Returns whether the method has been called (once or more).
  • calledOnce: Bool Returns whether the method has been called exactly once.
  • callCount: Int The number of times the method has been called.
XCTAssertTrue(engineMock.setSpeedFunc.called)XCTAssertTrue(engineMock.setSpeedFunc.calledOnce)XCTAssertEqual(engineMock.setSpeedFunc.callCount,3)

Argument verification

In addition to verifying if a function has been called, you often also want to check the argument(s) with which the function has been called. You can do that via the arguments property:

XCTAssertEqual(engineMock.setSpeedInUnitFunc.arguments.value,100.0)XCTAssertEqual(engineMock.setSpeedInUnitFunc.arguments.unit,.kilometersPerHour)

(This requires that you follow the recommended practice of naming the tuple members, see above. If you don't, you have to access the arguments by their index, e.g. arguments.0.)

For single-argument functions (where there's no arguments tuple) you can also use the argument property, which looks a bit nicer:

XCTAssertEqual(engineMock.setSpeedFunc.argument,100.0)

Stubbing

Sometimes it's necessary to stub the behavior of a function, for example to introduce some important side-effects. One common example for this is calling a delegate method. You can do that by providing a closure that will be executed when the function is called. The closure will be provided with the function arguments:

letdelegate=FooDelegateMock()
someMock.myFunction.stub{ arg in
delegate.somethingHappened(with: arg)}

Faking the return value

For returning functions it's crucial to be able to fake the return value. Mokka provides 3 ways of doing that: Static return values, dynamic return values and conditional return values. Let's have a look at each of those.

Providing a static return value

For most cases it's sufficient to provide a simple static value that should be returned by the mock implementation:

engineMock.currentSpeedFunc.returns(100.0)

Providing a return value dynamically

Sometimes it's convenient to provide a return value that is dynamically generated, often depending on the function's arguments. You can do that by providing a closure:

engineMock.currentSpeedFunc.returns{ unit in
// always return 100 km/h, converted to the requested target unit
letkmhValue=Measurement(value:100, unit:UnitSpeed.kilometersPerHour)return kmhValue.converted(to: unit).value
}

Providing return values conditionally

Both, static and dynamic return values can also be provided conditionally:

engineMock.currentSpeedFunc.returns(100.00, when:{ $0 ==.kilometersPerHour })
engineMock.currentSpeedFunc.returns(62.137, when:{ $0 ==.milesPerHour })
engineMock.currentSpeedFunc.returns(0) // otherwise

Mocking properties

This is how you use properties that are backed by PropertyMock in your testing code:

someMock.fooProperty.value =10	// use value to access the underlying property value
// do something
XCTAssertTrue(someMock.fooProperty.hasBeenRead)XCTAssertFalse(someMock.fooProperty.hasBeenSet

Example

You can find a simple example project in MokkaExample.

It includes

  • a subject under test (Car)
  • two mocked protocols (Engine and Battery)

It is a minimal example, but it should be enough to get you started with the concepts of Mokka.

Author

Mokka has been created and is maintained by Daniel Rinser, @danielrinser.

License

Mokka is available under the MIT License.

About

A collection of helpers to make it easier to write testing mocks in Swift.

Topics

Resources

Stars

6 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Repository files navigation

Mokka

Bitrise build statusCode coverageCocoaPodsSwift VersionLicenseTwitter

A collection of helpers to make it easier to write testing mocks in Swift.

Motivation

Due to Swift's very static nature, mocking and stubbing is much harder to do than in other languages. There are no dynamic mocking framework like OCMock or Mockito. The usual approach is to just write your mock objects manually, like so:

protocolFoo{func doSomething(arg:String)->Int}classFooMock:Foo{vardoSomethingHasBeenCalled=falsevardoSomethingArgument= String?vardoSomethingReturnValue:Int!func doSomething(arg:String)->Int{
doSomethingHasBeenCalled =true
doSomethingArgument = arg
return doSomethingReturnValue
}}

This is a lot of boilerplate (and this is just a simple example, which doesn't allow for conditional stubbing, for example). This is where Mokka comes in.

Overview

Mokka provides a testing helper class called FunctionMock<Args> (and a variant for returning functions called ReturningFunctionMock<Args, ReturnValue>) that takes care of:

  • Recording function/method calls for verification (Has the method been called?, How often has the method been called?)
  • Capturing the arguments for verification (With which arguments has the method been called?)
  • Stubbing return values (also conditionally) (This method should return 42 if called with argument "x")

With these helpers it gets much more convenient to define your mock objects:

classFooMock:Foo{letdoSomethingFunc=ReturningFunctionMock<String,Int>()func doSomething(arg:String)->Int{return doSomethingFunc.recordCallAndReturn(arg)}}

You can now use the function mock object for verification:

func testSomething(){
// ...
XCTAssertEqual(myMock.doSomethingFunc.callCount,2)XCTAssertEqual(myMock.doSomethingFunc.argument,"lorem ipsum")
// ...
}

and for faking the return value:

func testSomething(){
// static return value
myMock.doSomethingFunc.returns(100)
// dynamic return value
myMock.doSomethingFunc.returns{ $0 +200} // $0 is the argument(s) passed to the method
// conditional return value
myMock.doSomethingFunc.returns(123, when:{ $0 =="foo"})
myMock.doSomethingFunc.returns(456, when:{ $0 =="bar"})
myMock.doSomethingFunc.returns(789)}

Requirements

  • Xcode 10.2
  • Swift 5.0

Installation

CocoaPods

To install Mokka via CocoaPods, just add the Mokka pod for your test target to the Podfile:

pod'Mokka'

Swift Package Manager

You can install Mokka using Swift Package Manager. Just add this repository as a dependency to your Package.swift file (and don't forget to also add "Mokka" as a dependency in your test target):

dependencies:[.package(url:"https://github.com/danielr/Mokka", from:"1.0.0")
// ...
]

Carthage

To install Mokka with Carthage, add this to your Cartfile:

github "danielr/Mokka"

Then drag the built Mokka.framework to your project and make sure it's added to the unit test target, not the main app target. If you see issues errors like "The bundle xxx couldn’t be loaded because it is damaged or missing necessary resources.", then you might need to tweak your test target's Runtime Search Paths.

Documentation

Types of mocks

There are currently three types of mock helpers available:

  • FunctionMock: Allows to record the calls to a function (the call count and the arguments), as well as to optionally stub the function's behavior. Use this for functions that have a Void return value.
  • ReturningFunctionMock: Provides the same functionality as FunctionMock, but adds the ability to fake the returned value. Use this for functions that have a non-void return value.
  • PropertyMock: Allows to provide fake values for a property and record whether a property has been read or set.

How to implement your mocks

The first step is to implement your mocks using Mokka's helpers. The general approach is the same for all types of mocks: You declare a property for the mock and use that mock object inside your function implementations to record the calls to that function.

For the examples below, let's assume we want to mock the following protocol:

protocolEngine{func turnOn()func turnOff()varisOn:Bool{get}func setSpeed(to value:Float) // kilometers per hour
func setSpeed(to value:Float, in unit:UnitSpeed)func currentSpeed(in unit:UnitSpeed)->Double}

Functions without return value

For functions that don't return a value, use FunctionMock<Args>. This class has one generic parameter which defines the type(s) of the argument(s).

For functions with no arguments, that should be Void:

classEngineMock:Engine{letturnOnFunc=FunctionMock<Void>(name:"turnOn()")func turnOn(){
turnOnFunc.recordCall()}
// ...
}

Note: The name parameter in the mock initializers is optional. It is purely informational and might be useful for error messages (e.g. better assertion error messages). It is good practice to provide the names in the standard Swift #selector syntax.

For functions with a single argument, just use that argument's type:

classEngineMock:Engine{letsetSpeedFunc=FunctionMock<Float>(name:"setSpeed(to:)")func setSpeed(to value:Float){
setSpeedFunc.recordCall(value)}
// ...
}

For functions with more than one argument, you need to use a tuple to represent the arguments (because Swift does not (yet?) support variadic generic parameters). Although you don't have to, it is a good practice to name the tuple elements, which makes it much clearer when referring to them in your testing code.

classEngineMock:Engine{letsetSpeedInUnitFunc=FunctionMock<(value:Float, unit:UnitSpeed)>(name:"setSpeed(to:unit:)")func setSpeed(to value:Float, in unit:UnitSpeed){
setSpeedInUnitFunc.recordCall((value: value, unit: unit))}
// ...
}

Functions with return value

For functions with a return value, use ReturningFunctionMock<Args, ReturnValue>. This works very much the same way as FunctionMock, but adds a second generic parameter for the return value type. It also provides a recordCallAndReturn() method, instead of the recordCall() method.

classEngineMock:Engine{letcurrentSpeedFunc=ReturningFunctionMock<UnitSpeed,Double>(name:"currentSpeed(in:)")func currentSpeed(in unit:UnitSpeed)->Double{return currentSpeedFunc.recordCallAndReturn(unit)}
// ...
}

For the arguments of returning methods, the same rules apply as for non-returning functions (see above). For example:

  • A mock for a function that has no arguments and returns a Bool would be declared as ReturningFunctionMock<Void, Bool>
  • A mock for a function that has two arguments of type Int and String? and returns a Double would be declared as ReturningFunctionMock<(arg1: Int, arg2: String?), Double>

Properties

In many cases it is enough to just implement property requirements of the mocked protocol by declaring a stored property with a default value in your mock. However, when you want to be able to explicitly track whether a property has been read or written, Mokka's PropertyMock can be helpful. It is generic over the type of the property and its use in the mock implementation is quite self-explanatory: Instead of using a stored property, declare a computed property and delegate the getter and setter (if it's settable property) to the get() and set(_:) methods of the PropertyMock object:

classEngineMock:Engine{letisOnProperty=PropertyMock<Bool>(name:"isOn")varisOn:Bool{get{return isOnProperty.get()}set{ isOnProperty.set(newValue)}}
// ...
}

Note that you don't need to provide a default value for the property (the get() method fails with a preconditionFailure if there is no value).

Call count verification

A common use case for mocks is to verify if a method has been called, and sometimes specifically how often it has been called. For that, both FunctionMock and ReturningFunctionMock provide some properties:

  • called: Bool Returns whether the method has been called (once or more).
  • calledOnce: Bool Returns whether the method has been called exactly once.
  • callCount: Int The number of times the method has been called.
XCTAssertTrue(engineMock.setSpeedFunc.called)XCTAssertTrue(engineMock.setSpeedFunc.calledOnce)XCTAssertEqual(engineMock.setSpeedFunc.callCount,3)

Argument verification

In addition to verifying if a function has been called, you often also want to check the argument(s) with which the function has been called. You can do that via the arguments property:

XCTAssertEqual(engineMock.setSpeedInUnitFunc.arguments.value,100.0)XCTAssertEqual(engineMock.setSpeedInUnitFunc.arguments.unit,.kilometersPerHour)

(This requires that you follow the recommended practice of naming the tuple members, see above. If you don't, you have to access the arguments by their index, e.g. arguments.0.)

For single-argument functions (where there's no arguments tuple) you can also use the argument property, which looks a bit nicer:

XCTAssertEqual(engineMock.setSpeedFunc.argument,100.0)

Stubbing

Sometimes it's necessary to stub the behavior of a function, for example to introduce some important side-effects. One common example for this is calling a delegate method. You can do that by providing a closure that will be executed when the function is called. The closure will be provided with the function arguments:

letdelegate=FooDelegateMock()
someMock.myFunction.stub{ arg in
delegate.somethingHappened(with: arg)}

Faking the return value

For returning functions it's crucial to be able to fake the return value. Mokka provides 3 ways of doing that: Static return values, dynamic return values and conditional return values. Let's have a look at each of those.

Providing a static return value

For most cases it's sufficient to provide a simple static value that should be returned by the mock implementation:

engineMock.currentSpeedFunc.returns(100.0)

Providing a return value dynamically

Sometimes it's convenient to provide a return value that is dynamically generated, often depending on the function's arguments. You can do that by providing a closure:

engineMock.currentSpeedFunc.returns{ unit in
// always return 100 km/h, converted to the requested target unit
letkmhValue=Measurement(value:100, unit:UnitSpeed.kilometersPerHour)return kmhValue.converted(to: unit).value
}

Providing return values conditionally

Both, static and dynamic return values can also be provided conditionally:

engineMock.currentSpeedFunc.returns(100.00, when:{ $0 ==.kilometersPerHour })
engineMock.currentSpeedFunc.returns(62.137, when:{ $0 ==.milesPerHour })
engineMock.currentSpeedFunc.returns(0) // otherwise

Mocking properties

This is how you use properties that are backed by PropertyMock in your testing code:

someMock.fooProperty.value =10	// use value to access the underlying property value
// do something
XCTAssertTrue(someMock.fooProperty.hasBeenRead)XCTAssertFalse(someMock.fooProperty.hasBeenSet

Example

You can find a simple example project in MokkaExample.

It includes

  • a subject under test (Car)
  • two mocked protocols (Engine and Battery)

It is a minimal example, but it should be enough to get you started with the concepts of Mokka.

Author

Mokka has been created and is maintained by Daniel Rinser, @danielrinser.

License

Mokka is available under the MIT License.

About

A collection of helpers to make it easier to write testing mocks in Swift.

Topics

Resources

Stars

6 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Mokka

Bitrise build statusCode coverageCocoaPodsSwift VersionLicenseTwitter

A collection of helpers to make it easier to write testing mocks in Swift.

Motivation

Due to Swift's very static nature, mocking and stubbing is much harder to do than in other languages. There are no dynamic mocking framework like OCMock or Mockito. The usual approach is to just write your mock objects manually, like so:

protocolFoo{func doSomething(arg:String)->Int}classFooMock:Foo{vardoSomethingHasBeenCalled=falsevardoSomethingArgument= String?vardoSomethingReturnValue:Int!func doSomething(arg:String)->Int{
doSomethingHasBeenCalled =true
doSomethingArgument = arg
return doSomethingReturnValue
}}

This is a lot of boilerplate (and this is just a simple example, which doesn't allow for conditional stubbing, for example). This is where Mokka comes in.

Overview

Mokka provides a testing helper class called FunctionMock<Args> (and a variant for returning functions called ReturningFunctionMock<Args, ReturnValue>) that takes care of:

  • Recording function/method calls for verification (Has the method been called?, How often has the method been called?)
  • Capturing the arguments for verification (With which arguments has the method been called?)
  • Stubbing return values (also conditionally) (This method should return 42 if called with argument "x")

With these helpers it gets much more convenient to define your mock objects:

classFooMock:Foo{letdoSomethingFunc=ReturningFunctionMock<String,Int>()func doSomething(arg:String)->Int{return doSomethingFunc.recordCallAndReturn(arg)}}

You can now use the function mock object for verification:

func testSomething(){
// ...
XCTAssertEqual(myMock.doSomethingFunc.callCount,2)XCTAssertEqual(myMock.doSomethingFunc.argument,"lorem ipsum")
// ...
}

and for faking the return value:

func testSomething(){
// static return value
myMock.doSomethingFunc.returns(100)
// dynamic return value
myMock.doSomethingFunc.returns{ $0 +200} // $0 is the argument(s) passed to the method
// conditional return value
myMock.doSomethingFunc.returns(123, when:{ $0 =="foo"})
myMock.doSomethingFunc.returns(456, when:{ $0 =="bar"})
myMock.doSomethingFunc.returns(789)}

Requirements

  • Xcode 10.2
  • Swift 5.0

Installation

CocoaPods

To install Mokka via CocoaPods, just add the Mokka pod for your test target to the Podfile:

pod'Mokka'

Swift Package Manager

You can install Mokka using Swift Package Manager. Just add this repository as a dependency to your Package.swift file (and don't forget to also add "Mokka" as a dependency in your test target):

dependencies:[.package(url:"https://github.com/danielr/Mokka", from:"1.0.0")
// ...
]

Carthage

To install Mokka with Carthage, add this to your Cartfile:

github "danielr/Mokka"

Then drag the built Mokka.framework to your project and make sure it's added to the unit test target, not the main app target. If you see issues errors like "The bundle xxx couldn’t be loaded because it is damaged or missing necessary resources.", then you might need to tweak your test target's Runtime Search Paths.

Documentation

Types of mocks

There are currently three types of mock helpers available:

  • FunctionMock: Allows to record the calls to a function (the call count and the arguments), as well as to optionally stub the function's behavior. Use this for functions that have a Void return value.
  • ReturningFunctionMock: Provides the same functionality as FunctionMock, but adds the ability to fake the returned value. Use this for functions that have a non-void return value.
  • PropertyMock: Allows to provide fake values for a property and record whether a property has been read or set.

How to implement your mocks

The first step is to implement your mocks using Mokka's helpers. The general approach is the same for all types of mocks: You declare a property for the mock and use that mock object inside your function implementations to record the calls to that function.

For the examples below, let's assume we want to mock the following protocol:

protocolEngine{func turnOn()func turnOff()varisOn:Bool{get}func setSpeed(to value:Float) // kilometers per hour
func setSpeed(to value:Float, in unit:UnitSpeed)func currentSpeed(in unit:UnitSpeed)->Double}

Functions without return value

For functions that don't return a value, use FunctionMock<Args>. This class has one generic parameter which defines the type(s) of the argument(s).

For functions with no arguments, that should be Void:

classEngineMock:Engine{letturnOnFunc=FunctionMock<Void>(name:"turnOn()")func turnOn(){
turnOnFunc.recordCall()}
// ...
}

Note: The name parameter in the mock initializers is optional. It is purely informational and might be useful for error messages (e.g. better assertion error messages). It is good practice to provide the names in the standard Swift #selector syntax.

For functions with a single argument, just use that argument's type:

classEngineMock:Engine{letsetSpeedFunc=FunctionMock<Float>(name:"setSpeed(to:)")func setSpeed(to value:Float){
setSpeedFunc.recordCall(value)}
// ...
}

For functions with more than one argument, you need to use a tuple to represent the arguments (because Swift does not (yet?) support variadic generic parameters). Although you don't have to, it is a good practice to name the tuple elements, which makes it much clearer when referring to them in your testing code.

classEngineMock:Engine{letsetSpeedInUnitFunc=FunctionMock<(value:Float, unit:UnitSpeed)>(name:"setSpeed(to:unit:)")func setSpeed(to value:Float, in unit:UnitSpeed){
setSpeedInUnitFunc.recordCall((value: value, unit: unit))}
// ...
}

Functions with return value

For functions with a return value, use ReturningFunctionMock<Args, ReturnValue>. This works very much the same way as FunctionMock, but adds a second generic parameter for the return value type. It also provides a recordCallAndReturn() method, instead of the recordCall() method.

classEngineMock:Engine{letcurrentSpeedFunc=ReturningFunctionMock<UnitSpeed,Double>(name:"currentSpeed(in:)")func currentSpeed(in unit:UnitSpeed)->Double{return currentSpeedFunc.recordCallAndReturn(unit)}
// ...
}

For the arguments of returning methods, the same rules apply as for non-returning functions (see above). For example:

  • A mock for a function that has no arguments and returns a Bool would be declared as ReturningFunctionMock<Void, Bool>
  • A mock for a function that has two arguments of type Int and String? and returns a Double would be declared as ReturningFunctionMock<(arg1: Int, arg2: String?), Double>

Properties

In many cases it is enough to just implement property requirements of the mocked protocol by declaring a stored property with a default value in your mock. However, when you want to be able to explicitly track whether a property has been read or written, Mokka's PropertyMock can be helpful. It is generic over the type of the property and its use in the mock implementation is quite self-explanatory: Instead of using a stored property, declare a computed property and delegate the getter and setter (if it's settable property) to the get() and set(_:) methods of the PropertyMock object:

classEngineMock:Engine{letisOnProperty=PropertyMock<Bool>(name:"isOn")varisOn:Bool{get{return isOnProperty.get()}set{ isOnProperty.set(newValue)}}
// ...
}

Note that you don't need to provide a default value for the property (the get() method fails with a preconditionFailure if there is no value).

Call count verification

A common use case for mocks is to verify if a method has been called, and sometimes specifically how often it has been called. For that, both FunctionMock and ReturningFunctionMock provide some properties:

  • called: Bool Returns whether the method has been called (once or more).
  • calledOnce: Bool Returns whether the method has been called exactly once.
  • callCount: Int The number of times the method has been called.
XCTAssertTrue(engineMock.setSpeedFunc.called)XCTAssertTrue(engineMock.setSpeedFunc.calledOnce)XCTAssertEqual(engineMock.setSpeedFunc.callCount,3)

Argument verification

In addition to verifying if a function has been called, you often also want to check the argument(s) with which the function has been called. You can do that via the arguments property:

XCTAssertEqual(engineMock.setSpeedInUnitFunc.arguments.value,100.0)XCTAssertEqual(engineMock.setSpeedInUnitFunc.arguments.unit,.kilometersPerHour)

(This requires that you follow the recommended practice of naming the tuple members, see above. If you don't, you have to access the arguments by their index, e.g. arguments.0.)

For single-argument functions (where there's no arguments tuple) you can also use the argument property, which looks a bit nicer:

XCTAssertEqual(engineMock.setSpeedFunc.argument,100.0)

Stubbing

Sometimes it's necessary to stub the behavior of a function, for example to introduce some important side-effects. One common example for this is calling a delegate method. You can do that by providing a closure that will be executed when the function is called. The closure will be provided with the function arguments:

letdelegate=FooDelegateMock()
someMock.myFunction.stub{ arg in
delegate.somethingHappened(with: arg)}

Faking the return value

For returning functions it's crucial to be able to fake the return value. Mokka provides 3 ways of doing that: Static return values, dynamic return values and conditional return values. Let's have a look at each of those.

Providing a static return value

For most cases it's sufficient to provide a simple static value that should be returned by the mock implementation:

engineMock.currentSpeedFunc.returns(100.0)

Providing a return value dynamically

Sometimes it's convenient to provide a return value that is dynamically generated, often depending on the function's arguments. You can do that by providing a closure:

engineMock.currentSpeedFunc.returns{ unit in
// always return 100 km/h, converted to the requested target unit
letkmhValue=Measurement(value:100, unit:UnitSpeed.kilometersPerHour)return kmhValue.converted(to: unit).value
}

Providing return values conditionally

Both, static and dynamic return values can also be provided conditionally:

engineMock.currentSpeedFunc.returns(100.00, when:{ $0 ==.kilometersPerHour })
engineMock.currentSpeedFunc.returns(62.137, when:{ $0 ==.milesPerHour })
engineMock.currentSpeedFunc.returns(0) // otherwise

Mocking properties

This is how you use properties that are backed by PropertyMock in your testing code:

someMock.fooProperty.value =10	// use value to access the underlying property value
// do something
XCTAssertTrue(someMock.fooProperty.hasBeenRead)XCTAssertFalse(someMock.fooProperty.hasBeenSet

Example

You can find a simple example project in MokkaExample.

It includes

  • a subject under test (Car)
  • two mocked protocols (Engine and Battery)

It is a minimal example, but it should be enough to get you started with the concepts of Mokka.

Author

Mokka has been created and is maintained by Daniel Rinser, @danielrinser.

License

Mokka is available under the MIT License.

About

A collection of helpers to make it easier to write testing mocks in Swift.

Topics

Resources

Stars

6 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Mokka

Bitrise build statusCode coverageCocoaPodsSwift VersionLicenseTwitter

A collection of helpers to make it easier to write testing mocks in Swift.

Motivation

Due to Swift's very static nature, mocking and stubbing is much harder to do than in other languages. There are no dynamic mocking framework like OCMock or Mockito. The usual approach is to just write your mock objects manually, like so:

protocolFoo{func doSomething(arg:String)->Int}classFooMock:Foo{vardoSomethingHasBeenCalled=falsevardoSomethingArgument= String?vardoSomethingReturnValue:Int!func doSomething(arg:String)->Int{
doSomethingHasBeenCalled =true
doSomethingArgument = arg
return doSomethingReturnValue
}}

This is a lot of boilerplate (and this is just a simple example, which doesn't allow for conditional stubbing, for example). This is where Mokka comes in.

Overview

Mokka provides a testing helper class called FunctionMock<Args> (and a variant for returning functions called ReturningFunctionMock<Args, ReturnValue>) that takes care of:

  • Recording function/method calls for verification (Has the method been called?, How often has the method been called?)
  • Capturing the arguments for verification (With which arguments has the method been called?)
  • Stubbing return values (also conditionally) (This method should return 42 if called with argument "x")

With these helpers it gets much more convenient to define your mock objects:

classFooMock:Foo{letdoSomethingFunc=ReturningFunctionMock<String,Int>()func doSomething(arg:String)->Int{return doSomethingFunc.recordCallAndReturn(arg)}}

You can now use the function mock object for verification:

func testSomething(){
// ...
XCTAssertEqual(myMock.doSomethingFunc.callCount,2)XCTAssertEqual(myMock.doSomethingFunc.argument,"lorem ipsum")
// ...
}

and for faking the return value:

func testSomething(){
// static return value
myMock.doSomethingFunc.returns(100)
// dynamic return value
myMock.doSomethingFunc.returns{ $0 +200} // $0 is the argument(s) passed to the method
// conditional return value
myMock.doSomethingFunc.returns(123, when:{ $0 =="foo"})
myMock.doSomethingFunc.returns(456, when:{ $0 =="bar"})
myMock.doSomethingFunc.returns(789)}

Requirements

  • Xcode 10.2
  • Swift 5.0

Installation

CocoaPods

To install Mokka via CocoaPods, just add the Mokka pod for your test target to the Podfile:

pod'Mokka'

Swift Package Manager

You can install Mokka using Swift Package Manager. Just add this repository as a dependency to your Package.swift file (and don't forget to also add "Mokka" as a dependency in your test target):

dependencies:[.package(url:"https://github.com/danielr/Mokka", from:"1.0.0")
// ...
]

Carthage

To install Mokka with Carthage, add this to your Cartfile:

github "danielr/Mokka"

Then drag the built Mokka.framework to your project and make sure it's added to the unit test target, not the main app target. If you see issues errors like "The bundle xxx couldn’t be loaded because it is damaged or missing necessary resources.", then you might need to tweak your test target's Runtime Search Paths.

Documentation

Types of mocks

There are currently three types of mock helpers available:

  • FunctionMock: Allows to record the calls to a function (the call count and the arguments), as well as to optionally stub the function's behavior. Use this for functions that have a Void return value.
  • ReturningFunctionMock: Provides the same functionality as FunctionMock, but adds the ability to fake the returned value. Use this for functions that have a non-void return value.
  • PropertyMock: Allows to provide fake values for a property and record whether a property has been read or set.

How to implement your mocks

The first step is to implement your mocks using Mokka's helpers. The general approach is the same for all types of mocks: You declare a property for the mock and use that mock object inside your function implementations to record the calls to that function.

For the examples below, let's assume we want to mock the following protocol:

protocolEngine{func turnOn()func turnOff()varisOn:Bool{get}func setSpeed(to value:Float) // kilometers per hour
func setSpeed(to value:Float, in unit:UnitSpeed)func currentSpeed(in unit:UnitSpeed)->Double}

Functions without return value

For functions that don't return a value, use FunctionMock<Args>. This class has one generic parameter which defines the type(s) of the argument(s).

For functions with no arguments, that should be Void:

classEngineMock:Engine{letturnOnFunc=FunctionMock<Void>(name:"turnOn()")func turnOn(){
turnOnFunc.recordCall()}
// ...
}

Note: The name parameter in the mock initializers is optional. It is purely informational and might be useful for error messages (e.g. better assertion error messages). It is good practice to provide the names in the standard Swift #selector syntax.

For functions with a single argument, just use that argument's type:

classEngineMock:Engine{letsetSpeedFunc=FunctionMock<Float>(name:"setSpeed(to:)")func setSpeed(to value:Float){
setSpeedFunc.recordCall(value)}
// ...
}

For functions with more than one argument, you need to use a tuple to represent the arguments (because Swift does not (yet?) support variadic generic parameters). Although you don't have to, it is a good practice to name the tuple elements, which makes it much clearer when referring to them in your testing code.

classEngineMock:Engine{letsetSpeedInUnitFunc=FunctionMock<(value:Float, unit:UnitSpeed)>(name:"setSpeed(to:unit:)")func setSpeed(to value:Float, in unit:UnitSpeed){
setSpeedInUnitFunc.recordCall((value: value, unit: unit))}
// ...
}

Functions with return value

For functions with a return value, use ReturningFunctionMock<Args, ReturnValue>. This works very much the same way as FunctionMock, but adds a second generic parameter for the return value type. It also provides a recordCallAndReturn() method, instead of the recordCall() method.

classEngineMock:Engine{letcurrentSpeedFunc=ReturningFunctionMock<UnitSpeed,Double>(name:"currentSpeed(in:)")func currentSpeed(in unit:UnitSpeed)->Double{return currentSpeedFunc.recordCallAndReturn(unit)}
// ...
}

For the arguments of returning methods, the same rules apply as for non-returning functions (see above). For example:

  • A mock for a function that has no arguments and returns a Bool would be declared as ReturningFunctionMock<Void, Bool>
  • A mock for a function that has two arguments of type Int and String? and returns a Double would be declared as ReturningFunctionMock<(arg1: Int, arg2: String?), Double>

Properties

In many cases it is enough to just implement property requirements of the mocked protocol by declaring a stored property with a default value in your mock. However, when you want to be able to explicitly track whether a property has been read or written, Mokka's PropertyMock can be helpful. It is generic over the type of the property and its use in the mock implementation is quite self-explanatory: Instead of using a stored property, declare a computed property and delegate the getter and setter (if it's settable property) to the get() and set(_:) methods of the PropertyMock object:

classEngineMock:Engine{letisOnProperty=PropertyMock<Bool>(name:"isOn")varisOn:Bool{get{return isOnProperty.get()}set{ isOnProperty.set(newValue)}}
// ...
}

Note that you don't need to provide a default value for the property (the get() method fails with a preconditionFailure if there is no value).

Call count verification

A common use case for mocks is to verify if a method has been called, and sometimes specifically how often it has been called. For that, both FunctionMock and ReturningFunctionMock provide some properties:

  • called: Bool Returns whether the method has been called (once or more).
  • calledOnce: Bool Returns whether the method has been called exactly once.
  • callCount: Int The number of times the method has been called.
XCTAssertTrue(engineMock.setSpeedFunc.called)XCTAssertTrue(engineMock.setSpeedFunc.calledOnce)XCTAssertEqual(engineMock.setSpeedFunc.callCount,3)

Argument verification

In addition to verifying if a function has been called, you often also want to check the argument(s) with which the function has been called. You can do that via the arguments property:

XCTAssertEqual(engineMock.setSpeedInUnitFunc.arguments.value,100.0)XCTAssertEqual(engineMock.setSpeedInUnitFunc.arguments.unit,.kilometersPerHour)

(This requires that you follow the recommended practice of naming the tuple members, see above. If you don't, you have to access the arguments by their index, e.g. arguments.0.)

For single-argument functions (where there's no arguments tuple) you can also use the argument property, which looks a bit nicer:

XCTAssertEqual(engineMock.setSpeedFunc.argument,100.0)

Stubbing

Sometimes it's necessary to stub the behavior of a function, for example to introduce some important side-effects. One common example for this is calling a delegate method. You can do that by providing a closure that will be executed when the function is called. The closure will be provided with the function arguments:

letdelegate=FooDelegateMock()
someMock.myFunction.stub{ arg in
delegate.somethingHappened(with: arg)}

Faking the return value

For returning functions it's crucial to be able to fake the return value. Mokka provides 3 ways of doing that: Static return values, dynamic return values and conditional return values. Let's have a look at each of those.

Providing a static return value

For most cases it's sufficient to provide a simple static value that should be returned by the mock implementation:

engineMock.currentSpeedFunc.returns(100.0)

Providing a return value dynamically

Sometimes it's convenient to provide a return value that is dynamically generated, often depending on the function's arguments. You can do that by providing a closure:

engineMock.currentSpeedFunc.returns{ unit in
// always return 100 km/h, converted to the requested target unit
letkmhValue=Measurement(value:100, unit:UnitSpeed.kilometersPerHour)return kmhValue.converted(to: unit).value
}

Providing return values conditionally

Both, static and dynamic return values can also be provided conditionally:

engineMock.currentSpeedFunc.returns(100.00, when:{ $0 ==.kilometersPerHour })
engineMock.currentSpeedFunc.returns(62.137, when:{ $0 ==.milesPerHour })
engineMock.currentSpeedFunc.returns(0) // otherwise

Mocking properties

This is how you use properties that are backed by PropertyMock in your testing code:

someMock.fooProperty.value =10	// use value to access the underlying property value
// do something
XCTAssertTrue(someMock.fooProperty.hasBeenRead)XCTAssertFalse(someMock.fooProperty.hasBeenSet

Example

You can find a simple example project in MokkaExample.

It includes

  • a subject under test (Car)
  • two mocked protocols (Engine and Battery)

It is a minimal example, but it should be enough to get you started with the concepts of Mokka.

Author

Mokka has been created and is maintained by Daniel Rinser, @danielrinser.

License

Mokka is available under the MIT License.

About

A collection of helpers to make it easier to write testing mocks in Swift.

Topics

Resources

Stars

6 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Repository files navigation

Mokka

Bitrise build statusCode coverageCocoaPodsSwift VersionLicenseTwitter

A collection of helpers to make it easier to write testing mocks in Swift.

Motivation

Due to Swift's very static nature, mocking and stubbing is much harder to do than in other languages. There are no dynamic mocking framework like OCMock or Mockito. The usual approach is to just write your mock objects manually, like so:

protocolFoo{func doSomething(arg:String)->Int}classFooMock:Foo{vardoSomethingHasBeenCalled=falsevardoSomethingArgument= String?vardoSomethingReturnValue:Int!func doSomething(arg:String)->Int{
doSomethingHasBeenCalled =true
doSomethingArgument = arg
return doSomethingReturnValue
}}

This is a lot of boilerplate (and this is just a simple example, which doesn't allow for conditional stubbing, for example). This is where Mokka comes in.

Overview

Mokka provides a testing helper class called FunctionMock<Args> (and a variant for returning functions called ReturningFunctionMock<Args, ReturnValue>) that takes care of:

  • Recording function/method calls for verification (Has the method been called?, How often has the method been called?)
  • Capturing the arguments for verification (With which arguments has the method been called?)
  • Stubbing return values (also conditionally) (This method should return 42 if called with argument "x")

With these helpers it gets much more convenient to define your mock objects:

classFooMock:Foo{letdoSomethingFunc=ReturningFunctionMock<String,Int>()func doSomething(arg:String)->Int{return doSomethingFunc.recordCallAndReturn(arg)}}

You can now use the function mock object for verification:

func testSomething(){
// ...
XCTAssertEqual(myMock.doSomethingFunc.callCount,2)XCTAssertEqual(myMock.doSomethingFunc.argument,"lorem ipsum")
// ...
}

and for faking the return value:

func testSomething(){
// static return value
myMock.doSomethingFunc.returns(100)
// dynamic return value
myMock.doSomethingFunc.returns{ $0 +200} // $0 is the argument(s) passed to the method
// conditional return value
myMock.doSomethingFunc.returns(123, when:{ $0 =="foo"})
myMock.doSomethingFunc.returns(456, when:{ $0 =="bar"})
myMock.doSomethingFunc.returns(789)}

Requirements

  • Xcode 10.2
  • Swift 5.0

Installation

CocoaPods

To install Mokka via CocoaPods, just add the Mokka pod for your test target to the Podfile:

pod'Mokka'

Swift Package Manager

You can install Mokka using Swift Package Manager. Just add this repository as a dependency to your Package.swift file (and don't forget to also add "Mokka" as a dependency in your test target):

dependencies:[.package(url:"https://github.com/danielr/Mokka", from:"1.0.0")
// ...
]

Carthage

To install Mokka with Carthage, add this to your Cartfile:

github "danielr/Mokka"

Then drag the built Mokka.framework to your project and make sure it's added to the unit test target, not the main app target. If you see issues errors like "The bundle xxx couldn’t be loaded because it is damaged or missing necessary resources.", then you might need to tweak your test target's Runtime Search Paths.

Documentation

Types of mocks

There are currently three types of mock helpers available:

  • FunctionMock: Allows to record the calls to a function (the call count and the arguments), as well as to optionally stub the function's behavior. Use this for functions that have a Void return value.
  • ReturningFunctionMock: Provides the same functionality as FunctionMock, but adds the ability to fake the returned value. Use this for functions that have a non-void return value.
  • PropertyMock: Allows to provide fake values for a property and record whether a property has been read or set.

How to implement your mocks

The first step is to implement your mocks using Mokka's helpers. The general approach is the same for all types of mocks: You declare a property for the mock and use that mock object inside your function implementations to record the calls to that function.

For the examples below, let's assume we want to mock the following protocol:

protocolEngine{func turnOn()func turnOff()varisOn:Bool{get}func setSpeed(to value:Float) // kilometers per hour
func setSpeed(to value:Float, in unit:UnitSpeed)func currentSpeed(in unit:UnitSpeed)->Double}

Functions without return value

For functions that don't return a value, use FunctionMock<Args>. This class has one generic parameter which defines the type(s) of the argument(s).

For functions with no arguments, that should be Void:

classEngineMock:Engine{letturnOnFunc=FunctionMock<Void>(name:"turnOn()")func turnOn(){
turnOnFunc.recordCall()}
// ...
}

Note: The name parameter in the mock initializers is optional. It is purely informational and might be useful for error messages (e.g. better assertion error messages). It is good practice to provide the names in the standard Swift #selector syntax.

For functions with a single argument, just use that argument's type:

classEngineMock:Engine{letsetSpeedFunc=FunctionMock<Float>(name:"setSpeed(to:)")func setSpeed(to value:Float){
setSpeedFunc.recordCall(value)}
// ...
}

For functions with more than one argument, you need to use a tuple to represent the arguments (because Swift does not (yet?) support variadic generic parameters). Although you don't have to, it is a good practice to name the tuple elements, which makes it much clearer when referring to them in your testing code.

classEngineMock:Engine{letsetSpeedInUnitFunc=FunctionMock<(value:Float, unit:UnitSpeed)>(name:"setSpeed(to:unit:)")func setSpeed(to value:Float, in unit:UnitSpeed){
setSpeedInUnitFunc.recordCall((value: value, unit: unit))}
// ...
}

Functions with return value

For functions with a return value, use ReturningFunctionMock<Args, ReturnValue>. This works very much the same way as FunctionMock, but adds a second generic parameter for the return value type. It also provides a recordCallAndReturn() method, instead of the recordCall() method.

classEngineMock:Engine{letcurrentSpeedFunc=ReturningFunctionMock<UnitSpeed,Double>(name:"currentSpeed(in:)")func currentSpeed(in unit:UnitSpeed)->Double{return currentSpeedFunc.recordCallAndReturn(unit)}
// ...
}

For the arguments of returning methods, the same rules apply as for non-returning functions (see above). For example:

  • A mock for a function that has no arguments and returns a Bool would be declared as ReturningFunctionMock<Void, Bool>
  • A mock for a function that has two arguments of type Int and String? and returns a Double would be declared as ReturningFunctionMock<(arg1: Int, arg2: String?), Double>

Properties

In many cases it is enough to just implement property requirements of the mocked protocol by declaring a stored property with a default value in your mock. However, when you want to be able to explicitly track whether a property has been read or written, Mokka's PropertyMock can be helpful. It is generic over the type of the property and its use in the mock implementation is quite self-explanatory: Instead of using a stored property, declare a computed property and delegate the getter and setter (if it's settable property) to the get() and set(_:) methods of the PropertyMock object:

classEngineMock:Engine{letisOnProperty=PropertyMock<Bool>(name:"isOn")varisOn:Bool{get{return isOnProperty.get()}set{ isOnProperty.set(newValue)}}
// ...
}

Note that you don't need to provide a default value for the property (the get() method fails with a preconditionFailure if there is no value).

Call count verification

A common use case for mocks is to verify if a method has been called, and sometimes specifically how often it has been called. For that, both FunctionMock and ReturningFunctionMock provide some properties:

  • called: Bool Returns whether the method has been called (once or more).
  • calledOnce: Bool Returns whether the method has been called exactly once.
  • callCount: Int The number of times the method has been called.
XCTAssertTrue(engineMock.setSpeedFunc.called)XCTAssertTrue(engineMock.setSpeedFunc.calledOnce)XCTAssertEqual(engineMock.setSpeedFunc.callCount,3)

Argument verification

In addition to verifying if a function has been called, you often also want to check the argument(s) with which the function has been called. You can do that via the arguments property:

XCTAssertEqual(engineMock.setSpeedInUnitFunc.arguments.value,100.0)XCTAssertEqual(engineMock.setSpeedInUnitFunc.arguments.unit,.kilometersPerHour)

(This requires that you follow the recommended practice of naming the tuple members, see above. If you don't, you have to access the arguments by their index, e.g. arguments.0.)

For single-argument functions (where there's no arguments tuple) you can also use the argument property, which looks a bit nicer:

XCTAssertEqual(engineMock.setSpeedFunc.argument,100.0)

Stubbing

Sometimes it's necessary to stub the behavior of a function, for example to introduce some important side-effects. One common example for this is calling a delegate method. You can do that by providing a closure that will be executed when the function is called. The closure will be provided with the function arguments:

letdelegate=FooDelegateMock()
someMock.myFunction.stub{ arg in
delegate.somethingHappened(with: arg)}

Faking the return value

For returning functions it's crucial to be able to fake the return value. Mokka provides 3 ways of doing that: Static return values, dynamic return values and conditional return values. Let's have a look at each of those.

Providing a static return value

For most cases it's sufficient to provide a simple static value that should be returned by the mock implementation:

engineMock.currentSpeedFunc.returns(100.0)

Providing a return value dynamically

Sometimes it's convenient to provide a return value that is dynamically generated, often depending on the function's arguments. You can do that by providing a closure:

engineMock.currentSpeedFunc.returns{ unit in
// always return 100 km/h, converted to the requested target unit
letkmhValue=Measurement(value:100, unit:UnitSpeed.kilometersPerHour)return kmhValue.converted(to: unit).value
}

Providing return values conditionally

Both, static and dynamic return values can also be provided conditionally:

engineMock.currentSpeedFunc.returns(100.00, when:{ $0 ==.kilometersPerHour })
engineMock.currentSpeedFunc.returns(62.137, when:{ $0 ==.milesPerHour })
engineMock.currentSpeedFunc.returns(0) // otherwise

Mocking properties

This is how you use properties that are backed by PropertyMock in your testing code:

someMock.fooProperty.value =10	// use value to access the underlying property value
// do something
XCTAssertTrue(someMock.fooProperty.hasBeenRead)XCTAssertFalse(someMock.fooProperty.hasBeenSet

Example

You can find a simple example project in MokkaExample.

It includes

  • a subject under test (Car)
  • two mocked protocols (Engine and Battery)

It is a minimal example, but it should be enough to get you started with the concepts of Mokka.

Author

Mokka has been created and is maintained by Daniel Rinser, @danielrinser.

License

Mokka is available under the MIT License.

About

A collection of helpers to make it easier to write testing mocks in Swift.

Topics

Resources

Stars

6 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages