Geeks With Blogs

The Life and Times of a Dev Yes, we're really that weird

I spent the day today writing unit tests for code I wrote yesterday.  No, I’m not a big fan of TDD. Smile  I am, however, a big fan of high (as close to 100% as practical) code coverage.

The device I’m working on is very multi-threaded.  As a matter of fact, a key principle is DON’T BLOCK!  Because of this, there are quite a few Task’s, lots of awaiting, and several timers that poll on a regular basis.

This makes for code that can be tricky to unit test.  After all, if you’re not blocking, you’re on a different thread than the unit test thread and you need to make the unit test thread wait until the code has run its course.  Here’s a couple of things I’ve found to make this easier.

First, for any timers, like System.Timers.Timer, wrap them in an interface and then inject them with IoC.  Doing this, will let you raise the elapsed event whenever you want.  For example, the system.timers.timer class can easily be wrapped by something like this:

public class Timer : System.Timers.Timer, ITimer
{
    [InjectionConstructor]
    public Timer()
    {
    }

    public Timer(double interval)
        : base(interval)
    {
    }
}

This can then be mocked like this:

Mock timer = new Mock();
timer.Raise(t => t.Elapsed += null, (ElapsedEventArgs)null);

Note that ElapsedEventArgs has no public constructor, so the best you can do is pass in a null, which is far from ideal.

Second, if you’re going to be using Task.Run, you’ll want something that causes those tasks to run immediately.  I’ve wrapped the Task class with the following (Sorry for the bad formatting--I REALLY miss live writer plug-ins . . .):

using System;
using System.Threading;
using System.Threading.Tasks;

public class BackgroundTask : Task
{

public BackgroundTask(Action action)

: base(action)

{

}

public BackgroundTask(Action action, CancellationToken cancellationToken)

: base(action, cancellationToken)

{

}

public BackgroundTask(Action action, CancellationToken cancellationToken, TaskCreationOptions creationOptions)

: base(action, cancellationToken, creationOptions)

{

}

public BackgroundTask(Action action, TaskCreationOptions creationOptions)

: base(action, creationOptions)

{

}

public BackgroundTask(Action action, object state)

: base(action, state)

{

}

public BackgroundTask(Action action, object state, CancellationToken cancellationToken)

: base(action, state, cancellationToken)

{

}

public BackgroundTask(Action action, object state, CancellationToken cancellationToken, TaskCreationOptions creationOptions)

: base(action, state, cancellationToken, creationOptions)

{

}

public BackgroundTask(Action action, object state, TaskCreationOptions creationOptions)

: base(action, state, creationOptions)

{

}

public static bool RunTasksSynchronously { get; set; }

public static new Task Run(Action action)

{

if (RunTasksSynchronously)

{

Task task = new Task(action);

task.RunSynchronously();

return task;

}

return Task.Run(action);

}

public static new Task Run(Func function)

{

if (RunTasksSynchronously)

{

Task task = new Task(() => function.Invoke());

task.RunSynchronously();

return task;

}

return Task.Run(function);

}

public static new Task Run(Action action, CancellationToken cancellationToken)

{

if (RunTasksSynchronously)

{

Task task = new Task(action);

task.RunSynchronously();

return task;

}

return Task.Run(action, cancellationToken);

}

public static new Task Run(Func function, CancellationToken cancellationToken)

{

if (RunTasksSynchronously)

{

Task task = new Task(() => function.Invoke());

task.RunSynchronously();

return task;

}

return Task.Run(function, cancellationToken);

}

public static new Task Run(Func function)

{

if (RunTasksSynchronously)

{

Task task = new Task(function);

task.RunSynchronously();

return task;

}

return Task.Run(function);

}

public static new Task Run(Func> function, CancellationToken cancellationToken)

{

if (RunTasksSynchronously)

{

Task task = function.Invoke();

return task;

}

return Task.Run(function, cancellationToken);

}

public static new Task Run(Func function, CancellationToken cancellationToken)

{

if (RunTasksSynchronously)

{

Task task = new Task(function);

task.RunSynchronously();

return task;

}

return Task.Run(function, cancellationToken);

}

}

Note that setting RunTasksSynchronously in a non-testing environment will probably do bad things, but in testing makes all of your tasks run synchronously, which makes unit testing MUCH easier.

Now if only there were an easy way to write unit tests for race conditions!

Posted on Thursday, November 27, 2014 12:29 AM Unit Testing , Moq , C# , Threading | Back to top


Comments on this post: Unit Testing Multi-threaded code

# re: Unit Testing Multi-threaded code
Requesting Gravatar...
Good point about the need to test for race conditions. Practical experience has shown me the need to test under race conditions even if only by running two NUnit consoles at once.
Left by TATWORTH on Nov 27, 2014 11:08 AM

Your comment:
 (will show your gravatar)


Copyright © Robert May | Powered by: GeeksWithBlogs.net