Easier, dynamic mocking for Swift.

Build Status codecov

Why Scout?

Let's say we have a TestSubject that depends on the Example protocol.

protocol Example {
    var foo: String { get }

    func baz()

class TestSubject {
    let example: Example

    init(example: Example) {
        self.example = example

    func doAThing() {
        if == "baz" {

If you've done unit testing in Swift, you're probably all too familiar with this dance:

class ManualMockExample: Example {
    var foo: String

    var bazWasCalled: Bool = false

    init(foo: String) { = foo

    func baz() {
        bazWasCalled = true

class ManualMockExampleTests : XCTestCase {
    func testBoilerplateIsTedious() {
        let manualMock = ManualMockExample(foo: "baz")
        let testSubject = TestSubject(example: manualMock)



These <func>WasCalled flags and var stubs are likely duplicated in every mock you write. If you need to throw an exception, invoke a completion block, or anything more complicated, your mocks get even more convoluted. Making matters worse, none of this "mock functionality" is easy to reuse across tests.

Scout aims to remove all of this boilerplate, and in doing so, make tests easier to both read and write. This is done using a declarative, functional, and dynamic API for creating and configuring mocks.


  • Swift 5 or greater


Swift Package

Add this repo to your package's dependencies, similar to this repo's Example project manifest.


There's a workaround for integrating Swift packages using Carthage. TL;DR;

  • Add the project to your Cartfile
  • Use Carthage to checkout the project
  • Generate Scout's Xcode project: cd Carthage/Checkouts/Scout && swift package generate-xcodeproj
  • Follow Carthage's instructions to integrate Scout into your project

Getting Started

I recommend starting with Scout.playground for a narrative, interactive guide. See Usage for code examples and API documentation.



Mock is the entry point for all other APIs. It's meant to be embedded in a protocol-conformant mock class, like so:

protocol Example {
    var foo: String { get }

    func baz()

class MockExample : Example, Mockable {
    let mock = Mock()
    var foo: String {
        get {
    func baz() {

This example demonstrates the two APIs meant for use within a mock class:


Returns a @dynamicMemberLookup proxy that retrieves the next expectation for the var that's accessed. For example, to get the next expectation for the var foo:

protocol GetExample {
    var foo: String
class MockGetExample : GetExample, Mockable {
    let mock = Mock()
    var foo: String {
        get {

The dynamic member proxy is generic, meaning it uses type inference to determine that foo should be a String.


Returns a @dynamicCallable proxy that will retrieve an expectation for the called function.

protocol CallExample {
    func baz(buz: Int) -> Int
class MockCallExample : CallExample, Mockable {
    let mock = Mock()

    func baz(buz: Int) {
        return try! buz) as! Int

Make sure you pass all arguments from the wrapper class to the Mock.

Since call is declared as throws (to support expecting an error), you'll need to add try! if it's being used in a function that isn't declared throws.

As of Swift 5, call can't be made generic. Until Swift supports generic @dynamicCallable types, you'll need to force-cast from Any? to the expected return type.


Returns a dynamic DSL object which configures the behavior of calls to mock.get.<var> and<func>.

Expecting Var Gets

Simply access the desired var, then call to with the desired expectation:`return`("baz")) // returns "baz"

If there aren't any expectations when foo is called, Mock will fail the test. If there are still expectations left when verify() is called, Mock will fail the test.

Expecting Function Calls

Similar to var expectations, call the desired function, followed by .to() with the desired expectation as an argument:

mockCallExample.expect.baz(buz: equalTo(3)).to(`return`(4))
mockCallExample.baz(3) // returns 4

If baz was called with something other than 3, Mock would have failed the test. Also, if baz is called when no calls were expected, Mock will fail the test. As shown above, you'll need to specify an ArgMatcher when expecting a call to a function with arguments.

Expecting Function Arguments

See ArgMatcher for a list of available argument matchers. The two simplest are:

equalTo(value): checks that the argument is equal to the specified value.

any(): accepts any argument.

If an argument fails to satisfy the specified matcher, Mock will fail the test.


Once you've retrieved a var or called a function on expect, you need to set an expectation using the to() method:

mockCallExample.expect.baz(buz: equalTo(3)).to(`return`(4))

In this case, the expectation is to "return 4." What you can expect varies based on whether you're expecting a var or function call:

Var Expectations

ExpectVarDSL is used to expose the expectation DSL for var access. The to method only takes one form, and it accepts Expectation instances returned by any of the factory functions:

  • return: Return a single value one or more times (aliased as returnValue for the backtick averse).
  • alwaysReturn: Like return, but always.
  • get: Return a value from a closure.

Function Expectations

ExpectFuncDSL provides the expectation-setting DSL for function calls. It has two different signatures:

The var expectations DSL:`return`("foo"))

And another that accepts function-specific exepctations, which are just functions with the FuncExpectationBlock signature. You can write your own:

func incrementBy(_ amount: Int) -> FuncExpectationBlock {
    return { (args: KeyValuePairs<String, Any?>) in
        return args.first as! Int + amount

This especially comes in handy when you have more advanced behaviors to expect, like calling a completion block.

There's also a throw expectation for when you want your mock function to throw an error:`throw`(SomeError())

Once you've set expectations on your mock, you'll need to verify that they're met.


At the bottom of a test method, you should call verify() on your mock class if you want to assert that all of its expectations were met.

func testCallsBazIfFooIsBaz() {`return`("baz"))



In this example, the test will fail if bazFunc doesn't call mockExample.baz().

Any expectations added using toAlways won't fail the test if they aren't called.


Once you've set up a Mockable class, you can use some of Mock's methods on it courtesy of the protocol extension which exposes some methods of Mock on the class it's embedded in for convenience. This prevents you from having to type .mock.expect... in all your tests.


Tests using mocks with Scout should set continueAfterFailure to false, otherwise the tests could crash due to unwrapping an unexpected nil error.

There are a few things that @dynamicCallable can't do:

  • Generic return types. Workaround is to use Any and force-cast.
  • inout parameters. Workaround is to declare your mock class using inout and do a non-inout call on the mock (see ExampleProject tests for an example).


  • Swift Tools 5.0.0
View More Packages from this Author


  • None
Last updated: Mon Mar 25 2024 00:50:16 GMT-0900 (Hawaii-Aleutian Daylight Time)