views:

42

answers:

2

I have a unit test in which I mock (with moq) an object and let it verify if it executed a method properly. This method is being executed in a Thread that I create in my SUT (System under Test). When I want to do VerifyAll() on the Mock it could happen that the Thread is still running and that it isn't finished yet executing the method - failing the test.

Is there a way to resolve this in a correct matter? for example let the VerifyAll wait or something? Because now, the test is unreliable.

This is the Test:

    [Test]
    public void TryToExecute_SubjectNotYetBeingProcessed_ProcessesSubject()
    {
        var subject = new Subject();
        var rule = new Mock<IBusinessRule>();
        rule.Setup(x => x.RunChildren(subject)); //RunChildren will be called in a seperate Thread

        InBuffer.TryToExecute(subject, rule.Object);

        rule.VerifyAll(); //It could be possible that the Thread is still running and that RunChildren() isn't invoked yet, thus failing the test.
    }

    public void TryToExecute(Subject subject, IBusinessRule rule){
        var thread = new Thread(x =>
                {
                    SetCurrentAsProcessing(subject);
                    rule.RunChildren(subject) // This is where it executes
                    RemoveFromProcess(subject);
                });

        thread.Start(); // Start the Thread
    }
+1  A: 

If your class implements some mechanism to wait for the asynchronous operation to finish then you should use this one in your test.

If not you can try this "hack" (untested - I don't know your mocking framework and I am not sure how to make it run methodCalled.Set()):

[Test]
public void TryToExecute_SubjectNotYetBeingProcessed_ProcessesSubject()
{
    ManualResetEvent methodCalled = new ManualResetEvent(false);

    var subject = new Subject();
    var rule = new Mock<IBusinessRule>();
    rule.Setup(x => x.RunChildren(subject)).Do(X=>methodCalled.Set()); //RunChildren will be called in a seperate Thread

    InBuffer.TryToExecute(subject, rule.Object);

    Assert.IsTrue(methodCalled.WaitOne(1000), "RunChildren was not called within 1000ms");
}

Please note that this kind of approach is not nice nor safe, so avoid if you have an alternative.

Grzenio
A: 

Use a monitor in the test:

Monitor.Enter(lock)
Monitor.Wait(lock, timeout) // Will pause here until pulsed or timed out
Monitor.Exit(lock)

In your mock use a callback to notify the monitor (NB: my version of MoQ uses Callback rather than Do):

rule.Setup(x => x.RunChildren(subject)).Do( X => {
    ...
    Monitor.Enter(lock);
    Monitor.Pulse(lock);
    Monitor.Exit(lock);
}

There are some examples of using monitors here (part of an automation testing framework):

http://code.google.com/p/wipflash/source/browse/WiPFlash/Components/AutomationElementWrapper.cs

Lunivore
This seems to do the trick :] good mention! Guessing there isn't really anything else that solves this problem in a proper manner
Bas
Should have mentioned - make sure you Enter the monitor before you call the threaded code or it might complete first and you'll get timeouts.
Lunivore