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:
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
Cannot Decode Object of Class (Ampathpopupbutton)'
How to Condense Unwrapping Multiple Optionals in Swift
Swift: Using Member Constant as Default Value for Function Parameter
Navigationlink Doesn't Work with New .Searchable Modifier on Swiftui 3.0
Accessing an Actor's Isolated State from Within a Swiftui View
Binary to Hexadecimal in Swift
Changing Value of Character Using Ascii Value in Swift
Storagemetadata' Has No Member 'Downloadurl'
In App Purchase on MAC Catalyst Not Working
How to Refresh The Google Map in My Application Using Swift
How to Specify Japanese Encoding for a UIlabel
Hide Header While Collection View Is Loading
How to Spin and Add a Linear Force to an Entity Loaded from Reality Composer
Swift Lose Precision in Decimal Formatting
Swift Selector with Default Argument
Filter on Calayer Except for a Shape Which Is an Union of (Non Necessarily Distinct) Rectangles