共享XCTest单元测试接口的不同实现

时间:2016-01-21 16:07:55

标签: swift unit-testing xctest

我有一些接口(协议)的两个或更多实现:

protocol Interface {
    func methodOne()
    func methodTwo()
}

我想测试每个实现,但我不想复制代码。我有几个选择,但没有一个让我满意。

首先是为ImplementationA创建测试用例并将其子类化以获取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()
    }
}

这种方法的一个缺点是我无法进行仅适用于ImplementationA的测试。 (例如,测试一些特定于该实现的辅助方法)

我提出的第二个选项是为测试用例创建共享子类:

class InterfaceTests: XCTestCase {

    var implToTest: Interface!

    func testMethodOne() {
        ...
    }

    func testMethodTwo() {
        ...
    }
}

但是这些测试也将在这里执行,并且它们将失败,因为没有为implToTest分配实现。当然我可以为它分配一些实现,但接下来我将以相同实现的两个测试用例结束。最好的选择是以某种方式禁用InterfaceTests测试用例并仅运行其子类。有可能吗?

我得到的第三个想法可能看起来很棘手,但它会满足我的所有需求。不幸的是它没有用。 我决定创建InterfaceTestable协议:

protocol InterfaceTestable {
    var implToTest: Interface! { get set }
}

并使用所有共享测试对其进行扩展:

extension InterfaceTestable {

    func testMethodOne() {
        ...
    }

    func testMethodTwo() {
        ...
    }
}

然后为每个实现创建测试用例:

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
}

这些测试用例编译但Xcode没有看到在InterfaceTestable扩展名中声明的测试。

还有其他方法可以为不同的实现进行共享测试吗?

2 个答案:

答案 0 :(得分:3)

我遇到了同样的问题并使用您的第二个选项解决了它。 但是,我找到了一种方法来防止基类的测试用例运行:

重写基类中的defaultTestSuite()类方法以返回空XCTestSuite

class InterfaceTests: XCTestCase {

  var implToTest: Interface!

  override class func defaultTestSuite() -> XCTestSuite {
    return XCTestSuite(name: "InterfaceTests Excluded")
  }
}

这样就不会运行InterfaceTests的测试。不幸的是,也没有ImplementationATests的测试。通过覆盖defaultTestSuite()中的ImplementationATests,可以解决这个问题:

class ImplementationATests : XCTestCase {

  override func setUp() {
    super.setUp()
    implToTest = ImplementationA()
  }

  override class func defaultTestSuite() -> XCTestSuite {
    return XCTestSuite(forTestCaseClass: ImplementationATests.self)
  } 
}

现在ImplementationATests的测试套件将运行InterfaceTests的所有测试,但不会直接运行InterfaceTests的测试,而不会设置implToTest

答案 1 :(得分:1)

我之前使用共享基类的方式。使implToTest nillable。在基类中,如果没有提供实现,只需在保护条款中进行测试return

测试运行包括基类测试的报告,当它没有做任何事情时,这有点令人讨厌。但这是一个小烦恼。测试子类将提供有用的反馈。