UploadImage
他の多くの外部サービスや状態に結合されているため、単体テストに問題があります。( ) を含む静的呼び出しはnew
、コードを特定の実装に密結合します。あなたの目標は、より簡単に単体テストできるようにそれらをリファクタリングすることです。また、このクラスの単体テストを行った後も、Amazon S3 サービスを実際に使用して、アップロードがエラーなしで正しく行われたか、期待どおりに失敗したかを確認するための大きなテストを行う必要があることに注意してください。単体テストを徹底的に行うことで、これらの大規模でコストのかかる可能性のあるテストの数を減らすことができれば幸いです。
実装への結合を取り除くことで、AmazonS3Client
おそらくテスト費用を最大限に活用できます。呼び出しを引き出してリファクタリングする必要がありnew AmazonS3Client
ます。このクラスのインターフェイスがまだない場合は、それをラップするインターフェイスを作成します。次に、実装を注入する方法を決定する必要があります。メソッド パラメーター、コンストラクター パラメーター、プロパティ、ファクトリなど、さまざまなオプションがあります。
単純な他のアプローチよりも興味深いので、ファクトリ アプローチを使用しましょう。わかりやすく読みやすくするために、一部の詳細は省略しています。
interface IClientFactory
{
IAmazonS3Client CreateAmazonClient();
}
interface IAmazonS3Client
{
PutObjectResponse PutObject(PutObjectRequest request); // I'm guessing here for the signature.
}
public class AmazonS3Service : IAmazonS3Service
{
// snip
private IClientFactory factory;
public AmazonS3Service(IClientFactory factory)
{
// snip
this.factory = factory;
}
public Image UploadImage(Stream stream)
{
if (stream == null) throw new ArgumentNullException("stream");
var key = string.Format("{0}.jpg", Guid.NewGuid());
var request = new PutObjectRequest
{
CannedACL = S3CannedACL.PublicRead,
Timeout = -1,
ReadWriteTimeout = 600000, // 10 minutes * 60 seconds * 1000 milliseconds
InputStream = stream,
BucketName = imageBucket,
Key = key
};
// call the factory to provide us with a client.
using (var client = factory.CreateAmazonClient())
{
using (client.PutObject(request))
{
}
}
return new Image
{
UriString = Path.Combine(baseImageUrl.AbsoluteUri, key)
};
}
}
単体テストは、MSTest では次のようになります。
[TestMethod]
public void InputStreamSetOnPutObjectRequest()
{
var factory = new TestFactory();
var service = new AmazonS3Service(factory);
using (var stream = new MemoryStream())
{
service.UploadImage(stream);
Assert.AreEqual(stream, factory.TestClient.Request.InputStream);
}
}
class TestFactory : IClientFactory
{
public TestClient TestClient = new TestClient();
public IAmazonS3Client CreateClient()
{
return TestClient;
}
}
class TestClient : IAmazonS3Client
{
public PutObjectRequest Request;
public PutObjectResponse Response;
public PutObjectResponse PutObject(PutObjectRequest request)
{
Request = request;
return Response;
}
}
ここで、正しい入力ストリームがリクエスト オブジェクトで送信されることを確認する 1 つのテストがあります。明らかに、モッキング フレームワークは、この動作をテストするための定型コードの多くを削減するのに役立ちます。リクエスト オブジェクトの他のプロパティのテストを書き始めることで、これを拡張できます。エラー ケースは、多くの場合、本番環境の実装クラスで誘発することが困難または不可能な場合があるため、単体テストが真価を発揮できる場所です。
このメソッド/クラスの他のシナリオを完全に単体テストするには、ここに渡すかモックする必要がある他の外部依存関係があります。はConfigurationManager
構成ファイルに直接アクセスします。これらの設定を渡す必要があります。Guid.NewGuid
基本的には、制御されていないランダム性のソースであり、単体テストにも適していません。をさまざまなサービスへのキー値のプロバイダーとして定義し、IKeySource
それをモックするか、単にキーを外部から渡すことができます。
最後に、テスト/リファクタリングにかかるすべての時間を、それがもたらす価値と比べて比較検討する必要があります。より多くのコンポーネントを分離するために、いつでもレイヤーを追加できますが、レイヤーを追加するたびに利益が減少します。