Имитация асинхронных вызовов в прокси Silverlight WCF с использованием Moq - PullRequest
7 голосов
/ 01 октября 2010

У меня есть типичное приложение Silverlight со службой WCF, и я использую slsvcutil.exe для создания стандартного клиентского прокси-сервера для связи с веб-службой.Я пытаюсь написать модульные тесты и пытаюсь использовать инфраструктуру модульного тестирования Silverlight и Moq для макетирования прокси-сервера и удаления зависимости службы для тестирования.

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

Для того, чтобы сделать прокси-сервер "поддельным", я создал свой собственныйпростой интерфейс для сгенерированных прокси-вызовов и их завершенных событий:

public interface IServiceProxy
{
    void TestAsync(TestRequest request);
    void TestAsync(TestRequest request, object userState);
    event EventHandler<TestCompletedEventArgs> TestCompleted;
}

Я также подклассифицировал сгенерированный прокси-объект для реализации этого интерфейса:

public class MyServiceProxy : GeneratedServiceClient, IServiceProxy, ICommunicationObject
{
    // ... overloaded proxy constructors
}

После просмотра документации Moq, этоВот как я пытаюсь настроить макет, чтобы ожидать вызова TestAsync () и немедленно вызвать событие TestCompleted с результатом в EventArgs:

[TestMethod]
public void Test_Returns_Expected()
{
    var mockProxy = new Mock<IServiceProxy>();
    var result = new TestResponse() { Value = true };

    this.mockProxy.Setup(
        p => p.TestAsync(It.IsAny<TestRequest>()))
        .Raises(p => p.TestCompleted += null, new TestCompletedEventArgs(new object[] { result }, null, false, null));

    // rest of the test to actually use the mock and assert things
}

Все прекрасно, но когда я пытаюсь запустить любойвид проверки с использованием макета и установки точек останова, событие TestCompleted никогда невозникает, когда я вызываю TestAsync ().

Есть ли что-то очевидное, чего мне не хватает, или есть какие-нибудь идеи о том, чтобы высмеивать эти типы прокси-серверов асинхронных служб в Silverlight?

Спасибо!

РЕДАКТИРОВАТЬ:

Чтобы было более понятно, что я на самом деле пытаюсь протестировать, это вспомогательный класс, который я создал, который использует экземпляр IServiceProxy и предоставляет более чистый интерфейс сервиса для моей ViewModel, который можно использовать, принимая параметры обратного вызова Action<TResponse, Exception>вместо того, чтобы иметь дело с событиями обратного вызова в моей ViewModel.Я понимаю, как это можно смоделировать, чтобы напрямую протестировать мою ViewModel, но я решил, что было бы неплохо сначала протестировать класс помощника.

Вот пример того, о чем я говорю:

public class HelperClient : IServiceHelper
{
    private IServiceProxy proxy;

    public HelperClient(IServiceProxy proxy)
    {
        this.proxy = proxy;

        // register to handle all async callback events
        this.proxy.TestCompleted += new EventHandler<TestCompletedEventArgs>(TestCompleted);
    }

    public void Test(TestRequest request, Action<TestResponse, Exception> response)
    {
        this.proxy.TestAsync(request, response);
    }

    private void TestCompleted(object sender, TestCompletedEventArgs e)
    {
        var response = e.UserState as Action<TestResponse, Exception>;

        if (response != null)
        {
            var ex = GetServiceException(e);

            if (ex == null)
            {
                response(e.Result, null);
            }
            else
            {
                response(null, ex);
            }
        }
    }
}

Итак, в моем тесте я на самом деле проверяю ISerivceProxy и передаю его, а также просто пытаюсь проверить вызов службы, чтобы убедиться, что оболочка правильно вызывает Action:

[TestMethod]
[Asynchronous]
public void Test_Returns_Expected()
{
    var mockProxy = new Mock<IServiceProxy>();
    var helper = new HelperClient(mockProxy.Object);
    bool expectedResult = true;
    var result = new TestResponse() { Value = expectedResult };

    this.mockProxy.Setup(
        p => p.TestAsync(It.IsAny<TestRequest>()))
        .Raises(p => p.TestCompleted += null, new TestCompletedEventArgs(new object[] { result }, null, false, null));

    helper.Test(new TestRequest(), (response, ex) =>
    {
        Assert.AreEqual(expectedResult, response.Value);
        EnqueueTestComplete();
    });
}

Проблема заключается в том, что макет прокси-объекта никогда не вызывает событие TestCompleted, поэтому мое ответное действие никогда не вызывается для завершения теста (даже при том, что тест, кажется, завершается успешно, Assert фактически никогда не запускается).Извините за такой длинный пост, просто пытаюсь показать вам как можно больше кода.

РЕДАКТИРОВАТЬ 2

Добавлен [Asynchronous] и вызов EnqueueTestComplete(), который японял, что мне может понадобиться подождать, пока событие не будет вызвано.Это не очень помогло, событие до сих пор никогда не вызывается, поэтому тест просто зависает и никогда не завершается.

РЕДАКТИРОВАТЬ 3

Ответ Алиостада был верным, что подпись моего ожидания установки была правильнойне соответствует действительной сигнатуре Test (), что позволяет мне передать ответное действие в качестве второго параметра.Глупая ошибка, но именно это мешало Moq вызвать событие Completed.Я также забыл передать Action как объект userState в TestCompletedEventArgs, чтобы он действительно вызывался при возникновении события Completed.Кроме того, [Asynchronous] и EnqueueTestCompleted в этом случае, по-видимому, не нужны.

Вот обновленный тестовый код для всех, кто интересуется:

[TestMethod]
public void Test_Returns_Expected()
{
    var mockProxy = new Mock<IServiceProxy>();
    var helper = new HelperClient(mockProxy.Object);
    bool expectedResult = true;
    var result = new TestResponse() { Value = expectedResult };

    Action<TestResponse, Exception> responseAction = (response, ex) =>
    {
        Assert.AreEqual(expectedResult, response.Value);
    };

    this.mockProxy.Setup(
        p => p.TestAsync(It.IsAny<TestRequest>(), It.IsAny<Action<TestResponse, Exception>>()))
        .Raises(p => p.TestCompleted += null, new TestCompletedEventArgs(new object[] { result }, null, false, responseAction));

    helper.Test(new TestRequest(), responseAction);
}

Ответы [ 2 ]

4 голосов
/ 01 октября 2010

Насмешливые события довольно трудны, и юнит-тесты становятся хрупкими.Но, как вы сказали, обойти это невозможно.Но обычно вы выполняете вызов, который пытаетесь протестировать, и блокируете текущий поток (используя Sleep или другие методы) до тех пор, пока не произойдет событие (или тайм-аут).

На самом деле не ясно, кто вытестирование.Я могу увидеть макет и ответ, где находится реальный объект?

Я обновлю свой ответ соответственно.

ОБНОВЛЕНИЕ

Я могуувидеть проблему здесь:

helper.Test(new TestRequest(), (response, ex) =>
{
    Assert.AreEqual(expectedResult, response.Value);
    EnqueueTestComplete();
});

в последнем утверждении вы ставите EnqueueTestComplete ();и вы утверждаете, что это действие никогда не будет использовано, потому что оно передается объекту moq.

Также вы устанавливаете ожидание для TestAsync(It.IsAny<TestRequest>())) (один аргумент), когда вы вызываете его с двумя аргументами в HelperClient (this.proxy.TestAsync(request, response);) и поэтому его никогда не увольняют, поскольку ожидание не оправдывается.

0 голосов
/ 18 апреля 2012

только что искал фиктивный асинхронный клиент WCF и нашел этот вопрос.

, чтобы предотвратить эту ситуацию, Moq может .Verify() вызвать p.TestAsync ().

//will throw MockException if p.TestAsync() has never been called.
this.mockProxy.Verify(p => p.TestAsync(It.IsAny<TestRequest>()), Times.Once());
...