Определить реализацию метода в фиктивном объекте, используя Moq - PullRequest
6 голосов
/ 26 июля 2011

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

request.BeginGetResponse(new AsyncCallback(LoginCallback), requestState);

Итак, чтобы иметь возможность проверить это без реального запроса, я создал интерфейс, который я могу смоделировать.

public interface IRequestSender
    {
        void Send(HttpWebRequest request, AsyncCallback internalCallback, object requestState);
    }

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

public class RequestSenderMock : IRequestSender
    {
        public void Send(HttpWebRequest request, AsyncCallback internalCallback, object requestState)
        {
            var result = new Mock<IAsyncResult>();
            result.Setup(x => x.AsyncState).Returns(requestState);
            internalCallback(result.Object);
        }
    }

Теперь я могу легко создать фиктивный объект в моем модульном тесте и использовать его. Но когда я создаю

var sender = new Mock<RequestSenderMock>();

Я не могу проверить количество вызовов для этого объекта.

sender.Verify(x => x.Send(It.IsAny<HttpWebRequest>(), It.IsAny<AsyncCallback>(), It.IsAny<object>()), Times.Once());

Там написано, что мой метод должен быть виртуальным. Есть ли способ сделать это, не делая мой метод виртуальным? Было бы лучше, если бы я мог как-то указать имплементацию метода при использовании интерфейса.

var sender = new Mock<IRequestSender>();

А это как-то с помощью метода Setup или какого-то другого, чтобы сделать реализацию на этом фиктивном объекте. Чем я просто уберу свой ложный урок. Это возможно? Что вы предлагаете?

1 Ответ

10 голосов
/ 26 июля 2011

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

Пример:

public class RequestSenderHelpers
{
    public static void Send(HttpWebRequest request, AsyncCallback internalCallback, object requestState)
    {
        var result = new Mock<IAsyncResult>();
        result.Setup(x => x.AsyncState).Returns(requestState);
        internalCallback(result.Object);
    }

}

    [Test]
    public void Callback_VerifyingWithMethodImplementation_VerifyWorks()
    {
        // arrange
        var sender = new Mock<IRequestSender>();
        sender.Setup(s => s.Send(It.IsAny<HttpWebRequest>(), It.IsAny<AsyncCallback>(), It.IsAny<object>())).Callback<HttpWebRequest, AsyncCallback, object>(RequestSenderHelpers.Send);

        // act
        sender.Object.Send(null, delegate {}, null);

        // assert
        sender.Verify(s => s.Send(It.IsAny<HttpWebRequest>(), It.IsAny<AsyncCallback>(), It.IsAny<object>()));
    }

Чтобы избежать подробных настроек, вы можете обернуть настройку метода в метод расширенияи измените свой тест соответственно:

public static class RequestSenderHelpers
{
    public static void Send(HttpWebRequest request, AsyncCallback internalCallback, object requestState)
    {
        var result = new Mock<IAsyncResult>();
        result.Setup(x => x.AsyncState).Returns(requestState);
        internalCallback(result.Object);
    }

    public static void SetupSendWithMockedAsyncResult(this Mock<IRequestSender> sender)
    {
        sender.Setup(s => s.Send(It.IsAny<HttpWebRequest>(), It.IsAny<AsyncCallback>(), It.IsAny<object>())).Callback<HttpWebRequest, AsyncCallback, object>(Send);            
    }

}

    [Test]
    public void Callback_VerifyingWithMethodImplementation_VerifyWorks()
    {
        // arrange
        var sender = new Mock<IRequestSender>();
        sender.SetupSendWithMockedAsyncResult();

        // act
        sender.Object.Send(null, delegate {}, null);

        // assert
        sender.Verify(s => s.Send(It.IsAny<HttpWebRequest>(), It.IsAny<AsyncCallback>(), It.IsAny<object>()));
    }
...