John Chesshir
John Chesshir

Reputation: 640

How can I ensure coverage of threaded code

I am trying to write a unit test that can hit a piece of threaded code 100% of the time for code coverage. The code can ONLY be reached in the context of a threaded application, and is engineered to be hit only once for every object instance in order to minimize lock time expense.

As a simplified example:

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;
      }
    }
  }
}

To get code coverage to hit the code inside the second if statement, I've tried code like this:

[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 ...
  }
}

But code coverage fails to reach the inner if block at least 50% of the time.

Is there any way to force the unit tested code to stop the first thread before initializing the object so that the second thread can get into the first "if" block?

Upvotes: 0

Views: 227

Answers (2)

Dirk Herrmann
Dirk Herrmann

Reputation: 5939

With a slight design change you can make your code easier testable: Extract the access to objectToInit.HasValue into a helper method like hasObjectToInitValue(). In your test, you can then override that method and can then test the scenario that the helper method on first call returns false and on second returns true.

Upvotes: 0

bwing
bwing

Reputation: 781

It's a bit of a hack, but you could use reflection to get the lockObject from the Example class instance. Then lock it manually in the ExampleTest method. NOTE: this is a highly couple test. If you change the name of the lock the test will fail.

    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
            }
        }
    }

Upvotes: 1

Related Questions