views:

579

answers:

3

I'm trying to unit test, using VS unit testing facility following method.

void Get(string name, Action<string> callBack);

here is unit tester

    [TestMethod]
    public void Test()
    {
        Action<string> cb = name =>
        {
            Assert.IsNotNull(name);
        };

        var d = new MyClass();
        d.Get("test", cb);
    }

The only problem is that internal implementation uses BackgroundWorker, hence callback is invoked on another thread. Here is internal implementation.

    public void Get(string name, Action<string> callBack)
    {
        callBackString = callBack;
        GetData(name);
    }  

    private void GetData(string name)
    {
        BackgroundWorker bw = new BackgroundWorker();
        bw.DoWork += bw_DoWork;
        bw.RunWorkerCompleted += bw_RunWorkerCompleted;
        bw.RunWorkerAsync(name);
    }

    private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        //do work here
        if (null != callBackString)
            callBackString("ok");
    }

Of course because Get() returns right away, test completes with success and testing stops, thus RunWorkerCompleted never gets to be executed. I can easily test this via normal application (WPF) because it stays running, yet I'd like to have ability to unit test this.

Any ideas? Thanks in advance.

+1  A: 

Something like:

[TestMethod]
public void Test()
{   
    bool running = true;
    string result = null;

    Action<string> cb = name =>
    {
        result = name;
        running = false;
    };

    var d = new MyClass();
    d.Get("test", cb);

    while(running)
    {
        Thread.Sleep(100);
    }

    Assert.IsNotNull(result);
}

Though you probably want to add something in there to stop the test from running forever if it fails...

Martin Harris
This works. Thanks.
Sherlock
+2  A: 

I don't know the details for C# & BackgroundWorker but I'd do this by injecting the BackgroundWorker implementation and during the test use a stubbed BackgroundWorker that you can control when it executions and in which thread. If communication between two threads is required then have the stubbed BackgroundWorker create a new thread and have the test pause until it is complete, if not force it to run after the call to get.

mlk
A: 

I've written similar code and made it testable by following the IAsyncResult design pattern. My unit tests run against the synchronous (blocking) version of the method. The asynchronous methods can be a single line each, so I didn't write unit tests against those.

TrueWill
well, I could implement another version of Get() in a synchronous(blocking) way, and then let client deal with IAsyncResult design pattern, but this is exactly what I wanted to avoid in the first place. Client doesn't need to know about implementation details. It simply provides a callback which will be invoked at some later time.
Sherlock