Как проверить метод async void DelegateCommand, содержащий await с xUnit? - PullRequest
0 голосов
/ 13 июня 2019

Я впервые пишу модульные тесты для асинхронного метода. Я использую xUnit . Я искал SO без многообещающих результатов. Лучшее, что я нашел, но у меня не получилось, это реализовать IAsyncLifetime из ЭТОГО примера. Я буду благодарен за любые подсказки, как решить эту проблему.

В настоящее время, что у меня есть. В тестируемой ВМ у меня есть команда:

public ICommand TestResultsCommand { get; private set; }

И команда инициализируется в конструкторе VM, как показано ниже:

TestResultsCommand = new DelegateCommand(OnTestResultExecuteAsync);

Метод вызова команды:

private async void OnTestResultExecuteAsync(object obj)
        {
            TokenSource = new CancellationTokenSource();
            CancellationToken = TokenSource.Token;

            await TestHistoricalResultsAsync();
        }

TestHistoricalResultsAsync подпись метода, как показано ниже:

private async Task TestHistoricalResultsAsync()

Теперь давайте перейдем к проекту модульного теста. В настоящее время в тестовом классе у меня есть метод:

[Fact]//testing async void
        public void OnTestResultExecuteAsync_ShouldCreateCancellationTokenSource_True()
        {
            CancellationTokenSource tokenSource = new CancellationTokenSource();
            CancellationToken cancellationToken = tokenSource.Token;
            _viewModel.TestResultsCommand.Execute(null);
            Assert.Equal(cancellationToken.CanBeCanceled, _viewModel.CancellationToken.CanBeCanceled);
            Assert.Equal(cancellationToken.IsCancellationRequested, _viewModel.CancellationToken.IsCancellationRequested);
        }

И тест бросает мне исключение:

Сообщение: System.NullReferenceException: ссылка на объект не установлена ​​в экземпляр объекта.

Трассировка стека для исключения: enter image description here

Заранее благодарим вас за ваше время и предложения.

1 Ответ

2 голосов
/ 13 июня 2019

Одна из проблем async void методов заключается в том, что их сложно проверить.Для вашей проблемы большинство разработчиков делают одно из следующих действий:

  1. Определите и используйте интерфейс IAsyncCommand.
  2. Сделайте свою логику async Task и общедоступной.
  3. Используйте каркас, поддерживающий асинхронные команды, например, MvvmCross.

Подробнее см. В моей журнальной статье MSDN на тему .

Вот пример со вторымподход:

TestResultsCommand = new DelegateCommand(async () => await OnTestResultExecuteAsync());

public async Task OnTestResultExecuteAsync()
{
  TokenSource = new CancellationTokenSource();
  CancellationToken = TokenSource.Token;

  await TestHistoricalResultsAsync();
}

[Fact]
public async Task OnTestResultExecuteAsync_ShouldCreateCancellationTokenSource_True()
{
  CancellationTokenSource tokenSource = new CancellationTokenSource();
  CancellationToken cancellationToken = tokenSource.Token;
  await _viewModel.OnTestResultExecuteAsync();
  Assert.Equal(cancellationToken.CanBeCanceled, _viewModel.CancellationToken.CanBeCanceled);
  Assert.Equal(cancellationToken.IsCancellationRequested, _viewModel.CancellationToken.IsCancellationRequested);
}

Если вы не хотите выставлять async Task методы только ради модульного тестирования, тогда вы можете использовать IAsyncCommand некоторого вида;или ваш собственный (как подробно описано в моей статье) или один из библиотеки (например, MvvmCross).Вот пример использования типов MvvmCross:

public IMvxAsyncCommand TestResultsCommand { get; private set; }

TestResultsCommand = new MvxAsyncCommand(OnTestResultExecuteAsync);

private async Task OnTestResultExecuteAsync() // back to private
{
  TokenSource = new CancellationTokenSource();
  CancellationToken = TokenSource.Token;

  await TestHistoricalResultsAsync();
}

[Fact]
public async Task OnTestResultExecuteAsync_ShouldCreateCancellationTokenSource_True()
{
  CancellationTokenSource tokenSource = new CancellationTokenSource();
  CancellationToken cancellationToken = tokenSource.Token;
  await _viewModel.TestResultsCommand.ExecuteAsync();
  Assert.Equal(cancellationToken.CanBeCanceled, _viewModel.CancellationToken.CanBeCanceled);
  Assert.Equal(cancellationToken.IsCancellationRequested, _viewModel.CancellationToken.IsCancellationRequested);
}

Если вы предпочитаете подход IMvxAsyncCommand, но не хотите зависимости MvvmCross, нетрудно определить свои собственные IAsyncCommand и AsyncCommand типы .

...