79

私はTransferクラスを持っています、それは次のように簡略化されています:

public class Transfer
{
    public virtual IFileConnection source { get; set; }
    public virtual IFileConnection destination { get; set; }

    public virtual void GetFile(IFileConnection connection, 
        string remoteFilename, string localFilename)
    {
        connection.Get(remoteFilename, localFilename);
    }

    public virtual void PutFile(IFileConnection connection, 
        string localFilename, string remoteFilename)
    {
        connection.Get(remoteFilename, localFilename);
    }

    public virtual void TransferFiles(string sourceName, string destName)
    {
        source = internalConfig.GetFileConnection("source");
        destination = internalConfig.GetFileConnection("destination");
        var tempName = Path.GetTempFileName();
        GetFile(source, sourceName, tempName);
        PutFile(destination, tempName, destName);
    }
}

インターフェイスの簡略化されたバージョンはIFileConnection次のようになります。

public interface IFileConnection
{
    void Get(string remoteFileName, string localFileName);
    void Put(string localFileName, string remoteFileName);
}

実際のクラスはSystem.IO.IOException、具体的なクラスがリモートとの接続を失ったときにスローされる を処理しIFileConnection、電子メールを送信することになっています。

Moq を使用してクラスを作成し、それをすべてのプロパティとメソッドでTransfer具体的なクラスとして使用します。TransferGetFileSystem.IO.IOExceptionTransfer

仕事に適したツールを使用していますか? 私はこれを正しい方法で行っていますか?そして、その単体テストのセットアップをどのように記述しますNUnitか?

4

3 に答える 3

103

モックする方法は次のとおりですFileConnection

Mock<IFileConnection> fileConnection = new Mock<IFileConnection>(
                                                           MockBehavior.Strict);
fileConnection.Setup(item => item.Get(It.IsAny<string>,It.IsAny<string>))
              .Throws(new IOException());

次に Transfer クラスをインスタンス化し、メソッド呼び出しでモックを使用します

Transfer transfer = new Transfer();
transfer.GetFile(fileConnection.Object, someRemoteFilename, someLocalFileName);

アップデート:

まず、テストしているクラスではなく、依存関係のみをモックする必要があります(この場合はクラスを転送します)。コンストラクターでこれらの依存関係を記述すると、クラスが機能するために必要なサービスを簡単に確認できます。また、単体テストを作成するときに、それらを偽物に置き換えることもできます。現時点では、これらのプロパティを偽物に置き換えることは不可能です。

別の依存関係を使用してこれらのプロパティを設定しているため、次のように記述します。

public class Transfer
{
    public Transfer(IInternalConfig internalConfig)
    {
        source = internalConfig.GetFileConnection("source");
        destination = internalConfig.GetFileConnection("destination");
    }

    //you should consider making these private or protected fields
    public virtual IFileConnection source { get; set; }
    public virtual IFileConnection destination { get; set; }

    public virtual void GetFile(IFileConnection connection, 
        string remoteFilename, string localFilename)
    {
        connection.Get(remoteFilename, localFilename);
    }

    public virtual void PutFile(IFileConnection connection, 
        string localFilename, string remoteFilename)
    {
        connection.Get(remoteFilename, localFilename);
    }

    public virtual void TransferFiles(string sourceName, string destName)
    {
        var tempName = Path.GetTempFileName();
        GetFile(source, sourceName, tempName);
        PutFile(destination, tempName, destName);
    }
}

このようにして、 internalConfig をモックして、必要な機能を実行する IFileConnection モックを返すようにすることができます。

于 2012-04-25T21:16:05.950 に答える
16

私はこれがあなたが望むものだと思います、私はすでにこのコードをテストして動作します

使用されるツールは次のとおりです:(これらのツールはすべてNugetパッケージとしてダウンロードできます)

http://fluentassertions.codeplex.com/

http://autofixture.codeplex.com/

http://code.google.com/p/moq/

https://nuget.org/packages/AutoFixture.AutoMoq

var fixture = new Fixture().Customize(new AutoMoqCustomization());
var myInterface = fixture.Freeze<Mock<IFileConnection>>();

var sut = fixture.CreateAnonymous<Transfer>();

myInterface.Setup(x => x.Get(It.IsAny<string>(), It.IsAny<string>()))
        .Throws<System.IO.IOException>();

sut.Invoking(x => 
        x.TransferFiles(
            myInterface.Object, 
            It.IsAny<string>(), 
            It.IsAny<string>()
        ))
        .ShouldThrow<System.IO.IOException>();

編集:

説明させてください:

テストを書くときは、何をテストしたいかを正確に知っている必要があります。これは「テスト対象(SUT)」と呼ばれます。私の理解が正しければ、この場合のSUTは次のようになります。Transfer

したがって、これを念頭に置いて、SUTをモックするべきではありません。SUTを置き換えると、実際に実際のコードをテストすることはありません。

SUTに外部依存関係がある場合(非常に一般的)、 SUTを個別にテストするには、それらを置き換える必要があります。私が代用と言うとき、私はあなたのニーズに応じてモック、ダミー、モックなどを使用することを指します

この場合、外部依存関係はIFileConnection、この依存関係のモックを作成し、例外をスローするように構成する必要があるため、SUT実メソッドを呼び出して、メソッドが期待どおりに例外を処理することを表明します。

  • var fixture = new Fixture().Customize(new AutoMoqCustomization());:このlinieは、新しいFixtureオブジェクト(Autofixtureライブラリ)を初期化します。このオブジェクトは、コンストラクターパラメーターを明示的に気にすることなくSUTを作成するために使用されます。これは、コンストラクターパラメーターが自動的に作成されるかモックされるためです。この場合はMoqを使用します。

  • var myInterface = fixture.Freeze<Mock<IFileConnection>>();IFileConnection:これにより、依存関係がフリーズします。フリーズとは、Autofixtureが要求されたときに、単純化のためのシングルトンのように、常にこの依存関係を使用することを意味します。しかし、興味深いのは、この依存関係のモックを作成していることです。これは単純なMoqオブジェクトであるため、すべてのMoqメソッドを使用できます。

  • var sut = fixture.CreateAnonymous<Transfer>();:ここでAutoFixtureは私たちのためにSUTを作成しています

  • myInterface.Setup(x => x.Get(It.IsAny<string>(), It.IsAny<string>())).Throws<System.IO.IOException>();ここでは、メソッドが呼び出されるたびに例外をスローするように依存関係を構成していますGetが、このインターフェイスの残りのメソッドは構成されていないため、それらにアクセスしようとすると、予期しない例外が発生します

  • sut.Invoking(x => x.TransferFiles(myInterface.Object, It.IsAny<string>(), It.IsAny<string>())).ShouldThrow<System.IO.IOException>();:そして最後に、SUTをテストするとき、この行はFluenAssertionsライブラリを使用しTransferFiles 、SUTから実際のメソッドを呼び出すだけで、パラメーターとしてモックを受け取るため、SUTメソッドの通常のフローでIFileConnectionを呼び出すたびにモックオブジェクトは設定された例外のスローを呼び出します。これは、SUTが例外を正しく処理していることを表明するときです。この場合、(FluentAssertionsライブラリから)を使用して例外がスローされたことを確認します。IFileConnection.GetTransferFilesShouldThrow<System.IO.IOException>()

推奨される参照:

http://martinfowler.com/articles/mocksArentStubs.html

http://misko.hevery.com/code-reviewers-guide/

http://misko.hevery.com/presentations/

http://www.youtube.com/watch?v=wEhu57pih5w&feature=player_embedded

http://www.youtube.com/watch?v=RlfLCWKxHJ0&feature=player_embedded

于 2012-04-25T23:51:49.590 に答える
11

これは私がやろうとしていたことをどうにかしてやった方法です:

[Test]
public void TransferHandlesDisconnect()
{
    // ... set up config here
    var methodTester = new Mock<Transfer>(configInfo);
    methodTester.CallBase = true;
    methodTester
        .Setup(m => 
            m.GetFile(
                It.IsAny<IFileConnection>(), 
                It.IsAny<string>(), 
                It.IsAny<string>()
            ))
        .Throws<System.IO.IOException>();

    methodTester.Object.TransferFiles("foo1", "foo2");
    Assert.IsTrue(methodTester.Object.Status == TransferStatus.TransferInterrupted);
}

この方法に問題がある場合は、お知らせください。他の答えは、私がこれを間違っていることを示唆していますが、これはまさに私がやろうとしていたことでした。

于 2012-04-26T17:38:38.480 に答える