Подтвердить что ты не робот

Почему асинхронные модульные тесты терпят неудачу, если ключевые слова async/await не используются?

В соответствии с этим обсуждением не должно быть разницы между двумя следующими способами:

public async Task Foo()
{
    await DoSomethingAsync();
}

public Task Foo()
{
    return DoSomethingAsync();
}

На самом деле, казалось бы, для очень простых методов было бы предпочтительным использовать вызов без ключевых слов async/await, поскольку они устраняют некоторые издержки.

Однако это, по-видимому, не всегда работает в модульных тестах.

MSTest

[TestClass]
public class AsyncTest
{
    [TestMethod]
    public async Task Test1()
    {
        await Task.Delay(0);
    }

    [TestMethod]
    public Task Test2()
    {
        return Task.Delay(0);
    }
}

NUnit

[TestFixture]
public class AsyncTest
{
    [Test]
    public async Task Test1()
    {
        await Task.Delay(0);
    }

    [Test]
    public Task Test2()
    {
        return Task.Delay(0);
    }
}

XUnit

public class AsyncTest
{
    [Fact]
    public async Task Test1()
    {
        await Task.Delay(0);
    }

    [Fact]
    public Task Test2()
    {
        return Task.Delay(0);
    }
}
  • Во всех случаях проходит Test1.
  • В MSTest, Test2 отображается в тестовом бегуне, но он не запускается.
  • В NUnit, Test2 игнорируется, с сообщением:

    Метод тестирования имеет непустой тип возврата, но результат не ожидается

  • В XUnit проходит Test2.

Так как задачи все еще ждут во всех случаях, что это за ключевое слово async, которое влияет на тестовые бегуны NUnit и MSTest? Возможно, проблема отражения?

4b9b3361

Ответ 1

Похоже, что те тест-бегуны могут использовать отражение, чтобы проверить, действительно ли метод, возвращающий Task, асинхронный метод. Это не означает, что метод будет вести себя по-другому, если они будут запущены, но они просто не запускаются.

Мне нравится говорить, что:

public string Name { get; set; }

эквивалентно:

private string name;
public Name { get { return name; } set { name = value; } }

Они логически одинаковы с точки зрения поведения, но если вы достаточно усердничаете с отражением, вы можете сказать разницу. В этом конкретном случае есть и другие более тонкие различия, но применяется тот же общий принцип.

Похоже, что в текущем коде NUnit (на момент написания) обнаружение находится в AsyncInvocationRegion.cs.

По общему признанию, по крайней мере, необычно писать unit test, возвращающий Task, но без использования метода async, но далеко не невозможный.