2017-05-31 177 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