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