15

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);
}

この種のテスト (パラメーター化されていないテストと比べて) を使用すると、パラメーター値のみが異なるほぼ同一の一連の単体テストを作成することを回避できるため、費用対効果が大きくなります。

XCTest ベースのソリューションまたはそれを達成するための他の手段を探しています。最適なソリューションは、Xcode で各テスト ケース (パラメーター セット) を個別の単体テストとして報告する必要があるため、すべてのテスト ケースが失敗したか、一部のテスト ケースのみが失敗したかは明らかです。

4

6 に答える 6

14

パラメータ化されたものを使用する最良の方法は、XCTestCase サブクラスのプロパティを使用すること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))
    }
}
于 2018-04-29T19:54:20.237 に答える
11

関数のパラメーターはいたるところにあります。関数が乗算または除算を行っているかどうかはわかりません。ただし、1 つのテスト メソッドで複数のテスト ケースを実行できる 1 つの方法を次に示します。

この関数を考えると:

func multiply(_ a: Int, _ b: Int) -> Int {
    return a * b
}

複数のテスト ケースを含めることができます。

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)
        }
    }
}

最後の 2 つは失敗し、Xcode がそれらについて通知します。

于 2016-02-17T03:32:45.437 に答える
3

@DariusVソリューションが好きです。ただし、開発者が Xcode のサイドバーから直接テスト メソッドを実行すると、うまく処理されません。それは私にとって契約違反です。

私がやっていることは、かなり巧妙だと思います。

サブクラスのインスタンス計算プロパティとしてDictionaryof testValues(probs にはより適切な名前が必要です)を宣言します。XCTestCase次に、Dictionary期待される出力をキーイングする入力のリテラルを定義します。私の例では、 に作用する関数をテストするので、次Intのように定義しますtestValues

static var testRange: ClosedRange<Int> { 0...100 }

var testValues: [Int: Int] {
    let range = Self.testRange
    return [
        // Lower extreme
        Int.min: range.lowerBound,

        // Lower bound
        range.lowerBound - 1: range.lowerBound,
        range.lowerBound    : range.lowerBound,
        range.lowerBound + 1: range.lowerBound + 1,

        // Middle
        25: 25,
        50: 50,
        75: 75,

        // Upper bound
        range.upperBound - 1: range.upperBound - 1,
        range.upperBound    : range.upperBound,
        range.upperBound + 1: range.upperBound,

        // Upper extreme
        Int.max: range.upperBound
    ]
}

ここでは、エッジ ケースと境界ケースを非常に簡単に宣言します。同じことを達成するためのよりセマンティックな方法は、タプルの配列を使用することかもしれませんが、Swift の辞書リテラル構文は十分に細く、これが何をするかはわかっています。

さて、私のテスト メソッドには、単純なforループがあります。

/// The property wrapper I'm testing. This could be anything, but this works for example.
@Clamped(testRange) var number = 50

func testClamping() {
    let initial = number

    for (input, expected) in testValues {
        // Call the code I'm testing. (Computed properties act on assignment)
        number = input
        XCTAssertEqual(number, expected, "{number = \(input)}: `number` should be \(expected)")

        // Reset after each iteration.
        number = initial
    }
}

パラメータごとに実行するには、Xcode の通常の方法、または Linux で動作するその他の方法で XCTests を呼び出すだけです (私はそう思います)。このクラスのパラメータ化を取得するために、すべてのテスト クラスを実行する必要はありません。

それはきれいですね。たった数行のDRYコードですべての境界値と等価クラスをカバーしました!

失敗したケースの特定に関しては、各呼び出しはXCTAssert関数を介して実行されます。Xcode の規則に従って、考える必要がある何か問題がある場合にのみメッセージがスローされます。これらはサイドバーに表示されますが、同様のメッセージが混ざり合う傾向があります。ここでのメッセージ文字列は、失敗した入力とその結果の出力を識別し、混合を修正し、テスト フローを健全なチェリー アップル パイにします。(あなたは好きなようにフォーマットすることができます、バンプキン!あなたの正気を祝福するものは何でも。)

おいしい。

TL;DR

@Code Different回答の適応: 入力と出力の辞書を使用し、forループで実行します。

于 2020-02-10T18:40:52.113 に答える