Click here to Skip to main content
Click here to Skip to main content

Tagged as

TPL in a Unit Test? GENIUS!

, 14 May 2014
Rate this:
Please Sign up or sign in to vote.
How often have you created a unit test that you knew was long running (test all ___ overnight), only to find that eventually you hit a time when it never stopped, and horked up the rest of the test run? It’s happened to the best of us, I’m sure. However, thanks to .Net’s beautiful TPL, […]Code

How often have you created a unit test that you knew was long running (test all ___ overnight), only to find that eventually you hit a time when it never stopped, and horked up the rest of the test run?

It’s happened to the best of us, I’m sure.

However, thanks to .Net’s beautiful TPL, this becomes insanely easy. Let’s check it out.

The first thing we need to realize is what we want to do. What that is, in this case, is the ability to cancel a test mid-run after we know it’s gone too long. It’s somewhat along the lines of deadlock prevention, but not quite as academic Wink | ;)

So how does TPL enable task cancellation? CancellationTokenSource. You start a task, pass it a token, then you can use that token to cancel the task mid-run. With that in mind, let’s just get down to brass tax.

<span id="lnum1" style="color: #606060">   1:</span> [TestMethod]

<!--CRLF-->

<span id="lnum2" style="color: #606060">   2:</span> [ExpectedException(<span style="color: #0000ff">typeof</span>(OperationCanceledException))]

<!--CRLF-->

<span id="lnum3" style="color: #606060">   3:</span> <span style="color: #0000ff">public</span> <span style="color: #0000ff">void</span> TestMethod1()

<!--CRLF-->

<span id="lnum4" style="color: #606060">   4:</span> {

<!--CRLF-->

<span id="lnum5" style="color: #606060">   5:</span>     var cts = <span style="color: #0000ff">new</span> CancellationTokenSource();

<!--CRLF-->

<span id="lnum6" style="color: #606060">   6:</span>     cts.Token.Register(() =>

<!--CRLF-->

<span id="lnum7" style="color: #606060">   7:</span>     {

<!--CRLF-->

<span id="lnum8" style="color: #606060">   8:</span>         Console.WriteLine(<span style="color: #006080">"stuff to print to console if the test runs too long"</span>);

<!--CRLF-->

<span id="lnum9" style="color: #606060">   9:</span>     });

<!--CRLF-->

<span id="lnum10" style="color: #606060">  10:</span>     Task.Factory.StartNew(() =>

<!--CRLF-->

<span id="lnum11" style="color: #606060">  11:</span>     {

<!--CRLF-->

<span id="lnum12" style="color: #606060">  12:</span>         cts.CancelAfter(4000);

<!--CRLF-->

<span id="lnum13" style="color: #606060">  13:</span>         Thread.Sleep(5000);

<!--CRLF-->

<span id="lnum14" style="color: #606060">  14:</span>     })

<!--CRLF-->

<span id="lnum15" style="color: #606060">  15:</span>     .ContinueWith(t =>

<!--CRLF-->

<span id="lnum16" style="color: #606060">  16:</span>     {

<!--CRLF-->

<span id="lnum17" style="color: #606060">  17:</span>         <span style="color: #0000ff">if</span> (t.Exception != <span style="color: #0000ff">null</span>)

<!--CRLF-->

<span id="lnum18" style="color: #606060">  18:</span>         {

<!--CRLF-->

<span id="lnum19" style="color: #606060">  19:</span>             <span style="color: #0000ff">throw</span> t.Exception;

<!--CRLF-->

<span id="lnum20" style="color: #606060">  20:</span>         }

<!--CRLF-->

<span id="lnum21" style="color: #606060">  21:</span>  

<!--CRLF-->

<span id="lnum22" style="color: #606060">  22:</span>         Console.WriteLine(<span style="color: #006080">"Test completed w/in the alotted time"</span>);

<!--CRLF-->

<span id="lnum23" style="color: #606060">  23:</span>     })

<!--CRLF-->

<span id="lnum24" style="color: #606060">  24:</span>     .Wait(cts.Token);

<!--CRLF-->

<span id="lnum25" style="color: #606060">  25:</span> }

<!--CRLF-->

Let’s review the code.

First we create the CTS object.
Next, register some work to be done if/when the task ends up getting cancelled.
After that, fire off the actual work w/in a Task object.
Tell the CTS how long to wait before cancelling.
Do the long-running work.
After the task is done, continue by analyzing and re-throwing any exception aggregated by the task, or doing any additional work if you so choose.
Finally, wait on all this work based on the token. Either the task completes fully, or the token’s cancellation is tripped.

When it’s all said and done, the above UT passes (because it encounters an exception) as well as the following UT, because it finishes w/in the alotted time:

<span id="lnum1" style="color: #606060">   1:</span> [TestMethod]

<!--CRLF-->

<span id="lnum2" style="color: #606060">   2:</span> <span style="color: #0000ff">public</span> <span style="color: #0000ff">void</span> TestMethod2()

<!--CRLF-->

<span id="lnum3" style="color: #606060">   3:</span> {

<!--CRLF-->

<span id="lnum4" style="color: #606060">   4:</span>     var cts = <span style="color: #0000ff">new</span> CancellationTokenSource();

<!--CRLF-->

<span id="lnum5" style="color: #606060">   5:</span>     cts.Token.Register(() =>

<!--CRLF-->

<span id="lnum6" style="color: #606060">   6:</span>     {

<!--CRLF-->

<span id="lnum7" style="color: #606060">   7:</span>         Console.WriteLine(<span style="color: #006080">"stuff to print to console if the test runs too long"</span>);

<!--CRLF-->

<span id="lnum8" style="color: #606060">   8:</span>     });

<!--CRLF-->

<span id="lnum9" style="color: #606060">   9:</span>     Task.Factory.StartNew(() =>

<!--CRLF-->

<span id="lnum10" style="color: #606060">  10:</span>     {

<!--CRLF-->

<span id="lnum11" style="color: #606060">  11:</span>         cts.CancelAfter(6000);

<!--CRLF-->

<span id="lnum12" style="color: #606060">  12:</span>         Thread.Sleep(5000);

<!--CRLF-->

<span id="lnum13" style="color: #606060">  13:</span>     })

<!--CRLF-->

<span id="lnum14" style="color: #606060">  14:</span>     .ContinueWith(t =>

<!--CRLF-->

<span id="lnum15" style="color: #606060">  15:</span>     {

<!--CRLF-->

<span id="lnum16" style="color: #606060">  16:</span>         <span style="color: #0000ff">if</span> (t.Exception != <span style="color: #0000ff">null</span>)

<!--CRLF-->

<span id="lnum17" style="color: #606060">  17:</span>         {

<!--CRLF-->

<span id="lnum18" style="color: #606060">  18:</span>             <span style="color: #0000ff">throw</span> t.Exception;

<!--CRLF-->

<span id="lnum19" style="color: #606060">  19:</span>         }

<!--CRLF-->

<span id="lnum20" style="color: #606060">  20:</span>  

<!--CRLF-->

<span id="lnum21" style="color: #606060">  21:</span>         Console.WriteLine(<span style="color: #006080">"Test completed w/in the alotted time"</span>);

<!--CRLF-->

<span id="lnum22" style="color: #606060">  22:</span>     })

<!--CRLF-->

<span id="lnum23" style="color: #606060">  23:</span>     .Wait(cts.Token);

<!--CRLF-->

<span id="lnum24" style="color: #606060">  24:</span> }

<!--CRLF-->

Here’s the Test Run output for TestMethod1:

Test Name:    TestMethod1
Test Outcome:    Passed
Result StandardOutput:    stuff to print to console if the test runs too long

And for TestMethod2:

Test Name:    TestMethod2
Test Outcome:    Passed
Result StandardOutput:    Test completed w/in the alotted time

Now, I’m sure there are many different ways to solve this problem w/ TPL and Cancellation Tokens, and I’m certainly all ears. This one certainly looks like it’s going to work well for me in my situations. Let yours rip in the comments!

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

BC3Tech
Software Developer (Senior)
United States United States
No Biography provided
Follow on   Twitter   Google+

Comments and Discussions

 
-- There are no messages in this forum --
| Advertise | Privacy | Mobile
Web01 | 2.8.140827.1 | Last Updated 15 May 2014
Article Copyright 2014 by BC3Tech
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid