2016-09-15 68 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

至少在當前版本的Visual Studio中,Windows測試支持異步/等待。只需等待你在普通代碼中的方法。使你的測試方法做'公共異步任務Send_ValidMessage_ExecuteWriteMethodWithGivenValue()',然後把'.Wait()'調用到'await'。 –

0

你需要建立某種「連接」。像這樣的東西應該就足夠了:

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

請注意,您也應該在您的生產代碼中使用它。如果你的代碼最終沒有觀察到_ComposerTask,那麼Start拋出的任何異常都將被無聲地吞噬。

+0

對不起,我覺得自己非常不安全,因爲只是因爲測試需要向類的公共API添加一些行爲。 – Fabio

+0

@Fabio:正如我在我的回答中指出的那樣,這段代碼也應該在你係統的其他地方使用。作爲一般規則,應該等待所有的任務。 –