Search Unity

Resolved Unity 2017.1 Unit testing and waiting for WWWForm

Discussion in 'Testing & Automation' started by HaakonL, Sep 29, 2017.

  1. HaakonL

    HaakonL

    Joined:
    Mar 13, 2014
    Posts:
    123
    Hi,

    how should I write a test involving an external API call using the Unity3d test runner? I am unable to find a way to make the test runner await a callback. Take a look at this simplified example:

    [Test]
    public void TestingAsyncCall()
    {
    var result = false;
    new WWWApiCall(_ =>
    {
    result = true;
    });

    Assert.AreEqual(true, result);
    }
    The assert fails, result is still false but ought to be true...
     
  2. liortal

    liortal

    Joined:
    Oct 17, 2012
    Posts:
    3,562
    If you are on Unity 2017.1, you can use the new test infrastructure, and specifically the UnityTest attribute.
    This attribute allows you to declare and execute your test as a coroutine.

    You could write the test in your example similar to this:
    Code (CSharp):
    1. [UnityTest]
    2. public IEnumerator TestingAsyncCall()
    3. {
    4.     var timeout = 5f; // timeout after 5 seconds
    5.     var result = false;
    6.  
    7.     new WWWApiCall(_ =>
    8.     {
    9.         result = true;
    10.     });
    11.  
    12.     yield return new WaitForSeconds(timeout);
    13.  
    14.     Assert.AreEqual(true, result);
    15. }
    Not the best implementation as it will always wait for the timeout before asserting, but you can enhance it if you like.
     
  3. HaakonL

    HaakonL

    Joined:
    Mar 13, 2014
    Posts:
    123
    Hi, thanks for the answer. I actually already tested it, but it does not work. And even if it _did_ work, the hack is just way too ugly, and it locks you to using the rigid UnityTest instead of the regular nunit Test.

    Here is a complete example that illustrates how testing async is not working and so far seems impossible in Unity.

    Code (CSharp):
    1. [UnityTest]
    2.         public IEnumerator TestingAsyncCall()
    3.         {
    4.  
    5.             var result = false;
    6.  
    7.             Task.Run(() =>
    8.             {
    9.                 result = true;
    10.             });
    11.  
    12.             yield return new WaitForSeconds(1);
    13.  
    14.             Assert.AreEqual(true, result);
    15.         }
    Leads to: Unhandled log message: [Error] EditMode test can only yield null.

    I can change it to yield return null, but then the wait time needed for an async web api call is not going to be suficcient.
     
  4. liortal

    liortal

    Joined:
    Oct 17, 2012
    Posts:
    3,562
    async testing is possible. it doesn't "lock" you to anything. nunit tests are not async. they are triggered and completed, and cannot wait for any async operation to complete.

    The UnityTest attribute solves this by executing your test as a coroutine.

    The error you're getting means that you placed your test in an Editor folder. move it into another folder (e.g: non editor) and it should work.
     
  5. PlayItSafe_Fries

    PlayItSafe_Fries

    Joined:
    Jan 17, 2018
    Posts:
    11
    The error log tells you UnityTests can not yield return anything other than null, in EditMode.
    Either you can make it a runtime test by moving your test code to a file not in an editor folder,
    or you can work around the "yield return new WaitForSeconds(1)".

    Code (CSharp):
    1. public IEnumerator TestingAsyncCall()
    2.     {
    3.  
    4.         var result = false;
    5.  
    6.         Task.Run(() =>
    7.         {
    8.             result = true;
    9.         });
    10.  
    11.         DateTime startTime = DateTime.UtcNow;
    12.         do
    13.         {
    14.             yield return null;
    15.         }
    16.         while ((DateTime.UtcNow - startTime).TotalSeconds < 1.0);
    17.  
    18.         Assert.AreEqual(true, result);
    19.     }
     
    piersb likes this.