Testing in iOS - Part 15: Challenge: Adding XCTWaiter | Ray Wenderlich

In this challenge, you'll add an XCTWaiter to your unit test.


This is a companion discussion topic for the original entry at https://www.raywenderlich.com/3530-testing-in-ios/lessons/15

Hi bdmoakley,
Do you think it will be more clear if you use:

let exp = expectation(description: "Whait the http request for fetching info content to finish")

Outside the get closure and:

exp.fulfill()

Inside it. After that you just have to check the timeout, but the actual assertions are in the get function.
This way seems more easy readable for me.

Full test case:

func testInfoLoading() {
    let url = "https://raw.githubusercontent.com/FahimF/Test/master/DogYears-Info.rtf"
    
    let exp = expectation(description: "Whait the http request for fetching info content to finish")
    
    HTTPClient.shared.get(url: url) { data, err in
        
        XCTAssertNil(err, "There was an error fetching the info content: \(err!.localizedDescription)") // Nil safety is proved by the assertion
        
        XCTAssertNotNil(data, "There was no data after loading info content")
        
        exp.fulfill()
    }
    
    let result = XCTWaiter.wait(for: [exp], timeout: 5.0)
    assert(result == XCTWaiter.Result.completed, "The loading of info content took too long")
}

Sure, it’s a different way of thinking about the code. It’d be interesting to run through some various iterations of the test to see how your organization may affect the test results. That said, the original code was used to demonstrate the XCTWaiter which really dictated how the code was organized.

1 Like

Hi, Brain! Why did you use in your code this:

  if res == XCTWaiter.Result.completed {
    XCTAssertNotNil(resData, "No data recived from the server for InfoView content")
  } else {
    XCTAssert(false, "The call to get the URL ran into some other error")
  }

instead of just that:

XCTAssert(result == XCTWaiter.Result.completed, "No data was received from the server for InfoView content")

because if that statement is true:
res == XCTWaiter.Result.completed

then resData never be nil and XCTAssertNotNil assertion in your code is redundant

One more minor tweak to @budhead’s answer

    func testInfoLoading() {
      let url = "https://raw.githubusercontent.com/FahimF/Test/master/DogYears-Info.rtf"
        let exp = expectation(description: "Download Info Content for DogYears")

        HTTPClient.shared.get(url: url) {
        (data, error) in
          XCTAssertNil(error, "There was an error loading the InfoView content")
          XCTAssertNotNil(data, "No data was received from the server for InfoView content")
          exp.fulfill()
      }
      wait(for: [exp], timeout: 5.0)
    }

I found that

assert(result == XCTWaiter.Result.completed, "The loading of info content took too long")

is not required because if the expectation isn’t fulfilled, the test will fail saying unfulfilled exception.

Thanks for the heads up - we’ll definitely review in the next update.

The approach in testInfoLoading is to basically rewrite the actual code from the application inside the test code. Thereby, not even testing the app code. This appears to test if the call works, though does not test the app itself. Let me know if I misunderstand.

Thanks for the heads up. We’ll take a look at it next time we update the course.

Hi bdmoakley, i did notice that test result is NOT changing when i replace let predicate = NSPredicate(format: “resData != nil”) for let predicate = NSPredicate(format: “resData == nil”). It still have passed, even when predicate assert that resData will be nil, look like predicate not work here or i do something wrong?

@bdmoakley Can you please help with this when you get a chance? Thank you - much appreciated! :]

Why do you use a instance variable instead of a local variable for storing returned data.

@bdmoakley Do you have any feedback about this? Thank you - much appreciated! :]

This is explained in the video.