Unity: Testing async functions that returns IPromise

Viewed 647

I'm new to Unity and found its asynchronous management a little bit difficult to handle, so I'm using IPromises : https://github.com/Real-Serious-Games/C-Sharp-Promise which allows me to use

MyAsyncFunction.Then(() => 
{
  // What happened if everything went OK
}).Catch(error =>
{
  // What happend if an exception was thrown
})

I'm also using Unity Rest Client, which uses IPromises: https://github.com/proyecto26/RestClient

I'm using NUnit for testing and it seems it has to return a Task when testing async code.

In my code, I use the Unity Rest Client and put my Assert's in the Then part. The problem is that NUnit doesn't wait for the request and thus does not perform the assertions.

Here is my code:

        [Test]
        public async Task TestLogin()
        {
            _network.SendCode(_password)
                .Then(authResp =>
                {
                    Assert.True(authResp.IsSuccessful);
                    Assert.IsNotNull(authResp.Name);
                    Assert.IsNotNull(authResp.Surname);
                    Assert.IsNotNull(authResp.AccessToken);
                    Assert.AreEqual(authResp.AccessToken, _tokenStorage.RetrieveAccessToken());
                });
        }

And here is the implementation of SendCode:

        public IPromise<AuthenticationResponse> SendCode(string code)
        {
            var promise = new Promise<AuthenticationResponse>();

            RestClient.Post("/api/login", new Credentials(code))
                .Then(response =>
                {
                    EditorUtility.DisplayDialog("JSON", JsonUtility.ToJson(response, true), "Ok");
                    promise.Resolve(new AuthenticationResponse("", "", true, "", "200"));
                })
                .Catch(error =>
                {
                    EditorUtility.DisplayDialog("ERROR", JsonUtility.ToJson(error, true), "Ok");
                    promise.Reject(new Exception("Error when logging"));
                });

            return promise;
        }

I see 2 possibilities:

  1. Transforming the IPromise to a Task in the test
  2. Changing the implementation of SendCode so that it returns a Task. And renounce to use the UnityRestClient :'(

If anyone know how to do the first possibility or can give me a little bit of guidance to do the second one, it would be amazing.

1 Answers

I was looking on C-Sharp-Promise git respository and I came across an issue titled Not Usable inside Tasks?. One of the comments includes an example of using a promise with an async method by using a TaskCompletionSource, as follows:

private Task AuthenticateAsync()
{
    var tcs = new TaskCompletionSource<object>();

    RestClient.Post<signInResponse_model>(someUri, someString)
        .Then(res =>
        {
            currentIDToken = res.idToken;
            currentUID = res.idToken;

            tcs.SetResult(null);
        })
        .Catch(error =>
        {
            Debug.LogException(error);
            tcs.SetException(error);
        });

    return tcs.Task;
}

Also, the author of the comment made notable point:

While technically it is possible to use promises with async/await code, you should never do this. Choose one approach an go with it all the way.

I don't think it's fair to say it should never be done, but it would be fair to say to avoid this if possible. I think in your case, if you really want to use the Unity Rest Client, you'll have no choice but to combine them. However, I think accessing a REST service from within a Unity application is not an unusual use case so, with some research, it should be possible to find an alternative approach that uses Tasks, if you aren't set on using the Unity Rest Client.

Related