Как издеваться над веб-сервисом - PullRequest
25 голосов
/ 23 октября 2009

Нужно ли переписывать мой код, чтобы сделать это в интерфейсе? Или есть более простой способ? Я использую Moq

Ответы [ 4 ]

39 голосов
/ 23 октября 2009

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

например:

public class ServiceAdapter: IServiceAdapter
{
    public void CallSomeWebMethod()
    {
        var someService = new MyWebService();
        someService.SomeWebMethod();
    }
}

Тогда я просто заглушаю сервисный адаптер.

[Test]    
public void SomeMethod_Scenario_ExpectedResult()
{
    var adapterMock = new Mock<IServiceAdapter>();
    //do your test
}
34 голосов
/ 24 октября 2009

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


Если ваши проблемы на стороне сервера

Вам не нужно тестировать WebServices в целом. MS уже сделала это. Миллионы людей сделали это. Тестирование транспортного уровня, протокола, определения WebServices - пустая трата времени.

Вам нужно настроить свою бизнес-логику . Лучший способ сделать это - отделить вашу бизнес-логику от вашего WebService. Рассмотрим следующее

public class MyWebSevice : System.Web.Services.WebService
{
    private AuthenticationService _auth = new AuthenticationService ();
    private int _count = 0;
    [WebMethod]
    public string DoSomething ()
    {
        // embedded business logic, bad bad bad
        if (_auth.Authenticate ())
        {
            _count++;
        }
        return count.ToString ();
    }
}

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

public class MyService 
{
    // keeners will realise this too should be injected
    // as a dependency, but just cut and pasted to demonstrate
    // isolation
    private AuthenticationService _auth = new AuthenticationService ();
    private int _count = 0;
    public string DoSomething ()
    {
        if (_auth.Authenticate ())
        {
            _count++;
        }
        return count.ToString ();
    }
}

в продукт

// this web service is now a consumer of a business class,
// no embedded logic, so does not require direct testing
public class MyWebSevice : System.Web.Services.WebService
{
    private readonly MyService _service = new MyService ();

    [WebMethod]
    public string DoSomething ()
    {
        _service.DoSomething ();
    }
}

в тесте

// test business logic without web service! yay!
[Test]
public void Test_DoSomething ()
{
    MyService service = new MyService ();
    string actual = service.DoSomething ();
    // verify results
}

управление зависимостями [например, член AuthenticationService] является отдельной проблемой. Однако, делая ваши WebMethods простыми проходами для правильных базовых бизнес-классов и полностью удаляя из них логику, вы сможете ориентироваться на «настоящий» пользовательский код, а не на обычную реализацию вашей WebService.


Если ваши проблемы на стороне клиента

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

public partial class MyWebService :
    System.Web.Services.Protocols.SoapHttpClientProtocol 
{
    ...
    public string DoSomething () { ... }
}

public class MyClient
{
    public void CallService ()
    {
        MyWebService client = new MyWebService ();
        client.DoSomething ();
    }
}

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

public interface IMyWebService
{
    string DoSomething ();
}

public class MyWebServiceWrapper : IMyWebService
{
    public string DoSomething () 
    {
        MyWebService client = new MyWebService ();
        client.DoSomething ();
    }
}

public class MyClient
{
    private readonly IMyWebService _client = null;
    public MyClient () : this (new MyWebServiceWrapper ()) { }
    public MyClient (IMyWebService client)
    {
        _client = client;
    }
    public void CallService ()
    {
        _client.DoSomething ();
    }
}

в тесте

[Test]
public void Test_CallService ()
{
    IMyWebService mockService = null;
    // instantiate mock with expectations
    MyClient client = new MyClient (mockService);
    client.CallService ();
    // verify results
}

Как правило, если зависимости класса являются внутрипроцессными службами, решение о применении шаблона, такого как Dependency Injection [DI] или Inversion of Control [IoC], остается за вами - и вы хотите изолировать и выполнить модульное тестирование этих служб. сообщу ваш дизайн. Однако, если зависимости класса пересекают границу процесса , например, Database или WebService, я настоятельно рекомендую применять эти шаблоны, как мы это делали выше.

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

:)

10 голосов
/ 23 октября 2009

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

2 голосов
/ 09 октября 2011

есть простой способ. например, если у нас есть класс WebService с именем DbService, сначала создайте для него интерфейс (например, IService), и используйте этот интерфейс для насмешек, затем добавьте класс в свой проект и поместите это:

public partial class DbService:IService {

}

оставьте класс пустым, потому что веб-сервисы являются частичным классом, мы используем эту реализацию. (ранее

...