Метод обратного вызова RhinoMocks Testing

У меня есть класс прокси службы, который выполняет асинхронный вызов операции службы. Я использую метод обратного вызова, чтобы передать результаты обратно в мою модель представления.

Выполняя функциональное тестирование модели представления, я могу имитировать прокси-сервер службы, чтобы гарантировать, что методы вызываются на прокси-сервере, но как я могу гарантировать, что также вызывается метод обратного вызова?

С помощью RhinoMocks я могу проверить, обрабатываются ли события и генерируются события на имитируемом объекте, но как я могу проверить обратные вызовы?

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);
    }
}

Спасибо


person joblot    schedule 28.05.2010    source источник


Ответы (1)


Поигрался с этим немного, и я думаю, что у меня есть то, что вы ищете. Сначала я покажу код MSTest, который я сделал, чтобы убедиться в этом:

[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
{
}

Вы заметите несколько вещей:

  1. Я сделал ваш обратный звонок внутренним. Вам нужно будет использовать атрибут InternalsVisisbleTo (), чтобы ваша сборка ViewModel открывала внутренние компоненты вашим модульным тестам (я не без ума от этого, но это случается в таких редких случаях, как этот).

  2. Я использую Rhino.Mocks «Do» для выполнения обратного вызова всякий раз, когда вызывается GetData. Он не использует предоставленный обратный вызов, но на самом деле это скорее интеграционный тест. Я предполагаю, что у вас есть модульный тест ViewModel, чтобы убедиться, что реальный обратный вызов, переданный в GetData, выполняется в нужное время.

  3. Очевидно, вы захотите создать объекты-макеты / заглушки Entity и ServiceError вместо того, чтобы просто создавать новые, как это сделал я.

person PatrickSteele    schedule 28.05.2010
comment
Могли бы рассказать, как я могу сделать внутренний метод видимым для проекта модульного тестирования. Благодарность - person joblot; 01.06.2010
comment
Я разработал, как сделать видимым внутренний метод. Не могли бы вы объяснить, чем занимается Do actual и почему мы создали Stud для этой службы. Благодарность - person joblot; 01.06.2010
comment
Это просто модульный тест для вашей ViewModel. Следовательно, все внешние зависимости имитируются / заглушаются. Метод Do сообщает Rhino.Mocks выполнять определенный фрагмент кода при каждом вызове метода. Я использовал Do so, чтобы ваша ViewModel могла вызывать GetDataAsync, а также получать обратный вызов - все во время тестирования. Проверка того, выполняет ли служба обратные вызовы, будет выполняться как часть модульного теста класса Service. - person PatrickSteele; 01.06.2010
comment
Привет. Я поставил точку останова в методе обратного вызова, и он никогда не попадает в него. Я использую версию 3.5 и новый синтаксис AAA. Я не использую метод Do на имитируемой службе. Чем же шпилька отличается от издевательства? Благодарность - person joblot; 02.06.2010
comment
В Rhino.Mocks заглушки и имитаторы очень похожи, но большая разница в том, что заглушки используются только для создания заглушки зависимости. Обычно вы не возлагаете никаких надежд на заглушки. Вы просто создаете их, чтобы возвращать стандартные ответы. Моки используются для установления и проверки ожиданий. - person PatrickSteele; 03.06.2010