Showing posts with label tdd. Show all posts
Showing posts with label tdd. Show all posts

Wednesday, November 28, 2012

Unit testing asynchronous operations with the Task Parallel Library (TPL)

Unit testing asynchronous operations has never been easy in C#. The most common methods (or at least the methods I usually end up with) is either;
  1. Write a synchronous version of the method to test, unit test this one and then call the synchronous method from another method that runs it asynchronous in the production code.
  2. Raise an event in the production code when the asynchronous operation has finished, subscribe to the event in the unit test, and use the ManualResetEvent to wait for the event before making any assertions.
Neither is a good solution.
Writing a synchronous version and let the production code call it is probably the easiest one, but breaks down once you need to do more than just call the synchronous method in production (e.g. orchestrating several dependent asynchronous operations, or have some logic run when the asynchronous operation(s) completes). And the worst part of it; a vital part of the production code will be untested.
The ManualResetEvent is better, but it takes a lot more code, makes the unit tests harder to read and you need to fire events in the prod code that possibly only unit tests are interested in. And unit tests dependent on ManualResetEvent tends to be fragile when run in parallel.
But with the Task Parallel Library (TPL) the table has turned; TPL makes unit testing asynchronous code a lot easier. That is; it’s easy if you now how to do it.
Running some code asynchronously without any concerns for testability is pretty straight forward with TPL:
Task.Factory.StartNew(MyLongRunningJob);
And in fact; it’s not much harder to make it test-friendly. You only need a bit insight into what’s going in the Task Factory. And to have it straight from the horse’s mouth; here’s what MSDN says about it:
Behind the scenes, tasks are queued to the ThreadPool, which has been enhanced with algorithms (like hill-climbing) that determine and adjust to the number of threads that maximizes throughput. This makes tasks relatively lightweight, and you can create many of them to enable fine-grained parallelism. To complement this, widely-known work-stealing algorithms are employed to provide load-balancing.

The Task Factory will use a Task Scheduler to queue the tasks and the default scheduler is the ThreadPoolTaskScheduler, which will run the tasks on available threads in the thread pool.

The trick when unit testing TPL code is to not have those tasks running on threads that we have no control over, but to run them on the same thread as the unit test itself. The way we do that is to replace the default scheduler with a scheduler that runs the code synchronously. Enter the CurrentThreadTaskScheduler;

public class CurrentThreadTaskScheduler : TaskScheduler
{
    protected override void QueueTask(Task task)
    {
        TryExecuteTask(task);
    }

    protected override bool TryExecuteTaskInline(
       Task task, 
       bool taskWasPreviouslyQueued)
    {
        return TryExecuteTask(task);
    }

    protected override IEnumerable<Task> GetScheduledTasks()
    {
        return Enumerable.Empty<Task>();
    }

    public override int MaximumConcurrencyLevel { get { return 1; } }
}

TaskScheduler is an abstract class that all schedulers must inherit from and it only contains 3 methods that needs to be implemented;
  1. void QueueTask(Task)
  2. bool TryExecuteTaskInline(Task, bool)
  3. IEnumerable<Task> GetScheduledTasks()
In the more advanced schedulers like the ThreadPoolTaskScheduler, this is where the heavy-lifting of getting tasks to run on different threads in a thread-safe manner happens. But for running tasks synchronously, we really don’t need that. In fact, that’s exactly what we don’t need. So instead of scheduling tasks to run on different threads, the TryExecuteTaskInline method will just execute them immediately on the current thread.

Now it’s time to actually use it in the production code;

public TaskScheduler TaskScheduler
{
    get
    {
        return _taskScheduler
            ?? (_taskScheduler = TaskScheduler.Default);
    }
    set { _taskScheduler = value; }
}
private TaskScheduler _taskScheduler;

public Task AddAsync(int augend, int addend)
{
    return new TaskFactory(this.TaskScheduler)
        .StartNew(() => Add(augend, addend));
}

To be able to inject a different TaskScheduler from unit tests, I’ve made the dependency settable through a public property on the class I’ll be testing. If no TaskScheduler has been explicitly set (which it won’t be when executed ‘in the wild’), the default TaskScheduler will be used.

The method Task AddAsync(int, int) is the method we would like to unit test. As you can see it’s a highly CPU intensive computation that will add 2 numbers together. Just the kind of work you’d want to surround with all the ceremony and overhead of running asynchronously.

The important part here is the instantiation of the TaskFactory that will take the TaskScheduler as a constructor parameter.

With that in place we can set the TaskScheduler from the unit tests:

[Test]
public void It_should_add_numbers_async()
{
    var calc = new Calculator
    {
        TaskScheduler = new CurrentThreadTaskScheduler()
    };

    calc.AddAsync(1, 1);

    calc.GetLastSum().Should().Be(2);
}

The System Under Test, SUT, is the Calculator-class that has the AddAsync-method we’d like to unit test. Before calling the AddAsync-method we set the CurrentThreadTaskScheduler that the TaskFactory in the Calculator should use.

Since AddAsync doesn’t return the result of the calculation, I’ve added a method to get the last sum. Not exactly production-polished code, but it’ll do for the purpose of this example.

Anyway; the end result is that the test pass. And if I don’t assign the CurrentThreadTaskScheduler to Calculator.TaskScheduler – that is it runs with the default ThreadPoolTaskScheduler – it will fail, because the addition will not be finished before the assertion.

But don’t trust me on this. I’ve uploaded the complete (absurd) example to GitHub, so you can run the tests and see for yourself; https://github.com/bulldetektor/TplSpike.

References


You can read the MSDN-article that I quoted from here; http://msdn.microsoft.com/en-us/library/dd537609.aspx

I found the code for the CurrentThreadTaskScheduler in the TPL samples here; http://code.msdn.microsoft.com/windowsdesktop/Samples-for-Parallel-b4b76364. The samples contains a dozen or so TaskSchedulers, for instance;


  • QueuedTaskScheduler - provides control over priorities, fairness, and the underlying threads utilized
  • OrderedTaskScheduler - ensures only one task is executing at a time, and that tasks execute in the order that they were queued.
  • ReprioritizableTaskScheduler - supports reprioritizing previously queued tasks
  • RoundRobinTaskSchedulerQueue - participates in scheduling that support round-robin scheduling for fairness
  • IOCompletionPortTaskScheduler - uses an I/O completion port for concurrency control
  • IOTaskScheduler - targets the I/O ThreadPool
  • LimitedConcurrencyLevelTaskScheduler - ensures a maximum concurrency level while running on top of the ThreadPool
  • StaTaskScheduler - uses STA threads
  • ThreadPerTaskScheduler - dedicates a thread per task
  • WorkStealingTaskScheduler - a work-stealing scheduler, not much more to say about that

Tuesday, February 24, 2009

“Legacy Code is Code Without Tests”

I wish I’d come up with that phrase first, but it was Michal Feathers who stated this in his “Working effectively with legacy code”. It’s a great statement, and it pretty much sums up what testing is all about; if you’re not covered by tests, it is hard to refactor and change the code and at the same time know that you didn’t break anything. And if you have code that is resistant to change or that makes you nervous every time you touch it, then you have code that won’t be changed. You have legacy code. And you can try to wrap it, hide it, and forget it, but someday it will blow up. And someday you’ll have to go in there and make it work. But you won’t have any safety net. You’ll have to change something you do not know the reach of, and you’ll have to do it blindfolded and pray that your changes aren’t going to break something somewhere else. But I promise you; it will.

image And man I can tell you; it is good to be a consultant with skills in a technology where the software industry hasn’t had time to produce that much legacy code yet! That alone should be a good enough reason for you to invest some of your time into learning new skills. Skills that make you more valuable in the projects that produce new code, instead of maintaining legacy code that someone else hacked together years ago. It’s those green field projects that are fun!

And because there’s not that much WPF-based apps out there yet, there can still be time to save some poor souls from aiming at that same old pit of failure. The pit of strongly coupled, untestable, monolithic monsters. There’s hope and I believe in the goodness of coders. I believe that we want to make solid code. I believe that we want to produce code that is maintainable and changeable. And I believe that we, the residents of the software community, can make the leap into software craftsmanship. It’s just a matter of making those right choices. And I believe that loose couplings, testability and modularity are definitely the right choices in most cases. These are the key principles that will make you a better person (or at least a better developer).

Loose couplings and testability are tightly coupled (touché!). If you’re doing test-driven development, or behavior-driven development, or any other development practice that use tests to drive the design, you will end up with code that is loosely coupled. And if you’re building an app with loose couplings between modules and classes, you’ll end up with code that lends itself to testing very well. And testable, loosely coupled systems will be easier to maintain and change than a tightly coupled system with no tests to verify your code.

Modularity is another beast though. Modularity is about splitting the application in to pieces that multiple teams can work on in parallel - without getting in the way of each other. Modularity is about scalability and maintainability. Adding new functionality without ending up with a logarithmicimage time/functionality curve is an important factor in software development (maybe not for you and me, but for those white collars* that are deciding whether to fund or close down your project, predictability is extremely important). And modularity is about mastering complexity. How do you master too complex challenges? You break it down in to smaller, more manageable parts. And in software terms those parts are modules.

So if you take these 3 ingredients – loose couplings, testability, and modularity – and you shake it together with WPF (shake, not stir), you’ll have a fantastic opportunity to do WPF right. You’ll end up with code, not legacy code.

imageIf you’re in Stavanger on the 5th of March, Bergen on the 10th, Trondheim on the 12th or in Oslo on the 19th of March, you can hear me and my colleague Pål Fossmo give a talk on this topic at the MSDN Live event.

 

 

* Which btw just managed to bankrupt Iceland and is about to break the back of some of the strongest economies in the world… how the he** did they do that?!

Sunday, December 28, 2008

Effectively teaching test-first: Make it go green!

When you’re producing quality code using Test Driven Development you’re likely to use either the test first or test last approach (or more likely; a mix of those). Test first means that you write your test before you implement the method you’re testing (i.e. the functionality). If you’re following the test last mantra, you would implement the functionality first and then add test(s) to verify the behavior of the method. Strictly speaking, the latter approach will IMO not be a test driven development. If you want your tests to drive the architecture of your application, you should definitely go for the test first. It’s not that the test last won’t give you a testable application, it’s just that writing the test first makes it far easier to get your test actually be an unit test and not an integration test.

"I'm trying to free your mind, Neo. But I can only show you the door. You're the one that has to walk through it."

I must confess; I felt that writing tests first was really hard in the beginning. Before you start mastering the art of unit testing, you cannot really write the tests first unless you get some good guidance doing it. I’ve been doing TDD for a while now and I feel quite confident about it, and so I try to teach my team members the same philosophy. And the method I found that really helped getting them on the right track was to just sit down and do some pair-programming with them. We start out the session with me behind the keyboard. According to XP-ists the time between switching the roles of the driver (the one typing on the keyboard) and the observer (the one reviewing the code) should be around 30 minutes or so. But what I really find effective when it comes to teaching test first, is to do the switching after I’ve written the unit test. When writing the unit test itself, I do this while thinking out loud and discussing the code with my fellow dev. That way I practically show him/her how I think when I write the test and (s)he will have a good starting point for implementing the functionality. After I’ve finished writing the test, I just hand over the keyboard saying; “Make it go green!”

Would this method work in all circumstances? Certainly not! If (s)he was totally new to testing and I were to give an intro to unit testing, I’d probably choose another way. I’d probably show how to do some integration or unit testing on existing code before introducing the test first approach. Doing this exercise with someone who’s never seen a unit test before would probably be too much to grasp at once. When I’m coding the unit test in the Make-It-Go-Green-method I often have to use mocks to isolate the method under test and to introduce mocking to developers who have never seen unit test before will probably not be a good idea. In that case I’d rather let them do some integration tests before teaching them some real TDD. But maybe that’s just me…