Asynchronous Testing in Swift!

Asynchronous Testing.

If you’ve ever tried to test an asynchronous block of code you might have a felt that’s something fishy with deploying the normal way of testing for an asynchronous block of code.

Howdy! We’ll be discussing the asynchronous testing in this post. Picking up from where we left, in the previous post, we saw how to add Firebase to our project. We’ll extend that further to add login and sign up functionality to our application.

First, let us use Firebase for creating and logging in users.

Oh Man! I want to test! Let’s get to it quickly bro!

Head to the application that you created in Firebase. Let us add “Authentication” to our Firebase application.

There are a few steps involved. Let us get through them quickly.

  • Head to the left navigation bar to find the option Authentication. Click on “SET UP SIGN-IN METHOD” button.

  • You’ll be able to find an option to enable Authentication by Email/Password.

  • Enable the Email/Password Sign-in provider option.

That is it. You’ve added a Sign-in provider to your Firebase application now.


The sign in process is rather simple. Which we’ll cover in some time.

But first, let us prep up our test class. I’ll be naming it as UserTests.

There are four cases which should primarily be checked:

  1. A new user should be allowed to create an account.
  2. An existing user should be able to login.
  3. An existing user should not be able to create an account again!
  4. A non-existent i.e guest should not be able to login!

Let’s get going then!

Excited? Writing the test methods now.

These would be our test methods.

In order to test these, we’ll need to understand Firebase user authentication APIs. But before that, we’ll need to add Firebase’s Authentication module to our project.

Update your pod file similar to the one shown below.

There are a few things happening here! We’ve added the pods ‘Firebase/Core‘ and ‘Firebase/Auth‘ to our target and ‘Firebase‘ to our test target. This needs to be done in order to ensure the ‘Firebase’ module is available to the test target. If not added you’ll get the module not founderror on running a test.

Having done that we can move forward to getting acquainted with the Firebase Authentication APIs.

Creating a new user:

Sign In a user:

This is all we need to take care of as of now in this project. So we’ll stick to just these.

For additional enlightenment, you may refer to this link.


Using this information I’ll be writing my User model class. I’ve named by model class to be AppUser.

I’ve added two class methods one for sign up and other for sign in.

Both the methods are similar and fairly understandable. Both take three parameters, email, password and a completion callback which further takes two parameters and returns nothing.

Let us go through the methods point by point.

  1. This is Auth class of Firebase Authentication APIs. The createUser method is used to create a new user in the Firebase’s user base of our application. On successful creation, it gives a User* object and in case of a failure, it results in an error and no User object. (User and error both are optional values.)
  2. Similar to the createUser method, there is a signIn method. This is used to sign in an existent user into the application. On successful completion, it gives a User object and in case of a failure, it results in an error and no User object i.e. a nil value.
  3. Placed the user optional under guard in order to unwrap it safely. If found, the unwrapped value is provided to the custom initializer method, which initializes an AppUser object and provides it along with the completion callback.

*User is the class used by Firebase for holding user data. This the reason I named my user model class as AppUser

This is what the class looks like.

Now using these to populate our test methods.

This is how AppUser’s class method for login needs to be called. I’ve provided a placeholder/dummy value to test out our code.

I know, I know. It is not complete yet!

See! I’ve added it now. If you revisit the method we wrote for our AppUser class you’ll find out that it returns an AppUser object if the login is successful. But in the case discussed above, the user is a guest so the user object should be a nil value, hence XCTAssertTrue for user == nil condition has been used.

Let us run our first test case.

Ah! The beautiful green tick!

Let us check it again by changing (user == nil) to  (user != nil).

Run it again.

Green again! How can it be! Something is wrong for sure! Scooby-doo, where are you?? :p

Let’s scooby dooby doo it, I mean “debug” it! (Sorry for getting carried away!)

You can notice that the control does not wait for closure to be called and results in a successful test execution. This shows one thing, during the test execution, the thread does not wait for the callback to be received and exits before the callback is made, which, in this case, is absolutely not desirable.

What should be done now?

Hold your horses, this isn’t that big a deal. Apple’s got us covered. Let’s find out.

  • One way is to have a loop call the main thread’s run loop until the response arrives or the timeout period gets expired. This is the old school way of doing it and we won’t go there. (Do let me know in the comments if you feel the need to know this method too).
  • The other way is to use XCTestExpectation.

XCTestExpectation:

It is nothing more than an expected outcome in an asynchronous test. When a test expectation is instantiated, the testing framework expects it to get fulfilled at some point in time.

Whenever the expectation seems to get fulfilled, we can call the fulfill method of the expectation instance to alert the test framework of the task done and to assert accordingly.

But the question still remains, how to keep observing the main thread’s run loop for an asynchronous callback. The answer is pretty simple and was answered too by Apple with XCode 6’s XCTest framework. We can make use of the waitForExpectations method.

Let us see all of this action! Expanding the aforementioned testAGuestLogin method.

The waitForExpectations method takes in two parameters, first one is a TimeInterval value which is used as the threshold for the expectation timeout and another one a handler, XCWaitCompletionHandlerwhich takes an Error optional as the parameter and returns nothing.

The test framework will wait for 5 seconds for the expectation to get fulfilled. As soon as the fulfill method is called the appropriate assert is made and test execution finally finishes.

On the similar grounds, we may test the createUser Firebase API method as well.

Try it out yourself. It is pretty simple, with some slight nuances to it.

Feel free drop some comments or emails for more discussion on this.

You may find the project here.

 

Peace ✌

Please follow and like us:

Leave a Reply

Your email address will not be published. Required fields are marked *