Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Parametrized unit tests in Swift

Is there any way to use parameterized unit tests, similar to what you can achieve in .Net using NUnit framework.

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

Using this kind of tests (vs non-parameterized ones) can give you bigger back for buck by allowing you to avoid writing series of almost identical unit tests differing only by parameter values.

I am looking for either XCTest-based solution or some other means to achieve it. Optimal solution should report each test case (parameter set) as a separate unit test in Xcode, so is it clear whether all or only some of the tests cases failed.

like image 364
Maciej Jastrzebski Avatar asked Feb 16 '16 19:02

Maciej Jastrzebski


2 Answers

The best way to use parametrized is using the XCTestCase subclass's property defaultTestSuite. A clearly example with devision is the next:

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))
    }
}
like image 167
DariusV Avatar answered Oct 07 '22 19:10

DariusV


You function parameters are all over the place. I'm not sure if your function is doing multiplication or division. But here's one way you can do multiple test cases in a single test method.

Given this function:

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

You can have multiple test cases on it:

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

The last two would fail and Xcode will tell you about them.

like image 32
Code Different Avatar answered Oct 07 '22 19:10

Code Different