2016-09-15 5 views
1
public class Composer 
{ 
    private Task _ComposerTask;  
    private ConcurrentQueue<IValue> _Values; 
    public bool IsConnected { get; } 

    // Other dependencies 
    private IClient _Client; 
    private IWriter _Writer 

    public Task async ConnectAsync() 
    { 
     this.IsConnected = await _Client.ConnectAsync(); 
     _ComposerTask = Task.Run(() => this.Start()); 
    } 

    private void Start() 
    { 
     while(this.IsConnected) 
     { 
      IValue value; 
      if(_Values.TryDequeue(out value) == false) 
       continue; 

      _Writer.Write(value); 
     } 
    } 

    public void Send(IValue value) 
    { 
     _Values.Enqueue(value); 
    } 
} 

При подключении успешно Composer класс выполнения Start метод асинхронно (в другом потоке).
Start метод проверяет очередь значений и отправляет их вперед, если существует значение.методы тестирования блока на другой нити

Моя проблема в тестировании метода Send.

[Test] 
public void Send_ValidMessage_ExecuteWriteMethodWithGivenValue() 
{ 
    // Arrange 
    var fakeValue = Mock.Create<IValue>(); 
    var fakeWriter = Mock.Create<IWriter>(); 
    var fakeClient = Mock.Create<IClient>(); 

    Mock.Arrange(() => fakeClient.ConnectAsync().Returns(CompletedTask); 

    var composer = new Composer(fakeClient, fakeWriter); 

    // for (int i = 0; i < 10; i++) 
    // { 
    //  composer.Send(Mock.Create<IValue>()); 
    // } 

    composer.ConnectAsync().Wait(); 

    // Act 
    composer.Send(fakeValue); 

    // Assert 
    Mock.Assert(() => fakeWriter.Write(fakeValue), Occurs.Once()); 
} 

С прокомментированным for loop тест прошел. Но если исполняемая for loop и внутренняя очередь будут заполнены даже 10 значениями до ожидаемого добавленного значения, тогда тест завершится с сообщением: ожидается как минимум один раз, но встречается 0 раз.

Как я понимаю, утверждение происходит до того, как значение было поставлено в очередь другим потоком, но как такое поведение можно протестировать?

ответ

0

Мое решение, которое я придумал это редизайн Composer класса или более конкретных изменений Send метод асинхронного:

public Task SendAsync(IValue value) 
{ 

} 

Идея позади возвращенно Task, который завершается, когда дается значение состоит вперед на «фоне» нить.

Ед. Изм. Требуется только await, пока задача не будет выполнена и не будет выполнена надлежащее выполнение.

[Test] 
public async Task SendAsync_ValidMessage_ExecuteWriteMethodWithGivenValue() 
{ 
    // Arrange 
    var composer = TestFactory.GenerateComposer(); 

    // var tasks = new List<Task>(); 
    // for (int i = 0; i < 10; i++) 
    // { 
    //  tasks.Add(composer.SendAsync(Mock.Create<IValue>())); 
    // } 

    await composer.ConnectAsync(); 

    // Act 
    await composer.SendAsync(fakeValue); 

    // Assert 
    Mock.Assert(() => fakeWriter.Write(fakeValue), Occurs.Once()); 
} 

Мой оригинальный тест блок не был успешным, даже без дополнительных значений, добавленных в for loop. Иногда тест прерывался, если он выполнялся многократно. Я думаю, что причиной является «непредсказуемая» работа пула потоков.

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

+1

Windows тесты поддерживают async/ждут, по крайней мере, в текущих версиях визуальной студии. Просто ждите таких методов, как в обычном коде. сделайте свой тестовый метод «public async Task Send_ValidMessage_ExecuteWriteMethodWithGivenValue()», а затем вызовите '.Wait()' вызовы 'await'. –

0

Вам нужно будет создать какое-то «соединение». Что-то вроде этого должно быть достаточно:

public Task JoinAsync() 
{ 
    this.IsConnected = false; 
    return _ComposerTask; 
} 

Обратите внимание, что вы также должны использовать это в своем производственном коде. Если ваш код в конце концов не замечает _ComposerTask, то любые исключения, сделанные Start, будут проглатываться молча.

+0

Извините, я чувствую себя очень небезопасным, когда вам нужно добавить какое-то поведение в открытый API этого класса только из-за тестов. – Fabio

+0

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