4

サードパーティの FTP ライブラリhttp://ftps.codeplex.com/を使用するクラスがあり、FTP ライブラリではなくそのクラスだけを単体テストできるようにモックしたいと思います。私はそれをやったが、それは私には厄介だと感じる. 詳細には、クラスは AlexPilotti.FTPS.Client.FTPSClient クラスの次のメソッドを使用します。

public string Connect(string hostname, ESSLSupportMode sslSupportMode)
public ulong PutFile(
    string localFileName, string remoteFileName,
    FileTransferCallback transferCallback)

デリゲート AlexPilotti.FTPS.Client.FileTransferCallback は次のようになります。

public delegate void FileTransferCallback(
    FTPSClient sender, ETransferActions action, 
    string localObjectName, string remoteObjectName,
    ulong fileTransmittedBytes, ulong? fileTransferSize, ref bool cancel);

PutFile は FTP ライブラリからデリゲートを取得し、そのデリゲートもライブラリから 2 つの異なる型を取得するため、問題が発生する可能性があります。これらの型から切り離すために、ジェネリックを使用することにしました。

モック オブジェクトを作成できるようにするために、インターフェイスを作成してから、FTP ライブラリ機能のラッパーである派生クラスを作成しました。

// Interface so that dependency injection can be used.
public delegate void FileTransferCallback<T1, T2>(T1 sender, T2 action,
    string localObjectName, string remoteObjectName,
    ulong fileTransmittedBytes, ulong? fileTransferSize,
    ref bool cancel);

public interface IFTPClient<T1, T2> : IDisposable
{
    string Connect(string hostname, System.Net.NetworkCredential credential);
    ulong PutFile(
        string localFileName, string remoteFileName,
        FileTransferCallback<T1, T2> transferCallback);
}

// Derived class, the wrapper
using FTPSClient = FTPS.Client.FTPSClient;
using ETransferActions = FTPS.Client.ETransferActions;

public class FTPClient : IFTPClient<FTPSClient, ETransferActions>
{
    public FTPClient()
    {
        client = new AlexPilotti.FTPS.Client.FTPSClient();
    }

    public ulong PutFile(
        string localFileName, string remoteFileName,
        FileTransferCallback<FTPSClient, ETransferActions> transferCallback)
    {
        callback = transferCallback;
        return client.PutFile(localFileName, remoteFileName, TransferCallback);
    }

    public void Dispose()
    {
        client.Dispose();
    }

    public string Connect(
        string hostname, System.Net.NetworkCredential credential)
    {
        return client.Connect(hostname, credential, 
            FTPS.Client.ESSLSupportMode.ClearText);
    }

    void TransferCallback(
        FTPS.Client.FTPSClient sender, FTPS.Client.ETransferActions action, 
        string localObjectName, string remoteObjectName,
        ulong fileTransmittedBytes, ulong? fileTransferSize, 
        ref bool cancel)
    {
        callback.Invoke(sender, action, localObjectName, remoteObjectName,
            fileTransmittedBytes, fileTransferSize, ref cancel);
    }

    private AlexPilotti.FTPS.Client.FTPSClient client;
    private FileTransferCallback<FTPSClient, ETransferActions> callback;
}

このインターフェイスを使用し、単体テストを行うクラスを次に示します。Connect メソッドのみが使用されるように少し削除しましたが、それでも私の問題を示していることを願っています。

public class FTPServerConnection<T1, T2>
{
    public void Init(
        IFTPClient<T1, T2> client, string serverName,
        string userName, string passwd)
    {
        IsConnected = false;

        ServerName = serverName;
        UserName = userName;
        Passwd = passwd;

        ftpClient = client;

        Connect();
    }

    public void Connect()
    {
        ftpClient.Connect(
           ServerName,
           new System.Net.NetworkCredential(UserName, Passwd));
    }

    public string ServerName { protected set; get; }
    public string UserName { protected set; get; }
    public string Passwd { protected set; get; }

    public bool IsConnected { protected set; get; }

    private IFTPClient<T1, T2> ftpClient;
}

そして最後に単体テスト:

[TestMethod()]
public void FTPServerConnectionConstructorTest()
{
    var ftpClient = new Mock<IFTPClient<object, object>>();
    var ftpServer = new FTPServerConnection<object, object>();
    ftpServer.Init(ftpClient.Object, "1.2.3.4", "user", "passwd");

    Assert.AreEqual("1.2.3.4", ftpServer.ServerName);
    Assert.AreEqual("user", ftpServer.UserName);
    Assert.AreEqual("passwd", ftpServer.Passwd);
}

このような状況での通常のアプローチは何ですか? 私のアプローチは面倒なようで、もっと簡単な方法があるかどうか疑問に思っています。ありがとう。

4

1 に答える 1

1

サードパーティのデリゲートに対処する必要があるため、これは面倒だと思いますが、私にはかなり標準的な抽象化/モックアプローチのように見えます.

私の唯一のコメントは、 の構造がFTPServerConnection少し非標準に見えるということです。それが書かれている方法では、無効な状態(つまり、クライアントの詳細がないもの)でインスタンス化する必要がInit()あり、それ自体が呼び出す を呼び出しますConnect()(テストの外観から、サーバークラスのクライアントによって呼び出されることはありません)。コンストラクターで引数をInit()指定してからFTPServerConnection、メソッドを完全に削除しInit()、クライアントにこれを実行させるのがより一般的です。

var ftpClient = new Mock<IFTPClient<object, object>>();

using (var ftpServer = new FTPServerConnection<object, object>(
     ftpClient.Object,
     "1.2.3.4",
     "user",
     "passwd"))
{
    ftpServer.Connect();
}

...しかし、繰り返しになりますが、FTPライブラリを抽象化した方法に関しては、それはまともな方法だと思います:)

于 2012-09-19T14:28:45.147 に答える