Нужно ли мне переписать мой код, чтобы сделать это в интерфейсе? Или есть более простой способ? Я использую Moq
Как насмехаться с веб-службой
Ответ 1
То, что я обычно делаю, это создать оболочку или адаптер вокруг моего веб-сервиса и просто издеваться над этим.
например:
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
}
Ответ 2
писал пару ответов об модульном тестировании и насмешливость в последнее время. Я писал в другом месте, что важно спросить себя, что именно вы тестируете. Что касается вашей конкретной ситуации, я надеюсь, что ответ будет "Я тестирую бизнес-логику, которую предоставляет мой 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 ();
}
}
в prod
// 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
}
В общем случае, если зависимости класса являются службами in-proc, решение применить шаблон, такой как Injection Dependency [DI] или Inversion of Control [IoC], зависит от вас - и ваше желание изолировать и unit test эти услуги будут информировать ваш дизайн. Однако, если зависимости классов пересекают границу процесса, например Database или WebService, я настоятельно рекомендую применить эти шаблоны, как мы это делали выше.
Действительно, это просто старый интерфейс. Вы, наверное, уже видите, как это окупается.
:)
Ответ 3
I писал об этом давным-давно. В основном, используя частичные классы и немного усилий (автоматическое или ручное, в зависимости от того, как часто вы собираетесь менять веб-сервис), вы можете сделать прокси-класс веб-службы реализованным интерфейсом. Вы можете затем издеваться над ним как обычно.
Ответ 4
есть простой способ. например, если у нас есть класс WebService с именем DbService, сначала создайте для него интерфейс (например, IService) и используйте этот интерфейс для издевательств, затем добавьте класс в свой проект и поставьте это:
public partial class DbService:IService {
}
оставить класс пустым, поскольку веб-службы являются частичным классом, мы используем эту реализацию. (ранее