56

How do you mock an object in Swift?

The Mirror protocol sounded promising, but it doesn't do much right now.

So far the only approach I found is to subclass and override all methods of the mocked class. This is of course not a true mock, far from ideal, and a lot of work.

Any other ideas?

Why not OCMock?

From the source:

Can I use OCMock using the language bridge functionality?

Yes, but with limitations. If you are brave. As of now this is highly experimental. There's no guarantee that OCMock will ever fully support Swift.

Known limitations:

  • Tests have to be written in Objective-C
  • Objects that should be mocked must inherit from NSObject
  • No stubbing/expecting/verifying of class methods
Community
  • 1
  • 1
hpique
  • 119,096
  • 131
  • 338
  • 476
  • I still don't know how reflection works in Swift. Maybe we need to wait until Apple release more documentation and more Swift features. (and Swift is much more static than ObjC, which can make mocking harder due to static dispatch, `@final` method etc) – Bryan Chen Jun 11 '14 at 23:40
  • Why not use OCMock? Just import the framework in the Bridging Header file. Thats what I do. – Alex Reynolds Jun 13 '14 at 01:58
  • 1
    @AlexReynolds I love OCMock but (understandably) it has serious limitations with Swift. I updated the question with more info about it. – hpique Jun 13 '14 at 17:20
  • There's no real limitations if you are using OBJc as well. I'm using ocmock in a swift xctest. You can fix the only limitation I know if with XCTAssertNoThrow for mock verify by my answer here http://stackoverflow.com/questions/24049735/xcode-6-swift-and-mock-verification-without-exceptions/24065712#24065712 – Alex Reynolds Jun 13 '14 at 17:29
  • 8
    @AlexReynolds OCMock is designed for ObjC. It is not for Swift. We now write Swift code like ObjC code because we used to ObjC, but this is not the case in future. We will use lots Swift struct/enum and OCMock can't deal with them. The only solution is to implement some Swift mock library for Swift. But we don't know how to mock non-ObjC related code in Swift yet. – Bryan Chen Jun 14 '14 at 02:34
  • @BryanChen the question is too vague. Mocking can be done in swift using OCMock. I'd be happy to provide examples if you wish. Maybe hpique can include an example of something he is trying to mocj – Alex Reynolds Jun 16 '14 at 23:48
  • @AlexReynolds for [this code](https://gist.github.com/xlc/c5fc00684416c919007e) try mock `Point3DGenerator` to test `generateThree` – Bryan Chen Jun 16 '14 at 23:57
  • http://nshipster.com/xctestcase/ Maybe it will be useful for you. There is a note about way of mocking in Swift. – Alex Jul 22 '14 at 00:26
  • Taking in consideration the limited reflection capabilities in Swift I am more and more thinking that a possible solution for mocking "pure" Swift classes could be based on code generation... – e1985 Dec 21 '14 at 15:58

6 Answers6

30

NSHipster touches on language features in Swift which make an external mocking library less necessary:

In Swift, classes can be declared within the definition of a function, allowing for mock objects to be extremely self-contained. Just declare a mock inner-class, override and [sic] necessary methods:

func testFetchRequestWithMockedManagedObjectContext() {
    class MockNSManagedObjectContext: NSManagedObjectContext {
        override func executeFetchRequest(request: NSFetchRequest!, error: AutoreleasingUnsafePointer<NSError?>) -> [AnyObject]! {
            return [["name": "Johnny Appleseed", "email": "johnny@apple.com"]]
        }
    }

    ...
}

The ability to create a subclass of your external dependency in the local scope plus the addition of XCTestExpectation solve a lot of the same problems as OCMock.

The one thing that a library such as OCMock provides that is very useful are its "verify" methods to ensure that the mock classes were called. It's possible to manually add this, but the automatic addition is nice.

fabian789
  • 8,348
  • 4
  • 45
  • 91
Drew Beaupre
  • 2,437
  • 1
  • 17
  • 17
  • This approach currently doesn't work, my Xcode is crashing with "SourceKitService Crashed". – Rodrigo Ruiz Sep 26 '14 at 05:42
  • @Rodrigo is it fixed in 6.1.1? – fabian789 Nov 28 '14 at 16:19
  • Haven't tried, I just created a different file for each mock – Rodrigo Ruiz Nov 29 '14 at 09:34
  • That doesn't work if the class you're trying to stub is declared as final. – fbernardo Dec 05 '14 at 16:51
  • 4
    @hris.to I would argue that unit tests should not have access to private methods. Ideally, you'll be using Inversion of Control where you can are injecting a mock object. Since your code will only ever be accessing public methods on that mock object, the private methods are irrelevant. http://stackoverflow.com/questions/3058/what-is-inversion-of-control (edit: hit enter too soon. Wanted to add link to IoC info) – Drew Beaupre May 28 '15 at 18:19
  • 1
    Well you are right theoritically speaking. However there are some situations where you need access to private variables(or methods). For example if you had a public method which sets an internal(private) flag based on input. You can't know wether the method works correctly based on the input if you don't had access to this flag to inspect it's state. – hris.to May 29 '15 at 05:58
  • I haven't tried it yet, but xcode7 brings a new @testable attribute which allows your tests to access internal methods – Drew Beaupre Jun 12 '15 at 16:08
  • @testable gives access to internal methods, which is default access level. They cannot help in any way with private methods/properties. They are great however, that you can now test your code without making it either public or part of 'Test' target. – hris.to Aug 19 '15 at 13:35
  • 2
    @hris.to I respectfully disagree about exposing internal functionality ;-) The class you're testing is designed to work as part of a larger system, so everything it does will cause an effect which is somehow observable by other components - and therefore somehow observable by the test. (If what you're doing cannot be observed externally, then why are you writing the code?!) – Matthew Sep 10 '15 at 20:53
11

I create my mock classes by wrapping everything in a protocol. I hand roll a mock class to conform to the protocol in question, like so:

protocol Dog: class {
    var name: String { get }

    func bark()
}

class DogImpl: Dog {
    var name: String

    init(name: String) {
        self.name = name
    }

    func bark() {
        print("Bark!")
    }
}

class DogMock: Dog {
    var name = "Mock Dog"
    var didBark = false

    func bark() {
        didBark = true
    }
}

I use this in conjunction with dependency injection to achieve full test coverage. It's a lot of boilerplate, but I haven't had any issues with this approach so far.

Regarding subclass mocking, you'll run into trouble with final classes, or if they have non-trivial initializers.

Mark
  • 7,167
  • 4
  • 44
  • 68
2

I want to point something in addition to marked answer - I do not know whether it's a bug or not.

If you subclass NSObject somehow(in my case I was subclassing UIView which internally subclass NSObject) you need to declare overriden function explicity with @objc otherwise your test wont compile. In my case the compiler itself crashes with following:

Segmentation Fault: 11

So the following class:

public class ClassA: UIView
{
    @objc public func johnAppleseed() {

    }
}

Should be unit tested the following way:

class ClassATests: XCTestCase {

    func testExample()
    {
        class ClassAChildren: ClassA
        {
            @objc private override func johnAppleseed() {

            }
        }
    }
}
hris.to
  • 6,235
  • 3
  • 46
  • 55
1

You can achieve this kind of mocking with MockFive.

The gist of it is that you have to create a mock 'by hand' of the source class that you want to mock--

but then, you can stub its methods dynamically, so you can use it wherever you need and configure its behavior there.

I wrote an article about how to use it here.

Swati
  • 2,870
  • 7
  • 45
  • 87
1

I recommend using Cuckoo, which can handle most standard mocking tasks.

Example Classes:

class ExampleObject {

    var number: Int = 0

    func evaluate(number: Int) -> Bool {
        return self.number == number
    }

}

class ExampleChecker {

    func check(object: ExampleObject) -> Bool {
        return object.evaluate(5)
    }

}

Example Test:

@testable import App
import Cuckoo
import XCTest

class ExampleCheckerTests: XCTestCase {

    func testCheck() {
        // 1. Arrange
        let object = MockExampleObject().spy(on: ExampleObject())
        stub(object) { object in
            when(object.evaluate(any())).thenDoNothing()
        }
        let checker = ExampleChecker()

        // 2. Action
        checker.check(object)

        // 3. Assert
        _ = verify(object).number.get
        verify(object).evaluate(any())
        verifyNoMoreInteractions(object)
    }

}
sundance
  • 2,930
  • 1
  • 20
  • 25
  • 1
    Pretty neat. How about system libraries? Can it mock `NEVPNConnection` in `NetworkExtension`? – Houman Mar 20 '18 at 19:25
0

Because of the limitations you wrote, OCMock does not work very well in Swift (as every mocking framework strongly dependent on the runtime).

There are several mocking frameworks for Swift though, from semi-manual to almost fully automatic mock generation. Some of them are already listed in the answers, so I'll just recommend another one, which I'm one of authors.

https://github.com/MakeAWishFoundation/SwiftyMocky

I won't go much into details, it has its minor limitations, but from what I see it has widest set of features from Swift frameworks (at least the ones I know), including generics support, @objc members, and updating mocks while you write/change protocols (watcher mode).

Andrzej Michnia
  • 538
  • 3
  • 9