I have two or more implementations of some interface (protocol):
protocol Interface {
func methodOne()
func methodTwo()
}
I want to test each implementation and I don't want to duplicate code. I have couple of options, but none of them satisfies me.
First one is to create test case for ImplementationA
and subclass it to get test case for ImplementationB
:
class ImplementationATests: XCTestCase {
var implToTest: Interface!
override func setUp() {
super.setUp()
implToTest = ImplementationA()
}
func testMethodOne() {
...
}
func testMethodTwo() {
...
}
}
class ImplementationBTests: ImplementationATests {
override func setUp() {
super.setUp()
implToTest = ImplementationB()
}
}
One of the drawbacks of this method is that I can't have tests which apply only for ImplementationA
. (e.g. to test some helper method specific to that implementation)
Second option I came up with is creating shared subclass for test cases:
class InterfaceTests: XCTestCase {
var implToTest: Interface!
func testMethodOne() {
...
}
func testMethodTwo() {
...
}
}
But here those tests will be also executed, and they will fail, because no implementation is assigned to implToTest
. Of course I can assign some implementation to it, but then I will end with two test cases for the same implementation. The best option would be to somehow disable InterfaceTests
test case and run only its subclasses. Is it possible?
Third idea I got may seem tricky, but it would satisfy all my needs. Unfortunately it doesn't work.
I decided to create InterfaceTestable
protocol:
protocol InterfaceTestable {
var implToTest: Interface! { get set }
}
and make extension to it with all shared tests:
extension InterfaceTestable {
func testMethodOne() {
...
}
func testMethodTwo() {
...
}
}
and then create test cases for each implementation:
class ImplementationATests: XCTestCase, InterfaceTestable {
var implToTest: Interface!
override func setUp() {
super.setUp()
implToTest = ImplementationA()
}
// some tests which only apply to ImplementationA
}
class ImplementationBTests: XCTestCase, InterfaceTestable {
var implToTest: Interface!
override func setUp() {
super.setUp()
implToTest = ImplementationB()
}
// some tests which only apply to ImplementationB
}
Those test cases compile but Xcode doesn't see tests declared in InterfaceTestable
extension.
Is there any other way to have shared tests for different implementations?
I came across the same problem and solved it using your second option. However, I found a way to prevent the test cases from the base class from running:
Override the defaultTestSuite()
class method in your base class to return an empty XCTestSuite
:
class InterfaceTests: XCTestCase {
var implToTest: Interface!
override class func defaultTestSuite() -> XCTestSuite {
return XCTestSuite(name: "InterfaceTests Excluded")
}
}
With this no tests from InterfaceTests
are run. Unfortunately also no tests of ImplementationATests
either. By overriding defaultTestSuite()
in ImplementationATests
this can be solved:
class ImplementationATests : XCTestCase {
override func setUp() {
super.setUp()
implToTest = ImplementationA()
}
override class func defaultTestSuite() -> XCTestSuite {
return XCTestSuite(forTestCaseClass: ImplementationATests.self)
}
}
Now the test suite of ImplementationATests
will run all test from InterfaceTests
, but no tests from InterfaceTests
are run directly, without setting implToTest
.