Swift中的参数化单元测试

MDJ*_*MDJ 9 unit-testing parameterized-unit-test swift

有没有办法使用参数化单元测试,类似于使用NUnit框架在.Net中可以实现的.

[TestCase(12, 3, 4)]
[TestCase(12, 2, 6)]
[TestCase(12, 4, 3)]
public void DivideTest(int expectedResult, int a, int b)
{
  Assert.AreEqual(expectedResult, a / b);
}
Run Code Online (Sandbox Code Playgroud)

使用这种测试(与非参数化测试相比)可以让您避免编写一系列几乎相同的单元测试,而不仅仅是参数值.

我正在寻找基于XCTest的解决方案或其他一些方法来实现它.最佳解决方案应该在Xcode中将每个测试用例(参数集)报告为单独的单元测试,因此是否所有测试用例或仅有一些测试用例失败是明确的.

Dar*_*usV 10

使用参数化的最佳方法是使用XCTestCase子类的property defaultTestSuite。下一个明显的例子是:

import XCTest

class ParameterizedExampleTests: XCTestCase {

    //properties to save the test cases
    private var array: [Float]? = nil
    private var expectedResult: Float? = nil

    // This makes the magic: defaultTestSuite has the set of all the test methods in the current runtime
    // so here we will create objects of ParameterizedExampleTests to call all the class' tests methodos
    // with differents values to test
    override open class var defaultTestSuite: XCTestSuite {
        let testSuite = XCTestSuite(name: NSStringFromClass(self))
        addTestsWithArray([12, 3], expectedResult: 4, toTestSuite: testSuite)
        addTestsWithArray([12, 2], expectedResult: 6, toTestSuite: testSuite)
        addTestsWithArray([12, 4], expectedResult: 3, toTestSuite: testSuite)
        return testSuite
    }

    // This is just to create the new ParameterizedExampleTests instance to add it into testSuite
    private class func addTestsWithArray(_ array: [Float], expectedResult: Float, toTestSuite testSuite: XCTestSuite) {
        testInvocations.forEach { invocation in
            let testCase = ParameterizedExampleTests(invocation: invocation)
            testCase.array = array
            testCase.expectedResult = expectedResult
            testSuite.addTest(testCase)
        }
    }

    // Normally this function is into production code (e.g. class, struct, etc).
    func division(a: Float, b: Float) -> Float {
        return a/b
    }

    func testDivision() {
        XCTAssertEqual(self.expectedResult, division(a: array?[0] ?? 0, b: array?[1] ?? 0))
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 当然可以 - 但无法深入了解调用结果会大大降低此方法的实用性。 (5认同)
  • 不幸的是,该套件的测试仅显示为 Xcode 测试导航器的单个测试。有解决办法吗? (2认同)

Cod*_*ent 9

你的功能参数到处都是.我不确定你的功能是做乘法还是除法.但是,这是一种可以在单个测试方法中执行多个测试用例的方法.

鉴于此功能:

func multiply(_ a: Int, _ b: Int) -> Int {
    return a * b
}
Run Code Online (Sandbox Code Playgroud)

您可以在其上有多个测试用例:

class MyTest: XCTestCase {
    func testMultiply() {
        let cases = [(4,3,12), (2,4,8), (3,5,10), (4,6,20)]
        cases.forEach {
            XCTAssertEqual(multiply($0, $1), $2)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

最后两个会失败,Xcode会告诉你它们.

  • @PeterSchorn 测试用例的执行:是的。不执行该特定测试。如果情况不再如此,则行为可能会随着时间而改变。还; 不要将您个人对代码风格的看法视为法律;没有人喜欢苦毒的人。 (4认同)
  • 这样做的主要缺点是,当第一种情况失败时,执行将停止。在继续进行参数化测试的情况下,每个案例在报告中都被视为单独的测试,这使得查明哪个确切的组合失败很容易。 (2认同)

Sou*_*unt 9

处理参数化测试的最佳方法是使用XCTContext.runActivity. 这允许创建一个具有某种名称的新活动,该名称将帮助您准确识别哪些迭代失败。因此,对于您的划分场景:

func testDivision() {
    let testCases = [
        (a: 12, b: 3, result: 4),
        (a: 12, b: 2, result: 6),
        (a: 12, b: 6, result: 1),
        (a: 12, b: 4, result: 3),
    ]
    for (a, b, result) in testCases {
        XCTContext.runActivity(named: "Testing dividing \(a) by \(b) to get result \(result)") { activity in
            XCTAssertEqual(result, a/b)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

请注意,运行上述测试后,案例编号为。1、2 和 4 将成功,而情况 1。3会失败。您可以在测试报告中准确查看哪些测试活动失败以及哪些断言导致失败:

测试活动的成功和失败案例