Parametrized Unit Tests in Swift

Parametrized unit tests in Swift

The best way to handle parameterized testing is to use XCTContext.runActivity. This allows to create a new activity with some name that will help you identify exactly which of the iterations failed. So for your division scenario:

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

Note that after running the above test, case no. 1, 2 and 4 will succeed while case no. 3 will fail. You can view exactly which test activity failed and which of the assertions caused faiure in test report:

Test activity with success and failure cases

Is it possible to use parameterised test case data with Xcode's XCTest Framework?

It does not look like XCTest has this built in, but there is project on GitHub aims to add this functionaltiy.

From their ReadMe:

KNMParametrizedTest adds support for parametrized test cases using the XCTest framework. Here is an example:


KNMParametersFor(testExample, @[ @"Hello", @"World" ])
- (void)testExample:(NSString *)word
{
NSString *result = [myUppercaser uppercaseString:word];
XCTAssertEqualObjects(result, [word uppercaseString],
@"Uppercaser failed for word %@", word);
}

It looks like the easiest way to install this is through CocoaPods.

Assign method (function of an object) to a variable or how to pass a parameter to unit tests

It turns out that for each instance method a type has, there's a
corresponding static method that lets you retrieve that instance
method as a closure, by passing an instance as an argument.

For example: [3, 2, 1].sort() == Array.sort([3, 2, 1])

Interesting fact, but pretty useless in our case, because

Partial application of 'mutating' method is not allowed.

So as long as I can't pass a mutating method as a parameter, I will pass an enumeration object:

extension Array where Element == Int {

mutating func sort(_ algorithm: SortAlgorithm) {
switch algorithm {
case .bubble: bubbleSort()
case .selection: selectionSort()
}
}
}

enum SortAlgorithm {
case bubble
case selection
}

As for second part of the question I think the best answer is inheritance:

// MARK: - Basic UnitTest Class

class SortingTest: XCTestCase {
static var algorithm: SortAlgorithm!

override static func tearDown() {
algorithm = nil
super.tearDown()
}

func testSorting1() {
var a = [9, 2, 5, 3, 8, 1, 4, 7, 6]
a.sort(Self.algorithm)
XCTAssertEqual(a, [1, 2, 3, 4, 5, 6, 7, 8, 9])
}

func testSorting2() {
var a = [4, 3, 2, 1]
a.sort(Self.algorithm)
XCTAssertEqual(a, [1, 2, 3, 4])
}
......

// MARK: - Inherited UnitTest Classes

class BubbleTest: SortingTest {

override static func setUp() {
super.setUp()
algorithm = .bubble
}
}

class SelectionTest: SortingTest {

override static func setUp() {
super.setUp()
algorithm = .selection
}
}

Everything is ready for nicely calling tests for appropriate sorting algorithm:

extension enum SortAlgorithm {

func runTests() {
switch self {
case .bubble: BubbleTest.defaultTestSuite.run()
case .selection: SelectionTest.defaultTestSuite.run()
}
}
}

SortAlgorithm.bubble.runTests()

P.S. Thank @DávidPásztor and @matt for hints :)

Testing for UINavigationController deallocation is failing?

As I recall, the docs say that objects held weakly "may be released at any time." The operative part is "may be".

I'm guessing that your view controller and navigation controller are auto-released. This is a term that harks back to the days of manual reference counting, but is still relevant under the covers. The object is created with a retain count of 1, and then added to an "auto-release pool". Then, next time your app's current function returns and it visits the event loop, the "auto-release pool is drained", which means that every entry in the auto-release pool gets a release message, dropping it's retain count by 1. When the retain count drops to zero, the object is deallocated.

(ARC actually uses reference counting under the covers, and so retain counts and auto-release pools are still relevant. It's just that with ARC the compiler takes care of maintaining them for you. Your strong and weak references get turned into to calls to the low level system retain, release, and auto-release functions.)

I'm not sure if it would work in the context of a test, but you might be able to use code like this:

func testReferences() throws {
var strongVC: UIViewController? = UIViewController()
var strongNC: UINavigationController? = UINavigationController(rootViewController: strongVC!)
weak var weakVC = strongVC
weak var weakNC = strongNC
strongVC = nil

XCTAssertNotNil(weakVC)
XCTAssertNotNil(weakNC)

strongNC = nil
DispatchQueue.main.async {
XCTAssertNil(weakVC) // fails
XCTAssertNil(weakNC) // fails
}
}
}

That code would cause the calls to XCTAssertNil() to be deferred until the next pass through the event loop.

The problem with that code is that by the time the call to DispatchQueue.main.async() is executed, the test may be over.

Edit:

As pointed out by Cristik in their comment, the better way would be to use an autoreleasepool command:

func testReferences() throws {
//Define the vars we want to test outside of the auto-release pool statement.
weak var weakVC: UIViewController
weak var weakNC: UINavigationController
autoreleasepool {
var strongVC: UIViewController? = UIViewController()
var strongNC: UINavigationController? = UINavigationController(rootViewController: strongVC!)
weakVC = strongVC
weakNC = strongNC
strongVC = nil

XCTAssertNotNil(weakVC)
XCTAssertNotNil(weakNC)

strongNC = nil
}
//Test for nil outside of the autorelasepool statement,
//after the auto-release pool is drained.
XCTAssertNil(weakVC) // fails
XCTAssertNil(weakNC) // fails
}

How do you write unit tests with Parse in Xcode (Swift in my case)?

This is happening because the parse block executes on a different thread than your test runs on and so your test finishes unaware of what the parse block does.

To test async operations you should create an expectation in your XCTest subclass and call the waitForExpectation method. Here's a quick example:

View Controller w/method

class ViewController: UIViewController {

override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}

override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}

func getUserStuff(completionHandler: (succeeded: Bool) -> ()) {

}
}

The test:

func testGetUserInfo() {

let vc = ViewController()

let userOpExpectation: XCTestExpectation = expectationWithDescription("Got user info")

vc.getUserStuff { (succeeded) in
XCTAssert(succeeded, "Should get user stuff")
userOpExpectation.fulfill()
}

waitForExpectationsWithTimeout(5.0, handler: nil)
}

There's a lot that goes into testing and Apple provides a lot, both in the way of included code and documentation, check out: https://developer.apple.com/library/tvos/documentation/DeveloperTools/Conceptual/testing_with_xcode/chapters/04-writing_tests.html for more info.

How to set parameter to a XCUITest test case do prepare for data driven testing

Sadly, XCTest has no support for parameterized testing.

(Will that change at this year's WWDC? I keep hoping, every year…)

Parameterized testing tools with automatic code exploration

If you're looking for other automatic testing tools for .NET, take a look at FsCheck. It's a port of Haskell's QuickCheck to F#. It's also usable from other .NET languages.

QuickCheck has also been ported to other platforms (ClojureCheck, ScalaCheck)

*Check libraries are not exactly like Pex, but there are similarities.

If you mean parameterized tests for .NET (as in writing tests that take parameters), all major test frameworks in .NET (NUnit, MbUnit, xUnit) support this.



Related Topics



Leave a reply



Submit