Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Asynchronous UI Testing in Xcode With Swift

I am writing an app that makes plenty of network requests. As usual they are async, i.e. the call of the request method returns immediately and the result is delivered via a delegate method or in a closure after some delay. Now on my registration screen I sent a register request to my backend and want to verify that the success UI is shown when the request finishes.

Which options are out there to wait for the request to finish, verify the success UI and only after that leave the test method?

Also are there any more clever options than waiting for the request to finish?

Thanks in advance!

like image 657
blackjacx Avatar asked Dec 08 '22 15:12

blackjacx


1 Answers

Trivial Approach

Apple implemented major improvements in Xcode 9 / iOS 11 that enables you to wait for the appearance of a UI element. You can use the following one-liner:

<#yourElement#>.waitForExistence(timeout: 5)

Advanced Approach

In general UI and unit tests (referred to as tests here) must run as fast as possible so the developer can run them often and does not get frustrated by the need to run a slow test suite multiple times a day. In some cases, there is the possibility that an (internal or security-related) app accesses an API that can only be accessed from certain networks / IP ranges / hosts. Also, most CI services offer pretty bad hardware and limited internet-connection speed.

For all of those reasons, it is recommended to implement tests in a way that they do no real network requests. Instead, they are run with fake data, so-called fixtures. A clever developer realizes this test suite in a way that source of the data can be switched using a simple switch like a boolean property. Additionally, when the switch is set to fetch real backend data the fixtures can be refreshed/recorded from the backend automatically. This way it is pretty easy to update the fake data and quickly detect changes of the API.

But the main advantage of this approach is speed. Your test will not make real network requests but instead run against local data what makes them independent on:

  • server issues
  • connection speed
  • network restrictions

This way you can run your tests very fast and thus much more often - which is a good way of writing code ("Test Driven Development").

On the other hand, you won't detect server changes immediately anymore since the fake data won't change when the backend data changes. But this is solved by simply refreshing your fixtures using the switch you have implemented because you are a smart developer which makes this issue a story you can tell your children!

But wait, I forgot something! Why this is a replacement for the trivial approach above - you ask? Simple! Since you use local data which is available immediately you also can call the completion handler immediately too. So there is no delay between doing the request and verifying your success UI. This means you don't need to wait which makes your tests even faster!

I hope this helps some of my fellows out there. If you need more guidance regarding this topic don't hesitate and reply to this post.

Cya!

like image 143
blackjacx Avatar answered Jan 01 '23 20:01

blackjacx