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);
}
実際のクラスは、IFileConnection
具象クラスがリモートとの接続を失ったときにスローされるSystem.IO.IOException
を処理することになっています。
Moqを使用してTransfer
クラスを作成し、Transfer
メソッドが呼び出される場合を除き、すべてのプロパティとメソッドで具体的なGetFile
クラスとして使用したいSystem.IO.IOException
をスローし、Transfer
クラスがそれを適切に処理するようにします。
仕事に適したツールを使用していますか?私はこれについて正しい方法で行っていますか?そして、NUnit
の単体テストのセットアップをどのように書きますか?
これは私がやろうとしていたことをどうやってやったかです:
[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);
}
この方法に問題がある場合、私は知りたいです。他の答えは、私がこれを間違っていることを示唆していますが、これはまさに私がやろうとしていたことでした。
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);
更新:
まず、テストするクラス(この場合はTransferクラス)ではなく、依存関係のみをモックする必要があります。コンストラクターでこれらの依存関係を記述すると、クラスが機能するために必要なサービスを簡単に確認できます。また、単体テストを作成するときに、それらを偽物に置き換えることができます。現時点では、これらのプロパティを偽物に置き換えることは不可能です。
別の依存関係を使用してこれらのプロパティを設定しているため、次のように記述します。
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モックを返すことができます。
私はこれがあなたが望むものだと思う、私はすでにこのコードをテストして動作します
使用されるツールは次のとおりです(これらのツールはすべてNugetパッケージとしてダウンロードできます)。
http://fluentassertions.codeplex.com/
http://autofixture.codeplex.com/
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をisolationでテストするためにそれらを置き換える必要があります。私が代替と言うとき、私はあなたのニーズに応じてモック、ダミー、モックなどを使用することを言っています
この場合、外部依存関係はIFileConnection
であるため、この依存関係のモックを作成して例外をスローするように構成し、SUTの実際のメソッドを呼び出して、メソッドが期待どおりに例外を処理することをアサートする必要があります
var fixture = new Fixture().Customize(new AutoMoqCustomization());
:このリニーは、新しい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ライブラリを使用し、SUTからTransferFiles
realメソッドを呼び出すだけです。 およびパラメーターとして、モックされたIFileConnection
を受け取るため、SUT TransferFiles
メソッドの通常のフローで_IFileConnection.Get
_を呼び出すたびに、モックされたオブジェクト構成された例外のスローを呼び出します。これは、SUTが例外を正しく処理していることをアサートする時間です。この場合、ShouldThrow<System.IO.IOException>()
(FluentAssertionsから)を使用して例外がスローされたことを保証しますとしょうかん)
推奨される参考文献:
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