Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

swift mocking using final

To test swift objects, from what I've read, we sub-class them and mock the methods we want return our test values. I then watched a WWDC video about swift performance and the presenters suggested marking classes as final to help the compiler decide how to call methods and from the example I saw adding final can help.

The issues I'm having how can we mark classes as final but still enable sub-class mocking? Has anyone actually run into this problem or should I drop the final keyword from my declaration?

Any suggestions would be awesome or if there are none tell me I'm not doing it right.

Thanks, Mike.

like image 842
MikeH Avatar asked Nov 02 '15 21:11

MikeH


2 Answers

The compiler uses static dispatch when you mark something as final instead of dynamic dispatch. This has performance benefits that are pretty significant. This is also directly contrary to being able to mock something, as the whole point of static dispatch is that it doesn't have to figure out what version of the method to call.

Unfortunately you cannot have it both ways. You can either not mark it as final and so you can provide a subclass that is a mock, or you can mark it final to make it statically dispatched for performance.

like image 134
Charles A. Avatar answered Nov 05 '22 20:11

Charles A.


I know this thread is a bit old but thought I would comment anyway. Your other option would be to go Protocol oriented. Define your public interface to your final class in a protocol and make the final class implement that protocol. Then all you have to do is mock the protocol and your class stays final. This gives you your static dispatching and your mocks. I do not know for sure if this is the best option per se but it is the one that I have been using in our framework building so that we can test our framework code while providing an optimized binary to the consuming apps.

internal protocol MockableProtocol {
    func deleteItem(_ itemId: String) -> Bool
    func fetchAllItems() -> [CustomObject]
    func fetchItem(for id: String) -> CustomObject?
}

internal final class MyFinalClass: MockableProtocol {
    func deleteItem(_ itemId: String) -> Bool {
        // Your code here
    }

    func fetchAllItems() -> [CustomObject] {
        // Your code here
    }

    func fetchItem(for id: String) -> CustomObject? {
        // Your code here
    }
}

Then in testing:

class TestMockClass: MockableProtocol {
    func deleteItem(_ itemId: String) -> Bool {
        // Your code here
    }

    func fetchAllItems() -> [CustomObject] {
        // Your code here
    }

    func fetchItem(for id: String) -> CustomObject? {
        // Your code here
    }
}
like image 38
Sepui Avatar answered Nov 05 '22 18:11

Sepui