2015-11-21 3 views
-2

У меня есть простой класс с простым методом, как это:блока тестирования асинхронного код

partial class SimpleClass 
{ 
    private readonly ISimpleManager _simpleManager; 
    public SimpleClass(ISimpleManager simpleManager) 
    { 
     _simpleManager = simpleManager; 
    } 

    public async void SimpleMethod() 
    { 
     IsInProgress = true; 
     DoSomeWork(); 
     Task<int> hardWork0Task = _simpleManager.DoHardWork0Async(); 
     Task<int> hardWork1Task = _simpleManager.DoHardWork1Async(); 
     DoIndependentWork(); 
     int hardWork0Result = await hardWork0Task.ConfigureAwait(false); 
     DoDependentWork(hardWork0Result); 
     int hardWork1Result = await hardWork1Task.ConfigureAwait(false); 
     DoDependentWork(hardWork1Result); 
     IsInProgress = false; 
    } 
} 

Давайте предположим, что свойство IsInProgress только bool свойства уведомления GUI о своем состоянии, чтобы обеспечить освежающий прогресс бар. DoSomeWork, DoDependentWork и DoIndependentWork - это некоторые методы, использующие или не результаты тяжелой работы.

ISimpleManager самый простой интерфейс, вы можете себе представить, в данном случае:

interface ISimpleManager 
{ 
    Task<int> DoHardWork0Async(); 
    Task<int> DoHardWork1Async(); 
} 

я должен написать несколько модульных тестов с помощью Moq и NUnit. Как я могу написать модульные тесты для этого случая? Я хотел бы проверить, не изменилось ли свойство IsInProgress на false во время всего кода, выполняемого асинхронно с графическим интерфейсом. Имеет ли это смысл? Является ли это возможным? Что делать, если мой метод async возвращает Task или общий Task<T>? Что делать, если я настроюсь на true?

+0

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

+1

Асинхронный метод, который возвращает 'void', является плохими идеями. Я бы предложил удалить свойство IsInProgress и вернуть 'Task'. –

+0

@ Сергий Березовский, ок, насколько я упоминал, я не против использования 'Task' или generic' Task 'вместо' void'. Не могли бы вы показать мне ответ в полном решении только для метода возврата «Задача»? Это было бы полезно для меня. И у меня есть дополнительный вопрос. Если вы разрабатываете тесты для модели просмотра Caliburn, у вас есть некоторые методы, которые должны быть «async void», т. Е. Действие связано с событием нажатия кнопки. Что делать в этом случае? Показать публично метод возвращает 'Задача', кроме метода, возвращающего' async void'? Это звучит не так хорошо для меня, потому что вы меняете свой код сут, чтобы его можно было тестировать. – pt12lol

ответ

1

Вам просто нужно когда выполняются задачи ISimpleManager. Вы можете сделать это с помощью SemaphoreSlim, в соответствии с ответом Илии, или вы можете просто использовать TaskCompletionSource<T> напрямую. Обычно я предпочитаю TaskCompletionSource<T>, потому что это проще; однако экземпляр SemaphoreSlim может быть повторно использован, а TaskCompletionSource<T> может запускаться только один раз.

На стороне примечания следует избегать async void, если этот метод не обработчик события. Он должен не быть «дефолтом» любого типа - по умолчанию должно быть возвращено Task, если вы абсолютно не не может. Поэтому в этом примере SimpleMethod обязательно должен вернуть Task.

Вот что это может выглядеть следующим образом, используя ТКС:

async Task MyTestMethod() 
{ 
    // Set up the mock object 
    var tcs0 = new TaskCompletionSource<int>(); 
    var tcs1 = new TaskCompletionSource<int>(); 
    var stub = new Mock<ISimpleManager>(); 
    stub.Setup(x => x.DoHardWork0Async()).Returns(tcs0.Task); 
    stub.Setup(x => x.DoHardWork1Async()).Returns(tcs1.Task); 

    var sut = new SimpleClass(stub.Object); 
    var task = sut.SimpleMethod(); 

    Assert.True(sut.InProgress); 
    tcs0.SetResult(7); 
    Assert.True(sut.InProgress); 
    tcs1.SetResult(13); 
    await task; 
    Assert.False(sut.InProgress); 
} 
+0

Работает как и ожидалось. Даже для подписи «async void» (конечно, исключая ключевые слова 'async' и' await' в тесте). – pt12lol

0

Вот как вы можете блок-тест, который IsInProgress установлен правильно:

  1. Создание awaitable семафор:

    var sem = new SemaphoreSlim(1); 
    
  2. Создать окурок ISimpleManager со следующей реализацией:

    async Task<int> DoHardWork0Async() { 
        await sem.WaitAsync(); 
        return 0; 
    } 
    
  3. Звоните SimpleMethod. Он застрянет на sempahore внутри вызова DoHardWork0Async

  4. Проверьте эффекты IsInProgress

  5. В качестве шага очистки, освободить семафор:

    sem.Release();