2017-05-31 81 views
0

我正在開發自己的簡單IoC庫,這是我想讓線程安全的一個庫。使用單元測試進行線程安全測試

我典型的單元測試是這樣的:

[TestMethod] 
public void TestContainerUseExistingObjectFromLifetimeManagerWithFactoryMethod() 
{ 
    // Arrange 
    var container = new FsContainer(); 

    container 
     .For<IRepository>() 
     .Use(ctx => new Repository("sql_connection_string"), new ContainerControlledLifetimeManager()); 

    // Act 
    var first = container.Resolve<IRepository>(); 
    var second = container.Resolve<IRepository>(); 

    // Arrange 
    Assert.AreEqual(first.ConnectionString, "sql_connection_string"); 
    Assert.AreEqual(second.ConnectionString, "sql_connection_string"); 
    Assert.AreSame(first, second); 
} 

這個偉大的工程之前,我想測試下一個方式:

[TestMethod] 
public async Task TestMultiThreadContainerUseExistingObjectFromLifetimeManagerWithFactoryMethodAsync() 
{ 
    // Arrange 
    var container = new FsContainer(); 

    container 
     .For<IRepository>() 
     .Use(ctx => new Repository("sql_connection_string"), new ContainerControlledLifetimeManager()); 

    // Act 
    var instances = await Task.WhenAll(
     Task.Run(() => container.Resolve<IRepository>()), 
     Task.Run(() => container.Resolve<IRepository>()) 
    ); 

    var first = instances[0]; 
    var second = instances[1]; 

    // Arrange 
    Assert.AreEqual(first.ConnectionString, "sql_connection_string"); 
    Assert.AreEqual(second.ConnectionString, "sql_connection_string"); 
    Assert.AreSame(first, second); 
} 

這個測試告訴我,說我有問題與Assert.AreSame(我的第一個&第二個實例不一樣)。

我已經在Resolve方法中實現了lock語句,並且一切都開始正常工作。

問:是不是複製的大部分功能在single正確的方式來測試線程safity multi-thread方式?是否有意義?

+0

我不知道你在使用什麼容器,但對我來說,它是非常有意義的,它爲不同的線程創建不同的管理對象實例。你確定這不是預期的行爲? – yorodm

+0

在這種情況下,我使用'ContainerControlledLifetimeManager'與單例模式相關(類似於UnityContainer')。 – FSou1

回答

1

在大多數情況下,如果不是不可能的話,線程安全性的測試很難。

你的第二個測試用例可能會暴露一些問題,但不能保證代碼的行爲正確。即它可以通過創建每個線程實例而不是一個全局實例來檢測違反設計的代碼,如果代碼一致地執行它,但很少有機會獲得並行訪問共享字典(或代碼存儲單例集合的任何集合)。你真的很幸運能夠真正檢測出測試的問題 - 可能是確保單例實例足夠慢以允許兩個線程啓動併發生問題的代碼。如果代碼是快速且有些正確的話,測試不會發現錯誤(即使用double-checked locking而不鎖定)。

對於編寫線程安全的代碼,您應該從已知的正確的保守代碼開始(即只是鎖定所有操作),並且在代碼審查證明正確性的地方進行小的更改(並進行有助於驗證功能的測試) 。

如果您擔心特定代碼段,有時您可能會故意減慢代碼(即構造函數/回調中的Sleep(1000))以強制代碼的特定時間。

+0

設置Task.Delay/Thread.Sleep是一個好主意,哪一個幫助我找出一些問題。謝謝你,阿列克謝 – FSou1