在XCTestCase子类中使用泛型是否有效?

时间:2016-02-08 15:47:21

标签: ios swift generics xctest

我有一个XCTestCase子类,看起来像这样。为简洁起见,我删除了setup()tearDown方法:

class ViewControllerTests <T : UIViewController>: XCTestCase {
    var viewController : T!

    final func loadControllerWithNibName(string:String) {
        viewController  = T(nibName: string, bundle: NSBundle(forClass: ViewControllerTests.self)) 
        if #available(iOS 9.0, *) {
            viewController.loadViewIfNeeded()
        } else {
            viewController.view.alpha = 1
        }
    }
}

它的子类看起来像这样:

class WelcomeViewControllerTests : ViewControllerTests<WelcomeViewController> {
    override func setUp() {
        super.setUp()
        self.loadControllerWithNibName("welcomeViewController")
        // Put setup code here. This method is called before the invocation of each test method in the class.
    }

    func testName() {
       let value =  self.viewController.firstNameTextField.text
        if value == "" {
            XCTFail()
        }
    }
}

理论上,这应该按预期工作 - 编译器不会抱怨任何事情。但是,当我运行测试用例时,setup()方法甚至无法被调用。但是,它表示当明确的testName()方法失败时,测试已经过去了。

泛型的使用是一个问题吗?我可以很容易地想到许多非泛型方法,但我非常想写这样的测试用例。这是XCTest在Objective-C和Swift之间的互操作性问题吗?

4 个答案:

答案 0 :(得分:7)

XCTestCase使用Objective-C运行时加载测试类并查找测试方法等。

Swift泛型类与Objective-C不兼容。见https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/BuildingCocoaApps/InteractingWithObjective-CAPIs.html#//apple_ref/doc/uid/TP40014216-CH4-ID53

  

当您创建一个源自Objective-C类的Swift类时,可以从Objective-C自动获得与Objective-C兼容的类及其成员属性,方法,下标和初始化程序。这不包括仅限Swift的功能,例如此处列出的功能:

     
      
  • 泛型
  •   
     

...

Ergo XCTest无法使用您的通用XCTestCase子类。

答案 1 :(得分:4)

嗯,实际上它是完全可行的。你只需要创建一个类似于测试运行器的类。例如:

class TestRunner: XCTestCase {
    override class func initialize() {
        super.initialize()
        let case = XCTestSuite(forTestCaseClass: ViewControllerTests<WelcomeViewController>.self)
        case.runTest()
    }
}

这样您就可以运行所有通用测试。

答案 2 :(得分:1)

此方法(https://stackoverflow.com/a/39101121/311889)不再有效。我在Swift 5.2中做到的方式是:

class MyFileTestCase: XCTestCase {
    override func run() {
        let suite = XCTestSuite(forTestCaseClass: FileTestCase<MyFile>.self)
        suite.run()
        super.run()
    }

    // At least one func is needed for `run` to be called
    func testDummy() { }
}

答案 3 :(得分:0)

我使它以一种带有协议和相关类型的hacky方式工作:

import XCTest

// If your xCode doesn't compile on this line, download the lastest toolchain as of 30 november 2018
// or change to where Self: XCTestCase, and do it also in the protocol extension.
protocol MyGenericTestProtocol: XCTestCase {
    associatedtype SomeType

    func testCallRunAllTests()
}

extension MyGenericTestProtocol {
// You are free to use the associated type here in any way you want.
// You can constraint the associated type to be of a specific kind
// and than you can run your generic tests in this protocol extension!
    func startAllTests() {
        for _ in 0...100 {
            testPrintType()
        }
    }

    func testPrintType() {
        print(SomeType.self)
    }
}

class SomeGenericTestInt: XCTestCase, MyGenericTestProtocol {
    typealias SomeType = Int

    func testCallRunAllTests() {
        startAllTests()
    }
}

class SomeGenericTestString: XCTestCase, MyGenericTestProtocol {
    typealias SomeType = String

    func testCallRunAllTests() {
        startAllTests()
    }
}