我正在尝试编写一个单元测试,该测试可以100%地命中一段线程代码来覆盖代码。该代码只能在线程应用程序的上下文中访问,并且设计为每个对象实例仅被命中一次,以最大程度地减少锁定时间。
作为简化示例:
public class Example
{
private readonly object lockObject = new object();
private int? objectToInit = null;
public void EnsureObjectInit()
{
//To minimize hitting the lock code, the object is checked first.
if (!objectToInit.HasValue)
{
lock (lockObject)
{
//Then it is checked once more to ensure that it wasn't initiazlized
//before the lock was hit.
if (objectToInit.HasValue)
{
//This block can only be executed if a second thread is able to
//get to the lock before the first can initialize the object.
return;
}
objectToInit = 0;
}
}
}
}
要获得覆盖第二条if语句中代码的代码,我尝试过这样的代码:
[TestClass]
public class ExampleTest
{
[Test Method]
public void async MyTest()
{
Example example = new Example();
void runTask()
{
example.EnsureObjectInit();
}
//I've tried increasing the number of threads, but it doesn't make
//it hit the noted code any more consistently.
List<Task> tasks = new List<Task>(2);
tasks.Add(Task.Run(runTask));
tasks.Add(Task.Run(runTask));
await Task.WhenAll(tasks);
... perform extra validation ...
}
}
但是,如果至少有50%的时间阻塞,则代码覆盖率无法到达内部。
有没有办法强制经过单元测试的代码在初始化对象之前停止第一个线程,以便第二个线程可以进入第一个“ if”块?
答案 0 :(得分:1)
有点麻烦,但是您可以使用反射从Example类实例中获取lockObject
。然后将其手动锁定在ExampleTest方法中。注意:这是一个高度耦合的测试。如果您更改锁的名称,则测试将失败。
private class LockExample
{
private readonly object lockObject = new object();
public void TestLock()
{
lock(lockObject)
{
//Do something
}
}
}
private class LockTest
{
public void Test()
{
var example = new LockExample();
var lockOjbect = typeof(LockExample).GetField("lockObject", BindingFlags.NonPublic|BindingFlags.Instance).GetValue(example);
lock (lockOjbect)
{
var task = Task.Run((Action)example.TestLock);
task.Wait(1); //allow the other thread to run
}
}
}
答案 1 :(得分:0)
只需进行少许设计更改,就可以使代码更易于测试:将对objectToInit.HasValue
的访问提取到诸如hasObjectToInitValue()
这样的辅助方法中。在测试中,您可以覆盖该方法,然后可以测试以下情况:助手方法在第一次调用时返回false
,在第二次调用时返回true
。