views:

44

answers:

2

In my unit test how can I verify that an event is raised by the mocked object.

I have a View(UI) --> ViewModel --> DataProvider --> ServiceProxy. ServiceProxy makes async call to serivce operation. When async operation is complete a method on DataProvider is called (callback method is passed as a method parameter). The callback method then raise and event which ViewModel is listening to.

For ViewModel test I mock DataProvider and verify that handler exists for event raised by DataProvider. When testing DataProvider I mock ServiceProxy, but how can I test that callback method is called and event is raised.

I am using RhinoMock 3.5 and AAA syntax

Thanks

-- DataProvider --

public partial class DataProvider
{
    public event EventHandler<EntityEventArgs<ProductDefinition>> GetProductDefinitionCompleted;

    public void GetProductDefinition()
    {
        var service = IoC.Resolve<IServiceProxy>();
        service.GetProductDefinitionAsync(GetProductDefinitionAsyncCallback);
    }

    private void GetProductDefinitionAsyncCallback(ProductDefinition productDefinition, ServiceError error)
    {
        OnGetProductDefinitionCompleted(this, new EntityEventArgs<ProductDefinition>(productDefinition, error));
    }

    protected void OnGetProductDefinitionCompleted(object sender, EntityEventArgs<ProductDefinition> e)
    {
        if (GetProductDefinitionCompleted != null)
            GetProductDefinitionCompleted(sender, e);
    }
}

-- ServiceProxy --

public class ServiceProxy : ClientBase<IService>, IServiceProxy
{
    public void GetProductDefinitionAsync(Action<ProductDefinition, ServiceError> callback)
    {
        Channel.BeginGetProductDefinition(EndGetProductDefinition, callback);
    }

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

        ServiceError error;
        ProductDefinition results = Channel.EndGetProductDefinition(out error, result);

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

The following example sets up an IService stub which will simply invoke any callback that is passed to it when IService.AsyncOperationWithCallBack(Action callback) is called.

// arrange
var serviceStub = MockRepository.GenerateStub<IService>();
serviceStub.Stub(x => x.AsyncOperationWithCallBack(Arg<Action>.Is.NotNull))
    .WhenCalled(
        invokation =>
        {
            var callback = (Action)invokation.Arguments[0];
            callback();
        });

var dataProvider = new DataProvider(serviceStub);  

// act
bool raised = false;
dataProvider.MyEvent += delegate { raised = true; };
dataProvider.DoSomething();

// assert
serviceStub.AssertWasCalled(
    x=>x.AsyncOperationWithCallBack(Arg<Action>.Is.NotNull));
Assert.IsTrue(raised);
Wim Coenen
A: 

It sounds like you have two different unit tests to write:

  1. Service Proxy unit test: This test will make sure that the callback sent in to the ServiceProxy will be called upon completion of the async call.

  2. Data Provider unit test: This test will make sure that when a certain method is called, an event is raised (assuming there were some subscribers).

Which one are you looking for help on?

EDIT:

For item #1, I don't see that you'd need any mocking. Simply provide a callback that sets some variable to true when called:

// arrange
IServiceProxy serviceProxy = new ServiceProxy();
bool callbackMade;

// act
serviceProxy.GetDataAsync(() => callbackMade = true);

// assert
Assert.IsTrue(callbackMade);

For item #2, again, just subscribe to the event in your unit test and make sure the event is called:

// arrange
DataProvider dp = new DataProvider();
bool eventRaised;
dp.DataReturned += (s,e) => eventRaised = true;

// act
dp.DoSomethingThatShouldRaiseEvent();

// assert
Assert.IsTrue(eventRaised)

I don't know the signatures of your events/callbacks so I just made some guesses.

Patrick Steele
help on both would be great. thanks
joblot
sorry for delay. for 2nd test, issue is DoSomethingThatShouldRiaseEvent is not a public method on DataProvider. It is the callback method which is passed to the ServiceProxy. thanks
joblot
It would probably help if you provided the methods on the DataProvider and how they interact with the ServiceProxy class. I think separating this into two distinct unit tests is a good start. Then you can get fancier and couple them into an integration test (although the unit tests alone will help ensure the individual components are behaving as designed/expected).
Patrick Steele
hi thanks for your reply. I have included the implementation of data provider and service proxy in the original post. DoSomethingThatShouldRiaseEvent (GetProductDefinitionAsyncCallback) is not a public method. Thanks
joblot
I cannot create an instance of ServiceProxy (its a WCF service) as it complains about missing config file. Event if I add a config, instantiation will fail as there is no active service at the time of testing!
joblot
First: Your DataProvider constructor should be accepting an IServiceProxy instance. You shouldn't be calling your container's resolve event manually. For the DataProvider, I would make GetProductDefinitionAsyncCallback internal (instead of private) and make it visible to your unit testing assembly with the InternalsVisibleTo attribute. This will allow you to call the method and then make sure the event was fired.
Patrick Steele
For the ServiceProxy, consider abstracting the Channel class into an interface (IChannel). It would allow you to inject a mock on the ServiceProxy and would remove your dependency on an external resource (i.e. an actual WCF channel).
Patrick Steele