web-dev-qa-db-ja.com

Webサービスをモックする方法

これをインターフェースにするには、コードを書き直す必要がありますか?それとももっと簡単な方法はありますか?私はMoqを使用しています

25
zachary

私が通常行うことは、Webサービスの周りにラッパーまたはアダプターを作成し、それをモックすることです。

例えば:

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
}
39
Joseph

最近、ユニットテストとモックについていくつかの回答を書いています。私は他の場所で、何を正確にテストしているのかを自問することが重要だと書きました。あなたの特定の状況に関して、答えが「Webサービスが公開しているビジネスロジックをテストしている」であり、not「Webサービスをテストしている」-違いがあることを願っています。


あなたの懸念がサーバーサイドの場合

一般にWebサービスをテストする必要はありません。 MSはすでにそれを行っています。何百万もの人々がそれをしました。トランスポート層、プロトコル、Webサービスの定義をテストするのは時間の無駄です。

ビジネスロジックをターゲットにする必要があります。これを行う最良の方法は、Webサービスからビジネスロジックを分離することです。次のことを考慮してください

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 simple passthroughsを適切な基礎となるビジネスクラスに作成し、それらからロジックを完全に削除することで、通常のWebService実装の配管ではなく、「実際の」ユーザーコードをターゲットにすることができます。


あなたの懸念がクライアント側の場合

Webサービスを呼び出すビジネスコンポーネントがあり、単体テスト用のクライアントを作成したくないことに同意します。

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

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

ここでは、依存関係の問題があります。つまり、WebServiceをインスタンス化してホストしないとMyClient.CallServiceをテストできません。あなたがリモートサービスを所有していないか、ホストが言った場合は特に当惑します。この場合、はい、インターフェイスに対して書き込む必要があります。もう一度、ビジネスロジックを分離して分離します。

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
}

一般に、クラスの依存関係がインプロセスサービスである場合、依存性注入[DI]や制御の反転[IoC]などのパターンを適用するかどうかはあなた次第です。これらのサービスを分離して単体テストしたい場合は、設計。ただし、クラスの依存関係がプロセスの境界を超えている場合(データベースやWebサービスなど)、上記のようにこれらのパターンを適用することを強くお勧めします。

本当に、それは単なる古いインターフェース開発です。あなたはおそらくそれがどのように報われるかをすでに見ています。

:)

34
johnny g

これについてブログに書いています ずっと前に。基本的に、部分クラスと少しの労力(Webサービスを変更する頻度に応じて、自動または手動)を使用して、Webサービスプロキシクラスにインターフェイスを実装させることができます。その後、通常どおりにモックを作成できます。

10
Jon Skeet

簡単な方法があります。たとえば、DbServiceという名前のWebServiceクラスがある場合、最初にそのインターフェイス(IServiceなど)を作成し、このインターフェイスをモックに使用してから、プロジェクトにクラスを追加して次のように配置します。

public partial class DbService:IService {

}

webサービスは部分的なクラスであるため、クラスを空のままにします。この実装を使用します。 (以前は

2
SalmanAA