I am struggling with writing unittests for async methods since the unittest itself can not be async. I am trying to work around this by using task.Wait() but this seems to result in a deadlock in certain situations.
See the following code:
I guess that some of the code in GetTestTask will run on a different thread and try to sync up with the main thread while the mainthread is blocked because of task.Wait().
How can i work around this issue, and is this a bug or a feature?
Any help appreciated!
Alternatively, you could use the Trilleon Automation Framework that supports both Unit Testing and Integration Automation tests. It uses Coroutines to simulate asynchronocity, but it does not use multi threading or actual async (with a socket connection for server communication being the exception to that). But given your sample, it doesnât look as if you need real async. You just need something that can wait for work to be done before continuing the test execution. (Trilleon uses âWaitForâ which passes in a predicate that can express any boolean state, such as âis this work done yet?â).
It is an entire framework, and will take care of this problem for you. However, your Unit Tests written in Trilleon will not run on compilation. It is not designed identically to something like NUnit. It however allows you to unit test IEnumerators and anything that requires gameplay context to validate.
Unity uses an old Nunit framework. I donât know which version. But it does not offer proper async Task support.
You can use a workaround thatâs inefficient but works: Execute the async test logic on a different thread pool thread, and then (synchronously) block the unit test method until the actual test completes.
@Mathijs_Bakker works like a charm!
And if you need to get a result out of a task, it works in the similar way:
(You can change UnityTest to just Test if your NUnit version requires it to be like that)
[UnityTest]
public void TestAwait()
{
var task = Task.Run(async () =>
{
return await GetTestTaskAsync();
});
Assert.AreEqual(1, task.Result);
}
public async Task<int> GetTestTaskAsync()
{
await Task.Delay(TimeSpan.FromMilliseconds(200));
return 1;
}
I have these static Await utilities, similar to @ and @JakHussain âŚ
public static IEnumerator Await(Task task)
{
while (!task.IsCompleted) { yield return null; }
if (task.IsFaulted) { throw task.Exception; }
}
public static IEnumerator Await(Func<Task> taskDelegate)
{
return Await(taskDelegate.Invoke());
}
Which can be used to âawaitâ for an async delegate or an async Task within a coroutine likeâŚ
[UnityTest]
public IEnumerator TestSomeAsyncThing() {
yield return Await(async () => {
await UniTask.Delay(TimeSpan.FromSeconds(5)); // example async thing
});
}
async Task AsyncTestMethod()
{
await UniTask.Delay(TimeSpan.FromSeconds(5)); // example async thing
}
[UnityTest]
public IEnumerator TestSomeOtherAsyncThing() {
yield return Await(AsyncTestMethod());
}
The main reasons I went with this approach:
Calling GetResult() or reading Result, as above, blocks the main thread which is the thread most of the Unity code I want to test is designed to run from.
It avoids using Task.Run() (which will run code via a thread pool), again because most of the Unity code Iâm looking to test is designed to run on the main thread (or will handle spawning work on threads itself where appropriate)
The static Await() utilities feel similar to using the await keyword but within a coroutine
It supports the convenience of using an async lambda for writing tests
Hi. Team owning the Unity Test Framework package (UTF, also known as Test Runner) has recently made available a pre-release version of v2.0 - you can read the announcement here . One of the new features we added, and are looking to get usersâ eyes on it, are async tests. Weâd appreciate any feedback and comments, if you decide to test it out. Thanks!
Thanks! It would be great if you could post in the main thread where we made the announcement . Thatâs where a lot of discussion is happening and weâre actively monitoring it with the team.