如何确保线程代码的覆盖率
How can I ensure coverage of threaded code
我正在尝试编写一个单元测试,它可以在 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" 块?
这有点乱七八糟,但您可以使用反射从示例 class 实例中获取 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
}
}
}
稍作设计更改,您就可以使代码更易于测试:将对 objectToInit.HasValue
的访问权限提取到像 hasObjectToInitValue()
这样的辅助方法中。在您的测试中,您可以覆盖该方法,然后可以测试辅助方法在第一次调用 returns false
和第二次调用 returns true
.[=14 时的场景=]
我正在尝试编写一个单元测试,它可以在 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" 块?
这有点乱七八糟,但您可以使用反射从示例 class 实例中获取 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
}
}
}
稍作设计更改,您就可以使代码更易于测试:将对 objectToInit.HasValue
的访问权限提取到像 hasObjectToInitValue()
这样的辅助方法中。在您的测试中,您可以覆盖该方法,然后可以测试辅助方法在第一次调用 returns false
和第二次调用 returns true
.[=14 时的场景=]