Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to write unit tests with TPL and TaskScheduler

Imagine a function like this:

private static ConcurrentList<object> list = new ConcurrentList<object>();
public void Add(object x)
{
   Task.Factory.StartNew(() =>
   {
      list.Add(x); 
   }
}

I don't care WHEN exactly the fentry is added to the list, but i need it to be added in the end ( obviously ;) )

I don't see a way to properly unittest stuff like this without returning any callback-handler or sth. and therefor adding logic that's not required for the program

How would you do it?

like image 209
David Avatar asked Oct 21 '11 17:10

David


People also ask

What are the three essential A's related to unit testing?

The AAA (Arrange-Act-Assert) pattern has become almost a standard across the industry. It suggests that you should divide your test method into three sections: arrange, act and assert.


1 Answers

One way to do this is to make your type configurable such that it takes a TaskScheduler instance.

public MyCollection(TaskScheduler scheduler) {
  this.taskFactory = new TaskFactory(scheduler);
}

public void Add(object x) {
  taskFactory.StartNew(() => {
    list.Add(x);
  });
}

Now in your unit tests what you can do is create a testable version of TaskScheduler. This is an abstract class which is designed to be configurable. Simple have the schedule function add the items into a queue and then add a function to manually do all of the queue items "now". Then your unit test can look like this

var scheduler = new TestableScheduler();
var collection = new MyCollection(scehduler);
collection.Add(42);
scheduler.RunAll();
Assert.IsTrue(collection.Contains(42));

Example implementation of TestableScehduler

class TestableScheduler : TaskScheduler {
  private Queue<Task> m_taskQueue = new Queue<Task>();

  protected override IEnumerable<Task> GetScheduledTasks() {
    return m_taskQueue;
  }

  protected override void QueueTask(Task task) {
    m_taskQueue.Enqueue(task);
  }

  protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued) {
    task.RunSynchronously();
  }

  public void RunAll() {
    while (m_taskQueue.Count > 0) {
      m_taskQueue.Dequeue().RunSynchronously();
    }
  }
}
like image 170
JaredPar Avatar answered Oct 12 '22 23:10

JaredPar