Swiftpack.co - Package - danielsaidi/Mockery

Mockery

Mockery Icon
Version Swift 5.0 License Twitter: @danielsaidi

About Mockery

Mockery is a mocking library for Swift. Mockery lets you invoke method calls, register function results and inspect function invokations.

Example:

protocol Printer {
    func print(_ text: String)
}
 
// Inheriting `Mock`
class MockPrinter: Mock, Printer {
    func print(_ text: String) {
        invoke(print, arguments: (text))
    }
}

let printer = MockPrinter()
printer.print("Hello!")
let invokations = printer.invokations(of: printer.print)   
    // => 1 item
invokations[0].arguments.0
    // => "Hello!"
printer.hasInvoked(printer.print)
    // => true
printer.hasInvoked(printer.print, numberOfTimes: 1)
    // => true
printer.hasInvoked(printer.print, numberOfTimes: 2)
    // => false

Mockery supports mocking functions with void, optional and non-optional results. It supports values, structs, classes and enums and doesn't put any restrains on the code you write.

Installation

Swift Package Manager

https://github.com/danielsaidi/Mockery.git

CocoaPods

pod 'Mockery'

Carthage

github "danielsaidi/Mockery"

Demo App

This repository contains a demo app that invokes various mock functions. To try it out, open and run the Mockery.xcodeproj project.

Creating a mock

Consider that you have the following protocol:

protocol TestProtocol {
    
    func functionWithResult(arg1: String, arg2: Int) -> Int
    func functionWithOptionalResult(arg1: String, arg2: Int) -> Int?
    func functionWithVoidResult(arg: String)
}

To mock TestProtocol, just create a class that inherits Mock and implements TestProtocol:

class TestMock: Mock, TestProtocol {
    
    func functionWithResult(arg1: String, arg2: Int) -> Int {
        invoke(functionWithResult, args: (arg1, arg2))
    }

    func functionWithOptionalResult(arg1: String, arg2: Int) -> Int? {
        invoke(functionWithOptionalResult, args: (arg1, arg2))
    }
    
    func functionWithVoidResult(arg: String) {
        invoke(functionWithVoidResult, args: (arg))
    }
}

If your test class must inherit another class and therefore can't inherit Mock, you can let it implement Mockable instead. The rest is identical to using Mock.

The mock will now record the function calls and return any registered return values (or crash if you haven't registered any return values for non-optional functions).

Registering return values

You can register return values by calling registerResult(for:):

let mock = TestMock()
mock.registerResult(for: mock.functionWithIntResult) { _ in return 123 }

The result block takes the same arguments as the actual function, so you can adjust the function logic depending on the input arguments.

You don't have to register a return value for optional result functions. However, non-optional functions will crash if you fail to register a result before calling them.

Inspecting invokations

Use invokations(of:) to inspect a mock's performed invokations. An invokation reference contains information on how many times the function was executed, with which input arguments and the result:

_ = mock.functionWithResult(arg1: "abc", arg2: 123)
_ = mock.functionWithResult(arg1: "abc", arg2: 456)

let inv = mock.invokations(of: mock.functionWithResult)
expect(inv.count).to(equal(2))
expect(inv[0].arguments.0).to(equal("abc"))
expect(inv[0].arguments.1).to(equal(123))
expect(inv[1].arguments.0).to(equal("abc"))
expect(inv[1].arguments.1).to(equal(456))

The syntax above uses Quick/Nimble. You can use any test framework you like.

Registering and throwing errors

There is currently no support for registering and throwing errors, which means that async functions can't (yet) register custom return values.

Device limitations

Mockery uses unsafe bit casts to get the memory address of mocked functions. This only works on 64-bit devices, which means that mock-based unit tests will not work on old devices or simulators like iPad 2, iPad Retina etc.

Contact me

Feel free to reach out if you have questions or if you want to contribute in any way:

Acknowledgements

Mockery is inspired by Stubber, and would not have been possible without it. However, Stubber uses global functions, which requires you to reset the global state every now and then. Mockery moves this logic to each mock, which means that any recorded exeuctions are automatically reset when the mock is disposed. Mockery also adds some extra functionality, like support for optional and void results and convenient inspection utilities.

License

Mockery is available under the MIT license. See the LICENSE file for more info.

Github

link
Stars: 4
Help us keep the lights on

Dependencies