Generic parameter 'T' could not be inferred when XCTAssertEqual() called - swift

The topic title is a compiler error related to the xCode Assert Equal test in the testPrimesUpTo100ShouldBe25() method.
One thing I noticed is that the call to the calculate() method in PrimeCalculator, when called from testPrimePerformance() and testPrimesUpTo100ShouldBe25(), only 'colours up' green in testPrimePerformance().
Full swift source file is below:
import XCTest
struct PrimeCalculator {
static func calculate(upTo max: Int) -> [Int] {
guard max > 1 else {
return []
}
var sieve = [Bool](repeating: true, count: max)
sieve[0] = false
sieve[1] = false
for number in 2 ..< max {
if sieve[number] == true {
for multiple in stride(from: number * number, to: sieve.count, by: number) {
sieve[multiple] = false
}
}
}
// collapse our results down to a single array of primes
let primes = sieve.enumerated().compactMap { $1 == true ? $0 : nil }
return primes
}
}
class AsynchronousTests: XCTestCase {
func testPrimePerformance() {
measure {
_ = PrimeCalculator.calculate(upTo: 1_000_000)
}
}
func testPrimesUpTo100ShouldBe25() {
// given
let maximumCount = 100
// when
let progress = PrimeCalculator.calculate(upTo: maximumCount) {
XCTAssertEqual($0.count, 25)
}
// then
let predicate = NSPredicate(format: "#.completedUnitCount == %#", argumentArray: [progress, maximumCount])
let expectation = XCTNSPredicateExpectation(predicate: predicate, object: progress)
wait(for: [expectation], timeout: 10)
}
override func setUp() {
// Put setup code here. This method is called before the invocation of each test method in the class.
}
override func tearDown() {
// Put teardown code here. This method is called after the invocation of each test method in the class.
}
func testExample() {
// This is an example of a functional test case.
// Use XCTAssert and related functions to verify your tests produce the correct results.
}
func testPerformanceExample() {
// This is an example of a performance test case.
measure {
// Put the code you want to measure the time of here.
}
}
}

Here
let progress = PrimeCalculator.calculate(upTo: maximumCount) {
XCTAssertEqual($0.count, 25)
}
you pass a “trailing closure” to the calculate() method, which has two problems:
First, that method takes only a integer argument, but not a closure argument. This is the actual problem.
Second, the compiler can not determine the type of $0 from the context. This causes the compiler error message.
What you probably want is simply
let progress = PrimeCalculator.calculate(upTo: maximumCount)
XCTAssertEqual(progress.count, 25)

Related

Swift 5.5 test async Task in init

I would like to test if my init function works as expected. There is an async call in the init within a Task {} block. How can I make my test wait for the result of the Task block?
class ViewModel: ObservableObject {
#Published private(set) var result: [Item]
init(fetching: RemoteFetching) {
self.result = []
Task {
do {
let result = try await fetching.fetch()
self.result = result // <- need to do something with #MainActor?
} catch {
print(error)
}
}
}
}
Test:
func testFetching() async {
let items = [Item(), Item()]
let fakeFetching = FakeFetching(returnValue: items)
let vm = ViewModel(fetching: FakeFetching())
XCTAssertEqual(vm.result, [])
// wait for fetching, but how?
XCTAssertEqual(vm.result, items])
}
I tried this, but setting the items, only happens after the XCTWaiter. The compiler warns that XCTWaiter cannot be called with await, because it isn't async.
func testFetching() async {
let items = [Item(), Item()]
let fakeFetching = FakeFetching(returnValue: items)
let expectation = XCTestExpectation()
let vm = ViewModel(fetching: FakeFetching())
XCTAssertEqual(vm.result, [])
vm.$items
.dropFirst()
.sink { value in
XCTAssertEqual(value, items)
expectation.fulfill()
}
.store(in: &cancellables)
let result = await XCTWaiter.wait(for: [expectation], timeout: 1)
XCTAssertEqual(result, .completed)
}
Expectation-and-wait is correct. You're just using it wrong.
You are way overthinking this. You don't need an async test method. You don't need to call fulfill yourself. You don't need a Combine chain. Simply use a predicate expectation to wait until vm.result is set.
Basically the rule is this: Testing an async method requires an async test method. But testing the asynchronous "result" of a method that happens to make an asynchronous call, like your init method, simply requires good old-fashioned expectation-and-wait test.
I'll give an example. Here's a reduced version of your code; the structure is essentially the same as what you're doing:
protocol Fetching {
func fetch() async -> String
}
class MyClass {
var result = ""
init(fetcher: Fetching) {
Task {
self.result = await fetcher.fetch()
}
}
}
Okay then, here's how to test it:
final class MockFetcher: Fetching {
func fetch() async -> String { "howdy" }
}
final class MyLibraryTests: XCTestCase {
let fetcher = MockFetcher()
func testMyClassInit() {
let subject = MyClass(fetcher: fetcher)
let expectation = XCTNSPredicateExpectation(
predicate: NSPredicate(block: { _, _ in
subject.result == "howdy"
}), object: nil
)
wait(for: [expectation], timeout: 2)
}
}
Extra for experts: A Bool predicate expectation is such a common thing to use, that it will be found useful to have on hand a convenience method that combines the expectation, the predicate, and the wait into a single package:
extension XCTestCase {
func wait(
_ condition: #escaping #autoclosure () -> (Bool),
timeout: TimeInterval = 10)
{
wait(for: [XCTNSPredicateExpectation(
predicate: NSPredicate(block: { _, _ in condition() }), object: nil
)], timeout: timeout)
}
}
The outcome is that, for example, the above test code can be reduced to this:
func testMyClassInit() {
let subject = MyClass(fetcher: fetcher)
wait(subject.result == "howdy")
}
Convenient indeed. In my own code, I often add an explicit assert, even when it is completely redundant, just to make it perfectly clear what I'm claiming my code does:
func testMyClassInit() {
let subject = MyClass(fetcher: fetcher)
wait(subject.result == "howdy")
XCTAssertEqual(subject.result, "howdy") // redundant but nice
}
Tnx to matt this is the correct way. No need for async in the test function and just using a predicate did the job.
func testFetching() {
let items = [Item(), Item()]
let fakeFetching = FakeFetching(returnValue: items)
let expectation = XCTestExpectation()
let vm = ViewModel(fetching: FakeFetching())
let pred = NSPredicate { _, _ in
vm.items == items
}
let expectation = XCTNSPredicateExpectation(predicate: pred, object: vm)
wait(for: [expectation], timeout: 1)
}
Slight variation on Matt's excellent answer. In my case, I've broken out his extension method into even more granular extensions for additional convenience.
Helper Framework
public typealias Predicate = () -> Bool
public extension NSPredicate {
convenience init(predicate: #escaping #autoclosure Predicate) {
self.init{ _, _ in predicate() }
}
}
public extension XCTNSPredicateExpectation {
convenience init(predicate: #escaping #autoclosure Predicate, object: Any) {
self.init(predicate: NSPredicate(predicate: predicate()), object: object)
}
convenience init(predicate: #escaping #autoclosure Predicate) {
self.init(predicate: NSPredicate(predicate: predicate()))
}
convenience init(predicate: NSPredicate) {
self.init(predicate: predicate, object: nil)
}
}
public extension XCTestCase {
func XCTWait(for condition: #escaping #autoclosure Predicate, timeout: TimeInterval = 10) {
let expectation = XCTNSPredicateExpectation(predicate: condition())
wait(for: [expectation], timeout: timeout)
}
}
With the above in place, the OP's code can be reduced to this...
Unit Test
func testFetching() {
let items = [Item(), Item()]
let fakeFetching = FakeFetching(returnValue: items)
let vm = ViewModel(fetching: FakeFetching())
XCTWait(for: vm.items == items, timeout: 1)
}
Notes on Naming
Above, I'm using a somewhat controversial name in calling my function XCTWait. This is because the XCT prefix should be considered reserved for Apple's XCTest framework. However, the decision to name it this way stems from the desire to improve its discoverability. By naming it as such, when a developer types XCT In their code editor, XCTWait is now presented as one of the offered auto-complete entries** making finding and using much more likely.
However, some purists may frown on this approach, citing if Apple ever added something named similar, this code may suddenly break/stop working (although unlikely unless the signatures also matched.)
As such, use such namings at your own discretion. Alternately, simply rename it to something you prefer/that meets your own naming standards.
(** Provided it is in the same project or in a library/package they've imported somewhere above)

XCTWaiter().wait with XCTestExpectation and NSPredicate seems to fail

I am trying to write unit tests where I want my test case to wait for a variable in a certain class to change. So I create an expectation with a predicate and wait for the value to change using XCTWaiter().wait(for: [expectation], timeout: 2.0), which I assume is the correct method to use.
The following code works as expected:
class ExpectationTests: XCTestCase {
var x: Int = 0
private func start() {
_ = Timer.scheduledTimer(withTimeInterval: 0.5, repeats: false) { _ in
self.x = 1
}
}
func test1() {
let predicate = NSPredicate(format: "x == 1")
let expectation = XCTNSPredicateExpectation(predicate: predicate, object: self)
start()
let result = XCTWaiter().wait(for: [expectation], timeout: 2.0)
switch result {
case .completed: XCTAssertEqual(x, 1)
case .timedOut: XCTFail()
default: XCTFail()
}
}
A variable (x) is set to 0 and then changes to 1 after 0.5s by the start() function. The predicate waits for that var (x) to change. That works: result is set to .completed and the var actually is set to 1. Yeah :-)
However, when the variable that I want to observe is not a local var, but is in a class somewhere, it no longer works. Consider the following code fragment:
class MyClass: NSObject {
var y: Int = 0
func start() {
_ = Timer.scheduledTimer(withTimeInterval: 0.5, repeats: false) { _ in
self.y = 1
}
}
}
func test2() {
let myClass = MyClass()
let predicate = NSPredicate(format: "y == 1")
let expectation = XCTNSPredicateExpectation(predicate: predicate, object: myClass)
myClass.start()
let result = XCTWaiter().wait(for: [expectation], timeout: 2.0)
switch result {
case .completed: XCTAssertEqual(myClass.y, 1)
case .timedOut: XCTFail()
default: XCTFail()
}
}
It is quite similar to the first piece of code, but this always ends after 2 seconds with result being .timedOut. I can't see what I am doing wrong. I use a variable from object myClass that I pass into the expectation instead of a local var and object 'self'. (The class var myClass.y is actually set to 1 when the test ends.)
I tried replacing XCTNSPredicateExpectation(predicate:object) with expectation(for:evaluatedWith:handler), but that didn't make any difference.
Many examples here on StackOverflow use a predicate that checks for exists in XCUIElement. But I am not testing UI; I just want to check if some var in some class has changed within a timeout period. I don't understand why that is so different from checking var exists in XCUIElement.
Any ideas?! Thank you in advance!
Well, thanks to #Willeke for pointing me in the right direction, I did find a solution, but I can't say I understand it completely...
Here's what my code looks like now:
// MARK: - Test 2
class MyClass: NSObject {
var y: Int = 0
func start() {
_ = Timer.scheduledTimer(withTimeInterval: 0.5, repeats: false) { _ in
self.y = 1
}
}
}
func test2() {
let myClass = MyClass()
let predicate = NSPredicate() { any, _ in
// guard let myClass = any as? MyClass else { return false }
return myClass.y == 1
}
let expectation = XCTNSPredicateExpectation(predicate: predicate, object: myClass)
myClass.start()
let result = XCTWaiter().wait(for: [expectation], timeout: 2.0)
switch result {
case .completed: XCTAssertEqual(myClass.y, 1)
case .timedOut: XCTFail()
default: XCTFail()
}
}
I can use a predicate with a closure that regularly checks whether the var has changed and returns true if it has the correct value. (It does that about once per second.) However, I actually thought that's what XCTWaiter was for, given the description in the documentation of expectation(for:evaluatedWith:handler:) (which is a convenience method for XCTNSPredicateExpectation):
The expectation periodically evaluates the predicate. The test fulfills the expectation when the predicate evaluates to true.
So, I am happy that I can move on, but I still don't understand why this doesn't work with NSPredicate(format: "y == 1") instead of the predicate with the closure...

Weak reference becomes nil after dispatch into serial queue

I've toyed around with Swift Playground and noticed the following issue:
The code below describes a series of object connected to one another in the following way:
objectC --> ObjectB -- weak ref to another C --> another C --> Object B etc..
Each objectC consists of
- a ref to a object B
- a weak ref to a delegate => this one becomes nil!!
Each objectB consists of
- A var integer
- A weak ref to another object C
The code does the following:
objectC call a function, say run(), which will evaluate (objectB.weak_ref_to_another_C), and call objectB.weak_ref_to_another_C.run() in a serial Queue.
After calling .run() a couple of times, C's delegate mysteriously becomes nil....
Any idea what I'm doing wrong? To start the code, simply call test_recursive_serial() on Swift Playground.
let serialQueue = DispatchQueue(label: "myQueue");
public protocol my_protocol:class {
func do_something(ofValue:Int,completion:((Int) -> Void))
}
public class classA:my_protocol {
public let some_value:Int;
public init(value:Int){
self.some_value = value;
}
public func do_something(ofValue:Int,completion:((Int) -> Void)) {
print("A:\(some_value) in current thread \(Thread.current) is executing \(Thread.current.isExecuting)");
if self.some_value == ofValue {
completion(ofValue);
}
}
}
public class classB {
public weak var jump_to_C:classC?;
public var value:Int = 0;
}
public class classC {
weak var delegate:my_protocol?{
willSet {
if (newValue == nil) { print("target set to nil") }
else { print("target set to delegate") }
}
}
var someB:classB?
public func do_something_else() {
print(self.delegate!)
}
public func do_another(withValue:Int,completion:((Int) -> Void)) {
}
public func run(completion:#escaping ((Int) -> Void)) {
print("\(self.someB?.value)");
assert(self.delegate != nil, "not here");
if let obj = someB?.jump_to_C, obj !== self {
someB?.value += 1;
print("\(someB!)")
usleep(10000);
if let value = someB?.value, value > 100 {
completion(someB!.value);
} else {
serialQueue.async {
print("lauching...")
obj.run(completion: completion);
}
}
}else{
print("pointing to self or nil...\(someB)")
}
}
}
public func test_recursive_serial() {
let my_a = classA(value:100);
let arrayC:[classC] = (0..<10).map { (i) -> classC in
let c = classC();
c.delegate = my_a;
return c;
}
let arrayB:[classB] = (0..<10).map { (i) -> classB in
let b = classB();
let ii = (i + 1 >= 10) ? 0 : i + 1;
b.jump_to_C = arrayC[ii]
return b;
}
arrayC.forEach { (cc) in
cc.someB = arrayB[Int(arc4random())%arrayB.count];
}
arrayC.first!.run() { (value) in
print("done!");
}
}
Important note: if test_recursive_serial() content is directly called from the playground, that is not through a function, the problem doesn't appear.
Edit: You'll need to add 'PlaygroundPage.current.needsIndefiniteExecution = true' to the playground code.
Edit: Ok, I feel I need to add this. Big mistake on my side, test_recursive_serial() doesn't keep a reference on any of the called objects, so obviously, they all become nil after the code leaves the function. Hence the problem. Thanks to Guy Kogus for pointing that out.
Final edit: Adding this, in the hope it might help. Swift playground are great to test-drive code, but can sometime become very busy. Within the current issue, the solution requires to set the variables first, and then pass them to test_recursive_serial() which in turn adds to the chatty appearance of the playground. Here's another option to keep your code tidy and self-contained, while dealing with async functions of various flavours...
If you have an async task - one that doesn't fit into URL fetch -, say:
myObject.myNonBlockingTask(){ print("I'm done!"}
First, include XCTest at the top of your file.
import XCTest
then add the following:
func waitForNotificationNamed(_ notificationName: String,timeout:TimeInterval = 5.0) -> Bool {
let expectation = XCTNSNotificationExpectation(name: notificationName)
let result = XCTWaiter().wait(for: [expectation], timeout: timeout)
return result == .completed
}
finally, change your completion block to:
myObject.myNonBlockingTask(){
print("I'm done!")
let name = NSNotification.Name(rawValue: "foobar");
NotificationCenter.default.post(name:name , object: nil)
}
XCTAssert(waitForNotificationNamed("foobar", timeout: 90));
the full playground code will look like:
public func my_function() {
let somevar:Int = 123
let myObject = MyClass(somevar);
myObject.myNonBlockingTask(){
print("I'm done!")
let name = NSNotification.Name(rawValue: "foobar");
NotificationCenter.default.post(name:name , object: nil)
}
XCTAssert(waitForNotificationNamed("foobar", timeout: 90));
}
Playground will wait on the notification before going any further, and also generate an exception if it times out. All locally created objects will remain valid until the execution completes.
Hope this helps.
The main issue is that you're testing this in Playgrounds, which doesn't necessarily play nicely with multithreading. Following from this SO question, change the test_recursive_serial function to:
arrayC.first!.run() { (value) in
print("done! \(value)")
XCPlaygroundPage.currentPage.needsIndefiniteExecution = false
}
XCPlaygroundPage.currentPage.needsIndefiniteExecution = true
while XCPlaygroundPage.currentPage.needsIndefiniteExecution {
}
(You'll need to add import XCPlayground at the top of the code to make it work.)
If you don't add that code change, then my_a is released after you leave that function, which is why delegate becomes nil on the second call to run.
I also found that in run, if you don't call the completion closure in the else case like so:
public func run(completion:#escaping ((Int) -> Void)) {
...
if let obj = someB?.jump_to_C, obj !== self {
...
}else{
print("pointing to self or nil...\(someB)")
completion(-1) // Added fallback
}
}
Then the program gets stuck. By adding that it runs to the end, although I haven't actually worked out why.
Also, please get rid of all your ;s, this isn't Objective-C 😜

Where does this line of code return to?

In the code below, I'm confused as to where the return statement in the code returns to? When executed, it works as expected, but does it return to:
if userIsInTheMiddleOfTyping == true
or does it return to:
if let digit = sender.currentTitle
Below is the full chunk of code where this applies.
class ViewController: UIViewController {
private var userIsInTheMiddleOfTyping = false
private var decimalUsed = false
#IBAction private func touchDigit(sender: UIButton)
{
if let digit = sender.currentTitle {
if userIsInTheMiddleOfTyping == true {
if digit == "." && decimalUsed == true {
return //where does this return to?
} else if digit == "." && decimalUsed == false {
decimalUsed = true
}
let textCurrentlyInDisplay = display.text!
display.text = textCurrentlyInDisplay + digit
} else {
display.text = digit
}
userIsInTheMiddleOfTyping = true
}
}
A return always returns out of the function, so in this case it returns to the line of code that calls touchDigit(...)
Basically here, the return just stops the execution of the touchDigit function.
(Which means that none of the code following the return will be run)
The return simply stops the code. You can put it in functions if you would like. For example:
If I want to continue running some code only if a certain statement is true, then you can return the function to stop it if it is false.
func something(a: Int, b: Int) {
if a != b {
return//Stops the code
}
//Some more code -- if a is not equal to b, this will not be called
}
Remember, this only works with void functions. It can work with others as well, but that is slightly different. You must return something along with it. Another example:
func somethingElse(a: Int, b: Int) -> Bool{
if a != b {
return false //stops the code, but also returns a value
}
return true //Will only get called if a == b
}
In this function, it is return a Boolean. If a != b, then return false is written because that returns false while also stoping the code.
For more on returns, you can visit Apple's documentation on functions.

How to check if an XCTestCase test has failed

Is it possible to check within a running test if any of its XCTAsserts have failed? I have a test with a few assertions in a row, and I want to add some code afterward to perform a specific action if any of them failed:
class testClass : XCTestCase
{
func testSomething()
{
let someComputedValue1 = func1()
let someComputedValue2 = func2()
XCTAssertLessThanOrEqual(someComputedValue1, 0.5)
XCTAssertLessThanOrEqual(someComputedValue2, 0.2)
if anyOfTheAboveAssertionsFailed {
performAction()
}
}
}
The part I'd like tips on is that anyOfTheAboveAssertionsFailed condition without duplicating the comparisons to the hard-coded values.
While using your own assertion methods solves the PO's issue, it is cumbersome if you need to use several XCAssert-methods.
Another approach is to override continueAfterFailure. If there is no failure the property will not be requested. If there is one, it will.
class MyTest: XCTest {
private var hasFailed = false
override var continueAfterFailure: Bool {
get {
hasFailed = true
return super.continueAfterFailure
}
set {
super.continueAfterFailure = newValue
}
}
override func tearDown() {
if hasFailed { performAction() }
hasFailed = false
}
}
You could of course write a new function...
func assertLessThanOrEqual(value: Double, limit: Double) -> Bool {
XCTAssertLessThanOrEqual(value, limit)
return value <= limit
}
And then write your tests like...
var allGood = true
allGood = allGood && assertLessThanOrEqual(someComputedValue1, 0.5)
allGood = allGood && assertLessThanOrEqual(someComputedValue2, 0.2)
if !allGood {
performAction()
}