2016-10-10 1 views
0

Это методы моего бизнес-движка. Загрузить звонит внутренне метод асинхронной UploadAsync()Подождите, пока не будет выполнен асинхронный метод void в блоке тестирования

public void Upload(Stream data) 
{ 
    //Some logic 
    //Call private async method 
    UploadAsync(data); 
} 

private async void UploadAsync(Object data) 
{ 
    await Task.Run(() => 
    { 
       using (var factory = new DataRepositoryFactoryObject<IAllCommandRepository>(DataRepositoryFactory)) 
       { 
        factory.Repository.UploadData(data); 
       } 
      } 
    ); 
} 

Это устройство тест для загрузки() метод

[TestMethod, TestCategory("Unit")] 
public void Upload_ValidData_Success() 
{ 
    //other logic to setup mock repository 
    //call public method which calls async method 
    engine.Upload(data); 

    _mockAllCommandRepository.Verify(x => x.Upload(It.Is<Object>(t => t != null)), Times.Once); 
} 

Этот тест становится не удалось occasioally на проверить метод с исключением следующего:

016-10-06T19:25:20.4982657Z ##[error]Expected invocation on the mock once, but was 0 times: x => x.Upload(It.Is<Object>(t => t != null)), Times.Once); 

Согласно моему методу проверки проверки вызывается перед вызовом метода async. Это может быть причиной отказа этого теста. Как я могу проверить, вызван ли метод при макете, когда сам метод вызывается в делегате, переданном в Task.Run? К моменту времени mock.Verify называется Task еще не выполнил. Может ли кто-нибудь предложить какое-то решение, чтобы этот тестовый пример прошел каждый раз

+0

Какая версия Visual Studio (издание)? – zaitsman

+1

В большинстве случаев огонь и забывание - плохая идея. Можете ли вы выполнить асинхронную задачу «Загрузить»? – qxg

+0

Вы не можете ждать метода async void. * Не используйте 'async void', особенно в этом случае. Это предназначено только для обработчиков событий или подобных функций. Здесь не так. Есть даже предупреждения Resharper и Roslyn о некорректном использовании 'async void' –

ответ

0

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

В этом случае проблема заключается в использовании возвращаемого типа void с методом async, что-то, что Стивен Клири advises against. Действительно, одной из причин, которые он приводит в своей статье, является:

Методы асинхронной пустоты трудно проверить.

Я думаю, что один из его наиболее убедительных аргументов против async void методов заключается в следующем:

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

Если вы еще не уверены, here's another article, который содержит еще несколько причин, чтобы избежать async void.

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

private async Task UploadAsync(Object data) 
{ 
    return Task.Run(() => { 
      using (var factory = new DataRepositoryFactoryObject<IAllCommandRepository>(DataRepositoryFactory)) 
      { 
       factory.Repository.UploadData(data); 
      } 
    }); 
} 

и тест ...

[TestMethod, TestCategory("Unit")] 
public void Upload_ValidData_Success() 
{ 
    //other logic to setup mock repository 
    //call public method which calls async method 
    engine.Upload(data).Wait(); 

    _mockAllCommandRepository.Verify(x => x.Upload(It.Is<Object>(t => t != null)), Times.Once); 
} 
+0

У меня есть никогда не работал в Microsoft. Я просто написал статью для журнала, который * опубликован * Microsoft. –

+0

извинения за очень плохое предположение, основанное на ваших, казалось бы, экспертных знаниях на async/await с самого раннего возраста. Исправленный! – tonicsoft

2

Как уже отмечалось, лучшее Решение, чтобы сделать метод возвращения Task.Task -returning async методов более легко проверяемый:

private async Task UploadAsync(Object data) 
{ 
    await Task.Run(() => 
    { 
    using (var factory = new DataRepositoryFactoryObject<IAllCommandRepository>(DataRepositoryFactory)) 
    { 
     factory.Repository.UploadData(data); 
    } 
    }); 
} 

private async void Upload(Object data) 
{ 
    await UploadAsync(data); 
} 

Тогда ваш блок тестирование может быть:

[TestMethod, TestCategory("Unit")] 
public async Task Upload_ValidData_Success() 
{ 
    //other logic to setup mock repository 
    //call public method which calls async method 
    await engine.UploadAsync(data); 

    _mockAllCommandRepository.Verify(x => x.Upload(It.Is<Object>(t => t != null)), Times.Once); 
} 

Однако, если по какой-то причине вы необходимости для тестирования async void метода, возможно, делать с использованием моих AsyncContext type:

[TestMethod, TestCategory("Unit")] 
public void Upload_ValidData_Success() 
{ 
    //other logic to setup mock repository 
    //call public method which calls async method 
    AsyncContext.Run(() => engine.Upload(data)); 

    _mockAllCommandRepository.Verify(x => x.Upload(It.Is<Object>(t => t != null)), Times.Once); 
} 

Сторона примечания: В идеале al, я бы рекомендовал добавить UploadDataAsync в ваш репозиторий, а затем удалить Task.Run.

Смежные вопросы