views:

159

answers:

1

Hi All

I have a service proxy class that makes asyn call to service operation. I use a callback method to pass results back to my view model.

Doing functional testing of view model, I can mock service proxy to ensure methods are called on the proxy, but how can I ensure that callback method is called as well?

With RhinoMocks I can test that events are handled and event raise events on the mocked object, but how can I test callbacks?

ViewModel:

public class MyViewModel
{
    public void GetDataAsync()
    {
        // Use DI framework to get the object
        IMyServiceClient myServiceClient = IoC.Resolve<IMyServiceClient>();
        myServiceClient.GetData(GetDataAsyncCallback);
    }

    private void GetDataAsyncCallback(Entity entity, ServiceError error)
    {
        // do something here...
    }

}

ServiceProxy:

public class MyService : ClientBase<IMyService>, IMyServiceClient
{
    // Constructor
    public NertiAdminServiceClient(string endpointConfigurationName, string remoteAddress)
        :
            base(endpointConfigurationName, remoteAddress)
    {
    }

    // IMyServiceClient member.
    public void GetData(Action<Entity, ServiceError> callback)
    {
        Channel.BeginGetData(EndGetData, callback);
    }

    private void EndGetData(IAsyncResult result)
    {
        Action<Entity, ServiceError> callback =
            result.AsyncState as Action<Entity, ServiceError>;

        ServiceError error;
        Entity results = Channel.EndGetData(out error, result);

        if (callback != null)
            callback(results, error);
    }
}

Thanks

A: 

Played around with this a bit and I think I may have what you're looking for. First, I'll display the MSTest code I did to verify this:

[TestClass]
public class UnitTest3
{
    private delegate void MakeCallbackDelegate(Action<Entity, ServiceError> callback);

    [TestMethod]
    public void CallbackIntoViewModel()
    {
        var service = MockRepository.GenerateStub<IMyServiceClient>();
        var model = new MyViewModel(service);

        service.Stub(s => s.GetData(null)).Do(
            new MakeCallbackDelegate(c => model.GetDataCallback(new Entity(), new ServiceError())));
        model.GetDataAsync(null);
    }
}

public class MyViewModel
{
    private readonly IMyServiceClient client;

    public MyViewModel(IMyServiceClient client)
    {
        this.client = client;
    }

    public virtual void GetDataAsync(Action<Entity, ServiceError> callback)
    {
        this.client.GetData(callback);
    }

    internal void GetDataCallback(Entity entity, ServiceError serviceError)
    {

    }
}

public interface IMyServiceClient
{
    void GetData(Action<Entity, ServiceError> callback);
}

public class Entity
{
}

public class ServiceError
{
}

You'll notice a few things:

  1. I made your callback internal. You'll need to use the InternalsVisisbleTo() attribute so your ViewModel assembly exposes internals to your unit tests (I'm not crazy about this, but it happens in rare cases like this).

  2. I use Rhino.Mocks "Do" to execute the callback whenever the GetData is called. It's not using the callback supplied, but this is really more of an integration test. I assume you've got a ViewModel unit test to make sure that the real callback passed in to GetData is executed at the appropriate time.

  3. Obviously, you'll want to create mock/stub Entity and ServiceError objects instead of just new'ing up like I did.

Patrick Steele
could please tell how can i make internal method visible to unit test project. thanks
joblot
i have worked out how to make internal method visible. could you please explain what does Do actual do and why we created a Stud for the service. thanks
joblot
This is just a unit test for your ViewModel. Therefore, all outside dependencies are mocked/stubbed. The "Do" method tells Rhino.Mocks to execute a specific piece of code whenever a method is called. I used "Do" so that your ViewModel would be able to call "GetDataAsync" and would also get a callback -- all during testing.Testing whether the service executes callbacks would be done as part of a unit test of the Service class.
Patrick Steele
hi. i put a break point in the callback method and it never gets stepped into. i am using version 3.5 and new AAA syntax i dont get Do method on the mocked service. how is stud different to mock? thanks
joblot
In Rhino.Mocks, stubs and mocks are pretty similar, but the big difference is that stubs are just used to provide a "stub" of a dependency. You usually don't set expectations on stubs. You just create them to return canned responses. Mocks are used to set and verify expectations.
Patrick Steele