我如何编写单元测试以确保获得锁定?
例如:
public void AddItem(object item)
{
lock (_list)
{
_list.Add(item)
}
}
有没有办法确保运行lock
语句?
编辑:我不是要证明线程安全(我会假设),只是调用了lock()语句。
例如,我可以通过在new object()
工厂函数中替换它来测试CreateObject()
语句。
答案 0 :(得分:8)
单元测试多个线程总是很棘手,应该小心处理。
在您的情况下,我不会费心测试lock
关键字,原因与您不为new
编写测试的原因相同。
Assert.IsNotNull(new object());
此外,您似乎将其封装为线程不安全的集合,以使其成为线程安全的。考虑使用 thread-safe collections 。
,而不是重新发明轮子答案 1 :(得分:4)
与测试任何其他东西的方式相同:编写一个没有它的测试失败。换句话说,首先确定您编写锁的原因,并编写指定该原因的测试。我假设原因是线程安全:如果是,请编写多线程测试。如果是其他原因,请进行测试。
如果你真的,真的想测试调用锁而不是锁导致的行为,请封装:
public class MyLock : IDisposable
{
private object _toLock;
public MyLock(object toLock)
{
_toLock = toLock;
Monitor.Enter(_toLock);
}
public virtual void Dispose()
{
Monitor.Exit(_toLock);
}
}
当然,你也必须建立一个可模拟的工厂。似乎过度使用它,但也许它在你的背景下是有道理的。
答案 2 :(得分:3)
请参阅Jon Skeet对类似问题的回答:How to test if a thread is holding a lock on an object in C#?:
我不相信有。你可以做的事情很糟糕 喜欢调用Monitor.Wait(监视器,0)并捕获 SynchronizationLockException,但这非常可怕(并且可以 理论上“捕获”另一个线程正在等待的脉冲)。 编辑:在.NET 4.5中,这可用于Monitor.IsEntered。
答案 3 :(得分:1)
如果您已经编写了Lock语句(它执行与System.Threading.Lock语句类似的操作),那么我可以看到您为什么要测试它。
在这种情况下,您需要有一个_list类,您已经为此类实现了.Add方法,如果您使用依赖注入注入IList来设置_list,这将更容易。您需要使用IList的虚拟实例来实现.Add()方法。
如果你有一个假的.Add()调用sleep一段时间(比如5秒),你可以测试一下,启动一个线程来调用.AddItem()方法,这个线程会锁定.Add ()通过.AddItem()方法调用,然后主线程可以在调用.AddItem方法之前等待3秒。
如果锁定工作,第二个线程将在执行.Add之前延迟2秒,如果锁定不起作用,它将直接调用。
这是混乱和非确定性的,所以如果你运行足够的时间(数百万),你将得到错误的结果。
答案 4 :(得分:0)
你需要一个小程序来模拟读写线程......
在一个新线程中创建10,000个项目并添加它们,而在另一个线程中读取然后再睡眠一段时间。
然后在主要等待两个线程退出,你应该根据测试添加的次数得到结果并且没有调用remove ...
例如当i%2 == 0然后删除而不是添加生成器线程...这也应该影响消费者线程......
答案 5 :(得分:0)
不确定单元测试锁是否是正确的方法,而是应该正确测试AddItem
的功能。但是你可以做几件事:
1)使用反射获取_list
的引用,将其锁定在测试中,然后尝试调用AddItem
2)添加internal
方法进行测试,锁定_list
并暂停一段合理的时间(2秒?)。如果您make your internals visible to your test assembly,您可以在一个线程上调用测试方法,并在另一个线程上调用AddItem
。请记住,这不是万无一失的,因为从技术上讲,测试可能会因为某种原因而被阻止,足以让初始锁定到期。