Ложные вызовы WCF-сервиса с помощью FakeItEasy

Я хочу проверить мой класс, который вызывает сторонний веб-сервис. Можно ли использовать FakeItEasy для этого?

Когда я пытаюсь подделать класс от Reference.cs (автоматически генерируется), UnitTest запущен и не возвращается.

Reference.cs(генерируется автоматически)

    [System.Diagnostics.DebuggerStepThroughAttribute()]
    [System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
    public partial class ws_AccessoryClient : System.ServiceModel.ClientBase<AccessoryService.ws_Accessory>,
        AccessoryService.ws_Accessory
    {
        public ws_AccessoryClient()
        {
        }

        public ws_AccessoryClient(string endpointConfigurationName) :
            base(endpointConfigurationName)
        {
        }

        public AccessoryService.ResponseMessageOf_ListOf_SomeMethodInfo SomeMethod(
            AccessoryService.RequestMessageOf_SomeMethod request)
        {
            return base.Channel.SomeMethod(request);
        }
    }

Test.cs

    [Test]
    public void DoBusinessLogicTryTest()
    {
        var accessoryProxy = A.Fake<ws_AccessoryClient>();
    }

2 ответа

Решение

Вы не можете (и почему вы хотите?).

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

interface ICallTheService
{
    void CallTheService();
}

class ServiceCaller : ICallTheService
{
    void CallTheService()
    {
        // Call the service...
    }
}

Затем вы можете подделать этот класс и убедиться, что тестируемый класс вызывает операцию CallTheService.

// fake the service caller and pass it into your service
var serviceCaller = A.Fake<ICallTheService>();

// Verify invocation
A.CallTo(() => serviceCaller.CallTheService()).MustHaveHappened();

Я хочу проверить логику в моем классе, зависит от ответа от WCF-Service

Здесь я думаю, что вы ошибаетесь с разделением интересов. Ваш тест называется DoBusinessLogicTryTest, но он зависит от System.ServiceModel, которая является проблемой инфраструктуры. Ваша бизнес-логика должна быть тестируемой без этой зависимости. Если тестируемый класс должен вести себя по-разному в зависимости от ответа, вы можете сделать что-то вроде этого:

interface ICallTheService
{
    ServiceResponseModel CallTheService();
}

enum ServiceResponseModel
{
    Success,
    PartialSuccess,
    FailureCondition1,
    FailureCondition2,
    // etc...
}

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

A.CallTo(() => serviceCaller.CallTheService()).Returns(ServiceResponseModel.Success);

Например, если некоторые исключения (определенные в WCF) обрабатываются правильно

Это также не имеет ничего общего с бизнес-логикой. Фактическая обработка исключений является обязанностью реализации ICallTheService. Фактически, я бы представил для этого другой класс, задачей которого было бы перевести различные возможные исключения из System.ServiceModel в вашу модель ответа. Например

class WCFErrorResponseTranslator
{
    ServiceResponseModel TranslateWCFException (Exception ex)
    {
        if (ex.GetType() == typeOf(TimeoutException)) { return ServiceResponseModel.TimeOut; }
        /// etc
    }
}

Это поведение затем может быть проверено изолированно.

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

Я сам использую этот подход для раскрутки фальшивых конечных точек с помощью NSubstitute, о которой рассказывается в моем блоге " Хостинг макетов как сервис WCF". Главное, что вам нужно сделать, это развернуть ServiceHost, дать ему адрес конечной точки, который вы хотите использовать, установить режим контекста на single и предоставить макет, который вы хотите использовать в качестве конечной точки.

var serviceHost = new ServiceHost(mock, new[] { baseAddress });

serviceHost.Description.Behaviors
    .Find<ServiceDebugBehavior>().IncludeExceptionDetailInFaults = true;
serviceHost.Description.Behaviors
    .Find<ServiceBehaviorAttribute>().InstanceContextMode = InstanceContextMode.Single;

serviceHost.AddServiceEndpoint(typeof(TMock), new BasicHttpBinding(), endpointAddress);

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

Посмотрев на ваш пример, вы можете рассмотреть возможность использования WCF ChannelFactory для создания своего клиента вместо использования конкретного клиентского прокси-класса. ChannelFactory создает прокси на лету, используя предоставленный вами интерфейс, и позволяет вам вводить прокси в его зависимости, используя интерфейс службы. Это упростит модульное тестирование и даст вам более отделенный дизайн.

Другие вопросы по тегам