Showing posts with label Nunit. Show all posts
Showing posts with label Nunit. Show all posts

Saturday, June 7, 2008

Testing Concurrency with Nunit



Nunit is really nice for unit testing. One thing I wanted to do in my unit test is simulate multiple users using the system. Normally it would be nice to have a set of tests for single user scenario. Once those tests pass, it is nice to be able to run them in a "multi-user" environment. I didn't really want to re-code my tests to simulate a multi-user environment, and I didn't really want to create complex threading system to do it. So to solve the problem I used the .NET built-in thread pool, and I leverage the existing tests I have. Here is an example
Suppose we have 2 simple unit tests, one to test a customer request, and the other to test updating a customer record. The implementation is not needed for this sample.

      
      [TestFixture]
      public class TestCustmoerUserCase
      {
         [Test]
         public void TestProcessCustmerRequest()
         {
            // processing to handle a custmer request
         }
         [Test]
         public void TestUpdateCustomer()
         {
            // processing to update a custmer
         }
      }

Suppose I would like to test these two scenarios in a multi-user environment. Here is one possible solution:

      
      [TestFixture]
      public class TestCustmerUserCaseAsync
      {
         private delegate void AsyncDelegate();
         [Test]
         public void TestAsync()
         {
            TestCustmoerUserCase custTest = new TestCustmoerUserCase();
            // you can also run initalization code here, before starting the threads...
            AsyncDelegate asyncOperation = delegate
            {
               custTest.TestProcessCustmerRequest();
               custTest.TestUpdateCustomer();
            };
            Console.WriteLine("Running thread 1");
            IAsyncResult r1 = asyncOperation.BeginInvoke(null, null);
            Console.WriteLine("Running thread 2");
            IAsyncResult r2 = asyncOperation.BeginInvoke(null, null);
            Console.WriteLine("Running thread 3");
            IAsyncResult r3 = asyncOperation.BeginInvoke(null, null);
            Console.WriteLine("Waiting for threads to finish");
            asyncOperation.EndInvoke(r1);
            Console.WriteLine("Finished thread 1");
            asyncOperation.EndInvoke(r2);
            Console.WriteLine("Finished thread 2");
            asyncOperation.EndInvoke(r3);
            Console.WriteLine("Finished thread 3");
         }
      }
   }

Lets make a few notes about the example above

  • I have created a delegate using .NET C# 2.0 anonymous methods. This allows me to place a method within a method and give it a delegate name. In this case my delegate is called asyncOperation.
  • Within the body of asyncOperation are the unit tests calls (which we normally call from a single thread). Note that declaring the delegate does not execute it.
  • Right after declaring my delegate I ask .NET to invoke it 3 times on the thread pool. I do this with the BeginInvoke method, which each .NET delegate support.
  • I pass null for callback, and null for state. That's because in this case I don't need them
  • Each time a BeginInvoke is called, I get a "token" for the threaded execution called IAsyncResult. Its important to keep all 3 references I get from BeginInvoke. (in this example they are saved as r1, r2 and r3
  • After launching the 3 threads. I need to wait for them to finish. Calling EndInvoke waits for the execution to finish, but which one? this is where the IAsyncResult plays an important role.
  • EndInvoke(r1) will wait for the first thread to finish, EndInvoke(r2) waits for the second thread ... as so on.
  • I could of used a loop and an array to store the IAsyncResults, but that would of made the example more complex. For production use, you should use a loop and array for running the threads.

So there you have it. You ran a set of unit tests that you already have without too much work. But, remember multi-threading requires a few things:

  • Your unit tests need to be thread safe! its best that they don't share memory, or the small amount of memory they do share is protected between threads.
  • For more on Nunit go see http://www.nunit.org/
Happy .Netting everyone.