XCTestExpectation - Calling an async method twice causes API violation - ios

I’m writing unit tests in swifts, and testing a unique workflow.
In methodA(), I load an object incorrectly (say with incorrect credentials) using an async method. Also kick off an expectation
func methodA(withCred credential: NSURLCredential) {
var objA = ObjectA()
// Set objA.a, objA.b, objA.c,
objA.credential = credential //Incorrect credential First time, Correct Credential second time
objA.delegate = self
expectation = expectationWithDescription(“Aync”)
objA.callAsyncMethod() //This fires successDelegate() or failureDelegate()}
When FailureDelegate() is fired, I reload the object, correctly this time. In order do so, I need to call MethodA() again (so I can reuse all the other stuff there).
func failureDelegate(error: NSError!) {
XCTAssertTrue(error.localizedDescription == “Invalid Credentials“)
//Now that I’ve verified correct error is returned, I need to reload objA
methodA(withCred:correctCredential)
}
func successDelegate(obj : ObjectA) {
XCTAssert(“Object is loaded”)
expectation.fulfill()
}
3.This kicks off the same expectation again in methodA, and results in the following error:
API violation - creating expectations while already in waiting mode.
I understand this is not permitted by swift. Is there a workaround or better way to test these kinds of async methods looping with Swift using XCTest?
Thanks!

Don't share instances of expectation across tests. You should be declaring expectation (i.e. with let) in the body of each test, not as a property on XCTestCase. If you really need to use the delegation pattern (closures would be much, much simpler and more conventional), you can pass that as an additional parameter to your delegate method.

I think your code exemple is incomplete, could you provide the full code?
As #mattt said each test should preferably be unique and should not reuse other test variable.
Regarding your issue, you should declare all your expectation first before the triggering waitForExpectationsWithTimeout:handler:. You can't not create a new expectation after you've start waiting for another one.

Related

Clean up SimplePing code with RunLoop.current.run

I found the SimplePing library from Apple and want to use it in a SwiftUI Project.
To use the library I code online which works fine. The start function is as follows:
public func start(hostName: String) {
let pinger = SimplePing(hostName: "192.168.178.20")
pinger.delegate = self
pinger.start()
var count = 5
repeat {
if (self.canStartPinging) {
pinger.send(with: nil)
count-=1
if count == 0{
self.canStartPinging = false
break
}
}
RunLoop.current.run(mode: RunLoop.Mode.default, before: NSDate.distantFuture)
} while(true)
I don't really understand why I need the RunLoop.current.run(mode: RunLoop.Mode.default, before: NSDate.distantFuture) line. When I remove it the delegates of SimplePing doesn't get called.
How can I simplify this code and use it without blocking the Main thread?
The run(mode:before:) is there to allow the run loop to process events while this repeat-while loop spins. It’s a way to make a blocking loop allow things to occur on the run loop.
You haven’t shared the code that is setting canStartPinging, but I’m guessing that, at the very least, SimplePingDelegate method didStartWithAddress sets it. So, if you’re spinning on the main thread without calling that run(mode:before:), the SimplePing delegate method probably never gets a chance to be called. By adding that run call, at least the delegate method can run.
Your suspicion about this this whole pattern of spinning and calling run(mode:before:) is warranted. It’s horribly inefficient pattern. It should be eliminated.
If this were a standard Swift project, I’d suggest just using the delegate-protocol pattern and you’d be done. Since this is Swift UI, I’d suggest refactoring this to be a Combine Publisher, which you can then integrate into your SwiftUI project.

iOS unit tests: how to handle completion of tests?

I want to write path of test artifacts at the end of output in console. It would be handy. And I'm also just curious.
It seems that XCTest somehow terminate the app, app delegate doesn't receive lifecycle callbacks, program exits before main function from main.m.
Take a look at Test Execution and Observation specifically testBundleDidFinish.
If you define an object which adopts XCTestObservation you can add it to the XCTestObservationCenter.shared instance at any point during your test suite's run and receive a call when the entire bundle has finished.
Jonah helped me, I also want to place some code here for other people.
To start observation before any test there is a proper way to do it.
There is NSPrincipalClass key in test bundle's Info.plist. Put there a name of your "principal class" (google it for more info). In my case it is PrincipalClass. When test bundle is loaded, init is called in the principal class. Here's my PrincipalClass:
#objc(PrincipalClass)
final class PrincipalClass: NSObject {
override init() {
TestObservationEntryPoint.instance.startObservation()
}
}
I was able to make cool reporting system with Allure2. Reporting is not what I wanted at the time I asked this question, but it was possible because I found an answer. It is a good application of test observation in XCTest.
For me the answers were very confusing.
func testYourTest() {
let expectation = XCTestExpectation(description: "Your action")
API.sharedInstance.yourRequestWithCompletionAndFailure(parameter: parameter, completion: { (response) in
//XCTAssert with response
expectation.fulfill()
}) { (error) in
//XCTAssert with your error
expectation.fulfill()
}
wait(for: [expectation], timeout: 30.0)
}
With expectation you listen to a response. Don't forget to set a timeout.
More Details/Source: https://developer.apple.com/documentation/xctest/asynchronous_tests_and_expectations/testing_asynchronous_operations_with_expectations

Swift cast generic without knowing the type

Is it possible to typecast an object like so (let the code speak for itself):
protocol Parent {
...
}
class Child<LiterallyAnyValue, SameAsThePrevious>: Parent {
...
}
And then when using it:
func foobar(parent: Parent) {
if parent is Child { //ONE
print(parent as! Child) //TWO
}
}
At the signed points xcode wants me to supply the two types of "Child" within <> like Child<Int, String>...
The problem is that those types could be anything... LITERALLY
(And I've tried Child<Any, Any> but that doesn't work in this case)
Is there a workaround or a solution to this?
-------- Clarification --------
I am working on an iOS 7 project so I can't really use any modern library :)
That is including PromiseKit and Alamofire and the app has to make tons of http requests. The use promises in requests has grown on me, so I created my own Promise class.
At first I made it so that the Promise class would not be a generic and it would accept Any? as the value of the resolution procedure.
After that I wanted to improve my little Promise class with type clarification so the class Promise became "class Promise<T>"
In my implementation the then method created a PromiseSubscriber which then would be stored in a Promise property called subscribers.
The PromiseSubscriber is the protocol here that has two subset classes, one being PromiseHandler (this is called when the promise is resolved), and the other the PromiseCatcher (this is called when the promise is rejected)
Both PromiseSubscriber subset classes have a property called promise and one called handler.
These classes are also generics so that you know what kind of Promise they store and what is the return type of the handler.
In my resolution process I have to check if the PromiseSubscriber is a (let's say) PromiseHandler and if it is then call the handler that returns something and then resolve the subscribed promise with that value.
And here is the problem. I can't check if the subscriber is a catcher or a handler...
I hope it's clear enough now. Maybe this is not the right approach, I honestly don't know I am just trying to create something that is fun and easy to use (code completion without checking the type).
If it's still not clear and you are willing to help me, I'll send over the classes!
It's a little difficult to understand what you're really trying to do here (please tell me it's something other than JSON parsing; I'm so tired of JSON parsing and it's the only thing people ever ask about), but the short answer is almost certainly no. Some part of that is probably a misuse of types, and some part of that is a current limitation in Swift.
To focus on the limitation in Swift part, Swift lacks higher-kinded types. It is not possible to talk about Array. This is not a type in Swift. You can only work with Array<Int> or Array<String> or even Array<T>, but only in cases where T can be determined at compile time. There are several ways to work through this, but it really depends on what your underlying problem is.
To the misuse of types side, you generally should not have if x is ... in Swift. In the vast majority of cases this should be solved with a protocol. Whatever you were going to do in the if, make it part of the Parent protocol and give it a default empty implementation. Then override that implementation in Child. For example:
protocol Parent {
func doSpecialThing()
}
extension Parent {
func doSpecialThing() {} // nothing by default
}
class Child<LiterallyAnyValue, SameAsThePrevious>: Parent {}
extension Child {
func doSpecialThing() {
print(self)
}
}
func foobar(parent: Parent) {
parent.doSpecialThing()
}
Thanks for the clarification; Promise is a great thing to play with. Your mistake is here:
In my resolution process I have to check if the PromiseSubscriber is a (let's say) PromiseHandler and if it is then call the handler that returns something and then resolve the subscribed promise with that value.
Your resolution process should not need to know if it's a handler or a catcher. If it does, then your PromiseSubscriber protocol is incorrectly defined. The piece it sounds like you're missing is a Result. Most Promise types are built on top of Result, which is an enum bundling either success or failure. In your scheme, handlers would process successful Results and ignore failing results. Catchers would process failing results and ignore successful Results. The promise resolution shouldn't care, though. It should just send the Result to all subscribers and let them do what they do.
You can build this without a Result type by using a protocol as described above.
protocol PromiseSubscriber {
associatedType Wrapped // <=== It's possible you've also missed this piece
func handleSuccess(value: Wrapped)
func handleFailure(failure: Error)
}
extension PromiseSubscriber {
func handleSuccess(value: Wrapped) {} // By default do nothing
func handleFailure(failure: Error) {}
}
class PromiseHandler<Wrapped> {
func handleSuccess(value: Wrapped) { ... do your thing ... }
}
class PromiseCatcher {
func handleFailure(failure: Error) { ... do your thing ... }
}
I recommend studying PinkyPromise. It's a nice, simple Promise library (unlike PromiseKit which adds a lot of stuff that can make it harder to understand). I probably wouldn't use a protocol here; the associatedtype makes things a bit harder and I don't think you get much out of it. I'd use Result.
Use a generic type in your foobar function, the one below requires the parent parameter to conform to the Parent protocol and T will represent the class of the object passed.
func foobar<T: Parent>(parent: T) {
print(parent)
}

Differrence between closure and function as argument in swift

I have almost 4 years of experience with Objective C and a newbie in swift. Am trying to understand the concept of swift from the perspective of Objective C. So if I am wrong please guide me through :)
In objective c, we have blocks (chunck of code which can be executed later asynchronously) which made absolutely perfect sense. But in swift now we can pass a function as a parameter to another function, which can be executed later, and then we have closure as well.
As per Apple "functions are special cases of clauses."
As per O'Reilly "when a function is passed around as a value, it carries along its internal references to external variables. That is what makes a function a closure."
So I tried a little bit to understand the same :)
Here is my closure
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a ni
let tempNumber : Int = 5
let numbers = [1,2,3,4,5]
print (numbers.map({ $0 - tempNumber}))
}
The variable tempNumber is declared even before the closure was declared, yet closure has the access to the variable. Now rather then a map, I tried using a custom class passed closure as a parameter and tried executing the same code :) Though now closure is getting executed in differrent scope, it still has the access to tempNumber.
I concluded : closures have an access to the variables and methods which are declared in the same scope as closure it self, though it gets execcuted in differrent scope.
Now rather then passing closure as paramter, tried passing function as a parameter,
class test {
func testFunctionAsParameter(testMethod : (Int) -> Int){
let seconds = 4.0
let delay = seconds * Double(NSEC_PER_SEC) // nanoseconds per seconds
let dispatchTime = dispatch_time(DISPATCH_TIME_NOW, Int64(delay))
dispatch_after(dispatchTime, dispatch_get_main_queue(), {
self.callLater(testMethod)
})
}
func callLater(testMethod : (Int) -> Int) -> Int {
return testMethod(100)
}
}
In a differrent class I created an instance of Test and used it as follow
/* in differrent class */
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a ni
let tempAge : Int = 5
func test2(val : Int) -> Int {
return val - tempAge;
}
let testObj = test();
print(testObj.testFunctionAsParameter(test2))
}
Declared a class called test, which has a method called testFunctionAsParameter which in turn calls another method called callLater and finally this method executes the passed function :)
Now all these circus, just to ensure that passed method gets executed in differrent scope :)
When I executed the above code :) I was shocked to see that even though function passed as a parameter finally gets executed in different scope, still has the access to the variables testNumber that was declared at the same scope as the method declaration :)
I concluded : O'Reilly's statement "when a function is passed around as a value, it carries along its internal references to external variables." was bang on :)
Now my doubt is apple says functions are special cases of clauses. I thought special case must be something to do with scope :) But to my surprise code shows that both closure and function have access to variables in external scope !!!!
Other then, syntax differrence how closure is differrent from function passed as argument ??? Now there must be some differrence internally, otherwise Apple wouldn't have spent so much time in designing it :)
If not scope?? then what else is different in closure and function ?? O'Reilly states "when a function is passed around as a value, it carries along its internal references to external variables. That is what makes a function a closure." so what is it trying to point out ? that closure wont carry references to external variables ? Now they can't be wrong either, are they?
I am going mad with two conflicting statements from Apple and O'Reilly :( Please help, am I understanding something wrong ?? Please help me understand the difference.
In swift there really isn't any difference between functions and closures. A closure is an anonymous function (A function with no name.) That's about it, other than the syntax differences you noted.
In Objective-C functions and blocks/closures ARE different.
Based on your post, it looks like you have a pretty full understanding on the topic and you may just be getting lost in the semantics. It basically boils down to:
1. a closure is a closure
2. a function is a closure with a name
Here's a post with more details. But again, it's mostly a discussion of semantics. The "answer" is very simple.
What is the difference between functions and closures?

isKindOfClass returning different values for Test Cases

I have a method someMethod. This method, at some point has the following if-else condition.
- (void) someMethod {
// ... some more code ...
if ([userArray[0] isKindOfClass:[Me class]]) {
// some code
}
else {
// some other code
}
}
Now this if-condition is always met when I execute the code normally. But when I call it from one of my test-cases, the else-part gets executed instead. I am calling this method exactly the same way (it has no side-effects, etc).
When I debugged the thing in both normal run, and testing run. I saw something different.
While running in Test, the userArray had 1 object, (Me_Me_2 *)0x00007fa61d39dbf0.
And while running it normally, the userArray had the same object, but there was one difference. It said (Me_Me_ *)0x00007fce71459ae0.
When I print the value of NSStringFromClass([userArray[0] class]), they both print "Me".
"Me" is a NSManagedObject.
Another interesting thing is, if I add an expression in the debugger and evaluate it, it always evaluates to true - ([((NSObject*)userArray[0]) isKindOfClass:[Me class]]) returns (bool)true. This is totally bizarre! If the condition is true, why does it ever go into the else block?
Now some questions -
What is happening over here? Are Core Data objects treated different when running in tests?
Why is the type of the object "Me_Me_2" while testing and "Me_Me_" otherwise? Why is it not just "Me"?
This sounds similar to the following issue: isKindOfClass doesn't work as expected
In short, is the class being compared a target member of the test target? It should only be a target member of the application.

Resources