2010-10-01 3 views
7

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

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

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

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().

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

Спасибо!

EDIT:

Чтобы быть более ясно, что я на самом деле пытаюсь проверить вспомогательный класс, я сделал, который принимает экземпляр 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 и передавая ее и просто пытаюсь проверить вызов службы, чтобы убедиться, обертка она вызов действия правильно:

[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 поэтому мое действие ответа никогда не получение вызова, чтобы закончить тест (даже если испытание кажется завершить успешнее Утверждение ne ver фактически выполняется). Извините за такой длинный пост, просто пытаясь показать вам как можно больше кода.

EDIT 2

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

EDIT 3

ответ Aliostad был правилен, что подпись моей установки ожидания не совпадает с фактической Test() подписи позволяет мне передать ответные действия в качестве второго паров. Глупая ошибка, но это то, что мешало Моку поднимать событие 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); 
} 
+1

Привет, Dan, я обновил свой ответ. Ваша проблема заключалась в определении ожиданий метода с другой подписью от той, которую вы вызывали в своем вспомогательном классе. – Aliostad

ответ

4

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

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

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

UPDATE

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

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

Спасибо за помощь, я закончил игнорировать несоответствие подписи, которая была всей проблемой. Я отредактировал вопрос выше, чтобы предоставить еще один код того, как я, наконец, получил его работу. –

+0

Нет проблем. Счастлив разобрался. – Aliostad

0

только что искал асинхронный клиент 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());