4

インターフェイスの実装を単体テストしようとしていますが、SqlTransaction パラメーターをインターフェイス メソッドの 1 つに正常にモックアウトするのに少し苦労しています。

私が興味を持っているインターフェイスとテスト中のメソッドは次のようになります..

public class MyInterface 
{ 
  void MyMethod(SqlTransaction SharedTransaction, DateTime EventTime);
} 

public class MyImplementation : MyInterface
{
  public void MyMethod(SqlTransaction SharedTransaction, DateTime EventTime)
  {
    DateTime dtLastEventTime = DateTime.MinValue;
    using(SqlCommand comm = SharedTransaction.Connection.CreateCommand())
    {
      comm.CommandText = SQL_GET_LAST_EVENTTIME_DEFINED_ELSEWHERE;
      comm.Parameters.AddWithValue("ParamName", 123);
      object oResult = comm.ExecuteScalar();
      dtLastEventTime = DateTime.Parse(oResult.ToString());
    }
    //Do something with dtLastEventTime
  }
}

私はMoqとさまざまな構文アプローチを使用してデータベースオブジェクトをモックアウトしましたが、あまり運がありませんでした.. DbTransaction、DbConnection、DbCommand など)。

私が知りたいのは、主に、この方法でトランザクションをモックアウトできるかどうか、またはここで間違ったツリーを吠えているかどうかです。幸いなことに、プロバイダー固有の SqlTransaction ではなく、一般的な DbTransaction パラメーターを使用するようにインターフェイスを変換できる可能性がありますが、それがモック化に苦労している理由であるとは確信していません。

これが(そしてこれは完全に間違っている可能性があるので、私がこれに間違って近づいている場合は私を修正するかコメントしてください)コードをモックするためにこれまでに得たものです...

   var mockParams = new Mock<DbParameterCollection>();
    mockParams.Setup(p => p.Add(new SqlParameter("ParamName", 123)));
    var mockCommand = new Mock<DbCommand>();
    mockCommand.Setup(p => p.Parameters).Returns(mockParams.Object);
    var mockConnection = new Mock<DbConnection>();
    mockConnection.Setup(con => con.CreateCommand()).Returns(mockCommand.Object);
    var mockTrans = new Mock<DbTransaction>();
    mockTrans.Setup(t => t.Connection).Returns(mockConnection.Object);   

ただし、これは mockCommand.Setup 行で ArgumentException をスローするようです.. (オーバーライドできないメンバーでの無効なセットアップ)

モックされた SqlTransaction パラメータを使用してこのメ​​ソッドを正しく単体テストする方法について、アイデアや提案はありますか?

4

1 に答える 1

4

ピーター、私はSqlServerバックエンドを想定します。

まず、MyMethod()でインバウンドパラメータータイプをIDbTransactionに変更して、MyMethod()をリファクタリングし、モックを渡すことができるようにします。次に、ExecuteScalar()呼び出しを抽象化して、テストでSqlCommandパラメーターを簡単に確認できるようにします。

Moqは再帰的なモックをサポートしているため、テストでは次のようにsharedTransactionの呼び出しをモックします。

var mockTrx = new Mock<IDbTransaction>();
mockTrx.Setup(txn => txn.Connection.CreateCommand()).Returns(new SqlCommand());

メソッドのコマンドパラメーターをチェックし、ExecuteScalar()呼び出しで値を返すためのモックをもう1つ作成すると、完了です。

リファクタリングされたMyMethod():

public void MyMethod(IDbTransaction sharedTransaction, DateTime eventTime)
{
    DateTime lastEventTime;
    using (var cmd = (SqlCommand)sharedTransaction.Connection.CreateCommand())
    {
        cmd.CommandText = "somecmdtext";
        cmd.Parameters.AddWithValue("ParamName", 123);

        object oResult = dbUtility.ExecuteScalar(cmd);

        lastEventTime = DateTime.Parse(oResult.ToString());
    }

    //Do something with dtLastEventTime
    lastEventTime += TimeSpan.FromMinutes(1);
}

テスト:

[Test]
public void ShouldCallCorrectProcWithParams()
{
    var mockTrx = new Mock<IDbTransaction>();
    mockTrx.Setup(txn => txn.Connection.CreateCommand()).Returns(new SqlCommand());

    var dbUtil = new Mock<IDbUtility>();
    dbUtil.Setup(exec => exec.ExecuteScalar(
                             It.Is<SqlCommand>(
                             cmd => cmd.CommandText == "somecmdtext"
                             && cmd.Parameters.Count == 1
                             && cmd.Parameters[0].ParameterName == "ParamName")))
                 .Returns(DateTime.Now);

     var session = new Session {dbUtility = dbUtil.Object};

     session.MyMethod(mockTrx.Object, DateTime.Now);

     mockTrx.VerifyAll();
     dbUtil.VerifyAll();

}

個人的には、SqlCommand呼び出しでパラメーターをテストするのが好きなので、この例ではこの角度から問題を攻撃しました。

于 2009-07-30T18:06:47.557 に答える