Thursday, July 8, 2021

Generic Test Classes in Xcode 12.5

Jon Reid:

Once or twice in my career, I’ve written an abstract test suite as a superclass, where the subclasses provide some sort of factory method. Then all the test cases in that suite get repeated for the specific instances.

I do this all the time. It’s useful when I’m making a new, optimized version of a class and want to make sure it passes all the same tests as the original. And sometimes I need multiple implementations because certain APIs are broken on certain versions of the operating system.

Maybe you’ve wanted to repeat the same tests for a different object. For example, you may want to run the same test cases over different types that implement a protocol. Maybe you tried defining a generic XCTestCase, hoping to reuse the test suite across a few types. If you tried this before, you know that it didn’t work. XCTest uses introspection and follows specific rules to gather test cases and test suites. It didn’t pick up anything generic.

But Xcode 12.5 adds support for generic test suites. Here’s how it works. Write a generic subclass of XCTestCase.

[…]

And unlike the days when I did this with a quasi-abstract base class, XCTest will not run any tests for the top-level generic suite.

This is great. Also, note that in simpler cases his example with a factory method:

class AbstractSuite<T>: XCTestCase { … }
class OneClassTests: AbstractSuite<OneClass> {
    override func makeSpecificObject() -> OneClass? {
        /* Make instance of OneClass*/
    }
}

is more than you need. I often want to test a family of classes that all follow the same interface I. Then you can just do:

class AbstractSuite<T: I>: XCTestCase { … }
class OneClassTests: AbstractSuite<OneClass> {}

and have the base class call T() directly. The subclasses don’t need any code.

Previously:

Comments RSS · Twitter

Leave a Comment