Parameterized Unit Tests in Swift

Is it possible to use parameterized unit tests similar to what you can achieve in .Net using the 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 such tests (unlike non-parameterized ones) can give you a big return for the dollar, avoiding writing a series of almost identical unit tests that differ only in parameter values.

I am looking for either a solution based on XCTest, or some other means to achieve it. The optimal solution is to report each test case (set of parameters) as a separate unit test in Xcode, so it is clear whether all or only some of the tests were unsuccessful.

+8
source share
5 answers

You work with parameters everywhere. I'm not sure if your function does multiplication or division. But here in one way you can do several test cases in one testing method.

Given this function:

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

You can have several test cases:

 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 will fail, and Xcode will tell you about them.

+9
source

The best way to use a parameterized method is to use the subclass property of XCTestCase defaultTestSuite . A vivid development example is as follows:

 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 (eg 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)) } } 
+9
source

@Code Various answers are legal. Here are two other options, or rather, workarounds :

Property Based Testing

You can use a tool like Fox to do generative testing, where the test structure will generate many input sets for the behavior that you want to test and run for you.

More on this approach:

Common BDD Examples

If you like the BDD style and use a test environment that supports them, you can use common examples .

Using Quick , it will look like this:

 class MultiplySharedExamplesConfiguration: QuickConfiguration { override class func configure(configuration: Configuration) { sharedExamples("something edible") { (sharedExampleContext: SharedExampleContext) in it("multiplies two numbers") { let a = sharedExampleContext()["a"] let b = sharedExampleContext()["b"] let expectedResult = sharedExampleContext()["b"] expect(multiply(a, b)) == expectedResult } } } } class MultiplicationSpec: QuickSpec { override func spec() { itBehavesLike("it multiplies two numbers") { ["a": 2, "b": 3, "result": 6] } itBehavesLike("it multiplies two numbers") { ["a": 2, "b": 4, "result": 8] } itBehavesLike("it multiplies two numbers") { ["a": 3, "b": 3, "result": 9] } } } 

Honestly, this option: 1) a lot of work, 2) improper use of the general example method, since you do not use them to test the behavior shared by several classes, but rather to parameterize the test. But, as I said at the beginning, this is a more workaround.

+2
source

All statements seem throw , so maybe something like this will work for you:

 typealias Division = (dividend: Int, divisor: Int, quotient: Int) func testDivisions() { XCTAssertNoThrow(try divisionTest((12, 3, 4))) XCTAssertNoThrow(try divisionTest((12, 2, 6))) XCTAssertNoThrow(try divisionTest((12, 4, 3))) } private func divisionTest(_ division: Division) throws { XCTAssertEqual(division.dividend / division.divisor, division.quotient) } 

If a failure occurs, the entire function will fail. If more detail is required, each case can be divided into separate functions.

0
source

We found that this solution How to dynamically add XCTestCase offers us the flexibility we need. The ability to dynamically add tests, as well as have dynamic test names in the test report.

0
source

Source: https://habr.com/ru/post/1243172/


All Articles