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.
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.
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
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With