Mutli Threading Tests

time to read 18 min | 3593 words

Anyone can suggest a pattern to do that? I'm currently trying to test some of my multi threaded code, and I get into a whole lot of trouble.

The problem is that I'm trying to test that things happen in two threads at the same time, and I'm not sure I'm doing it properly. Per Udi's suggestion I changed my code to use notifications, rather than busy waiting.

Because of the need to syncronized between the threads, I created the following method:

public void ExecuteInUIThread(Delegate d, params object[] parameters)
{
  if(this.InvokeRequired)
     Invoke(d,parameters);
  else
     d.DynamicInvoke(parameters); 
}

The problem is how to test that the method called from the class I'm testing is the right one, I'm not so sure about how to do this properly. But before I get to the solution, here is the problem:

I've a class which is called from the UI thread to do work, it spun off a thread which does it works, and when done, it call the ExecuteInUIThread method with a delegate to an OnSuccess or OnFailure method. My problem is with the tests, how do I make sure that I get the correct response from the class. Take into account that I'm mocking the class which contains the ExecuteInUIThread method, and that is a complex issue.

I solved it this way, using a custom constraint in the mock object, which allows me to run my own test method when the mock is called, here is what I've, it's an extention of the idea I'd with DelegatingConstraint:

public class DelegatingConstraintWithArgs : BaseConstraint

      {

            private readonly string message;

            private readonly int numberOfParams;

            private Delegate d;

            private int currentParam = 0;

            private object[] args;

            private bool delegateCalled = false;

 

            public DelegatingConstraintWithArgs(Delegate d, int numberOfParams, string message)

            {

                  this.message = message;

                  this.d = d;

                  this.numberOfParams = numberOfParams;

                  this.args = new object[numberOfParams];

            }

 

 

            public override bool Eval(object val)

            {

                  args[currentParam] = val;

                  currentParam += 1;

                  if (currentParam == numberOfParams)

                  {

                      bool ret= (bool) d.DynamicInvoke(args);

                      delegateCalled = true;

                      return ret;

                  }

                  return true;

            }

 

            public override string Message

            {

                  get { return message; }

            }

 

            public bool DelegateCalled

            {

                  get { return delegateCalled; }

            }

}    

This class will gather the paramters of the method, and when it will reach the final number, will execute the delegate on them. This allows me to invoke the delegate that I get from the class under test*. I can execute it and then observe the effects.

On a more general level, this allows you to execute your own code from the mocked object, without writing much code. It's nice, but very much open to abuse, and more over, I'm not so sure that I'm not abusing it myself right now.

For instance, here is my current test, I setup quite a bit of expectations that it needs, and then run the tests, waiting until after the method is called. Verify()ing the mock is done on the TearDown() method.

[Test]

public void ExecuteQuery_NoParameters()

{

      ExpectNoError();

      DelegatingConstraintWithArgs cs = new DelegatingConstraintWithArgs(new ExecuteInUIThreadDeleagate(ExecuteInUIThread),

            2,"-not-relevant-");

      mockIQueryView.SetupResult("HqlQueryText",nonParametrized);

      mockIQueryView.SetupResult("Parameters",new Hashtable());

      mockIQueryView.Expect("StartWait",null,null,null);

      mockIQueryView.Expect("DisplayObjectGraph",

            objectGraphConstraint);

      mockIQueryView.Expect("DisplayDataSet",

            new And(new IsTypeOf(typeof(DataSet)),dataSetConstraint));

      mockIQueryView.Expect("ShowObjectGraph");

      mockIQueryView.Expect("EndWait", new IsTypeOf(typeof(string)));

      mockIQueryView.Expect("ExecuteInUIThread",cs,cs);

      context.ExecuteQuery();

      while(!cs.DelegateCalled)

            Thread.Sleep(100);

}

* As a side note, I've here two or three layers of delegates, spooky.