4

私のプロジェクトでは、SL5 + MVVM + Prism + WCF + Rx + Moq+Silverlightユニットテストフレームワークを使用しています。

私はユニットテストに不慣れで、最近DI、パターン(MVVM)などを始めました。したがって、次のコードには改善の余地がたくさんあります(そう思う場合は、私が取っているアプローチ全体を拒否してください)。

WCFサービスにアクセスするために、次のようなファクトリクラスを作成しました(ここでも、欠陥がある可能性がありますが、ご覧ください)。

namespace SomeSolution
{
    public class ServiceClientFactory:IServiceClientFactory
    {
        public CourseServiceClient GetCourseServiceClient()
        {
            var client = new CourseServiceClient();
            client.ChannelFactory.Faulted += (s, e) => client.Abort();
            if(client.State== CommunicationState.Closed){client.InnerChannel.Open();}
            return client;
        }

        public ConfigServiceClient GetConfigServiceClient()
        {
            var client = new ConfigServiceClient();
            client.ChannelFactory.Faulted += (s, e) => client.Abort();
            if (client.State == CommunicationState.Closed) { client.InnerChannel.Open(); }
            return client;
        }

        public ContactServiceClient GetContactServiceClient()
        {
            var client = new ContactServiceClient();
            client.ChannelFactory.Faulted += (s, e) => client.Abort();
            if (client.State == CommunicationState.Closed) { client.InnerChannel.Open(); }
            return client;
        }
    }
}

以下のようなシンプルなインターフェースを実装しています。

public interface IServiceClientFactory
{
    CourseServiceClient GetCourseServiceClient();
    ConfigServiceClient GetConfigServiceClient();
    ContactServiceClient GetContactServiceClient();
}

私のVMでは、上記のクラスのDIを実行し、Rxを使用して以下のようにWCFを呼び出しています。

var client = _serviceClientFactory.GetContactServiceClient();
try
{

    IObservable<IEvent<GetContactByIdCompletedEventArgs>> observable =
        Observable.FromEvent<GetContactByIdCompletedEventArgs>(client, "GetContactByIdCompleted").Take(1);

    observable.Subscribe(
        e =>
            {
                if (e.EventArgs.Error == null)
                {                                    
                    //some code here that needs to be unit-tested

                }
            },
        ex =>
        {
            _errorLogger.ProcessError(GetType().Name, MethodBase.GetCurrentMethod().Name, ErrorSource.Observable, "", -1, ex);
        }
        );
    client.GetContactByIdAsync(contactid, UserInformation.SecurityToken);
}
catch (Exception ex)
{
    _errorLogger.ProcessError(GetType().Name, MethodBase.GetCurrentMethod().Name, ErrorSource.Code, "", -1, ex);
}

次に、単体テストを作成します(はい、TDDではありません)。しかし、どこから始めればよいのかわかりません。Moqでは、BlahServiceClientをモックすることはできません。また、非同期メソッドは自動生成されたIBlahServiceインターフェースの一部ではないため、svcutilで生成されたインターフェースは役に立ちません。自動生成されたクラスのいずれかを(部分的なクラスなどを介して)拡張することを好むかもしれませんが、svcutilが生成できるすべてのコードを手動で構築することを選択するのは嫌です(率直に言って時間と予算を考慮して)。

誰か助けてもらえますか?正しい方向へのポインタは私を大いに助けます。

4

1 に答える 1

9

サービスクライアントをモックする場合、実際には、サービスクライアントが実装するインターフェイスの1つをモックします。だからあなたの場合はそうかもしれませんIContactService

System.ServiceModel.ClientBase<IContactService>生成されたコードはとの両方を実装しIContactServiceます。依存関係プロバイダー(この場合はファクトリ)が返されます-初心者向けにContactServiceClientこれを変更してください。IContactServiceこれは、現在および将来のDIに役立ちます。

さて、あなたはすでに抽象ファクトリを持っていて、今彼らはあなたのサービスインターフェースを返しますIContactService。現在インターフェースを使用しているだけなので、モックは非常に簡単です。

まず、実行しようとしているコードに関するいくつかの仮定。コードスニペットは、抽象ファクトリとサービスクライアントの両方にメッセージを提供しました。//ここで単体テストが必要なコードが 他の依存関係と相互作用しない場合は、ファクトリとサービスクライアントの両方をモックアウトして、テストをメソッドだけに分離することを検討しています。ボディコード。

例として調整を行いました。あなたのインターフェース:

public class Contact {
    public string Name { get; set; }
}

public interface IContactService {
    Contact GetContactById(int contactid);
}

public interface IContactServiceFactory {
    IContactService GetContactService();
}

次に、テストは次のように合計されます。

public void WhateverIsToBeTested_ActuallyDoesWhatItMustDo() {

    // Arrange
    var mockContactService = new Mock<IContactService>();
    mockContactService.Setup(cs => cs.GetContactById(It.IsAny<int>()))
        .Returns(new Contact { Name = "Frank Borland" });

    var fakeFactory = new Mock<IContactServiceFactory>();
    fakeFactory.Setup(f => f.GetContactService())
        .Returns(mockContactService.Object);

    /* i'm assuming here that you're injecting the dependency intoto your factory via the contructor - but 
     * assumptions had to be made as not all the code was provided
     */
    var yourObjectUnderTest = new MysteryClass(fakeFactory.Object);

    // Act
    yourObjectUnderTest.yourMysteryMethod();

    // Assert
    /* something mysterious, but expected, happened */            

}

編集:非同期メソッドのモック

非同期で生成されたメソッドはサービスメソッドの一部ではなく、Clientクラスの一部としてWCFによって作成されます。それらをインターフェースとしてモックするには、次のようにします。

  1. ContactServiceClientクラスのインターフェースを抽出します。VSでは、(クラス名を)右クリックし、リファクタリングし、インターフェイスを抽出するだけです。そして、適切な方法のみを選択してください。

  2. ContactServiceClientクラスは部分的であるため、新しいクラスファイルを作成し ContactServiceClientクラスを再定義して、IContactServiceClient抽出したばかりの新しいインターフェイスを実装します。

    public partial class ContactServiceClient : IContactServiceClient {
    }
    

そのように、そして今、クライアントクラスはまた、選択された非同期メソッドで新しいインターフェースを実装します。サービスインターフェイスを更新し、サービスクラスが再生成される場合、インターフェイス参照を使用して別の部分クラスを作成したため、インターフェイスを再抽出する必要はありません。

  1. 新しいファクトリを作成して、新しいインターフェイスを返します

    public interface IContactServiceClientFactory {
        IContactServiceClient GetContactServiceClient();
    }
    
  2. そのインターフェースで動作するようにテストを変更します。

于 2012-12-29T15:55:39.213 に答える