C# 如何确保线程代码的覆盖率
我正在尝试编写一个单元测试,它可以100%地命中一段线程代码,从而实现代码覆盖率。代码只能在线程化应用程序的上下文中访问,并且设计为每个对象实例只命中一次,以最小化锁定时间开销 作为一个简化的例子:C# 如何确保线程代码的覆盖率,c#,multithreading,unit-testing,code-coverage,mstest,C#,Multithreading,Unit Testing,Code Coverage,Mstest,我正在尝试编写一个单元测试,它可以100%地命中一段线程代码,从而实现代码覆盖率。代码只能在线程化应用程序的上下文中访问,并且设计为每个对象实例只命中一次,以最小化锁定时间开销 作为一个简化的例子: public class Example { private readonly object lockObject = new object(); private int? objectToInit = null; public void EnsureObjectInit() {
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块
有没有办法强制单元测试代码在初始化对象之前停止第一个线程,以便第二个线程可以进入第一个if块?这有点麻烦,但是您可以使用反射从示例类实例获取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的帮助器方法中。在测试中,您可以覆盖该方法,然后测试第一次调用的helper方法返回false,第二次调用的helper方法返回true的场景。是否可以模拟它以确保覆盖所需的代码位?好主意。我要做的唯一更改是在任务上使用Wait关键字,而不是任务的Wait函数。可能只是语义上的,但“await”将阻止当前线程,直到任务完成,这可能会导致死锁。Wait1只是停止当前线程足够长的时间,让另一个线程启动并被锁阻塞。