3

次のコードのテストに助けが必要です

public virtual void Update(T entity)
    {
        if (entity == null)
        {
            throw new ArgumentNullException("entity");
        }

        int iretries = 0;
        bool success = false;

        do
        {
            try
            {
                this.context.SaveChanges();
                success = true;
            }
            catch (DbUpdateConcurrencyException ex)
            {
                // Get the current entity values and the values in the database
                // as instances of the entity type
                var entry = ex.Entries.Single();
                var databaseValues = entry.GetDatabaseValues();

                // Choose an initial set of resolved values. In this case we
                // make the default be the values currently in the database: StoreWins
                object resolvedValues = ResolveConcurrency(databaseValues.ToObject());

                // Update the original values with the database values and
                // the current values with whatever the user choose.
                entry.OriginalValues.SetValues(databaseValues);
                entry.CurrentValues.SetValues(resolvedValues);

                // give up after n retries
                if (++iretries == NUMBER_OF_CONC_RETRIES)
                    throw;
            }
            catch (Exception)
            {
                //rethrow 
                throw;
            }
        } while (!success);
    }

DbUpdateConcurrencyExceptionブランチを単体テストしたい。

したがって、1 つの単純なテスト シナリオは次のようになります。

  • 新規作成DbUpdateConcurrencyException
  • SaveChanges上記の例外をスローするモック
  • SaveChangesの番号が呼び出されたことを確認します。NUMBER_OF_CONC_RETRIES
  • Updateメソッドが例外を再スローすることをアサートする

IEnumerable<DbEntityEntry>現在の状態では、上記のテスト シナリオをテストすることはできませんDbEntityEntryGetDatabaseValues()などをモックすることはできません。

簡単な解決策は、抽象化の新しいレイヤーを挿入することです。インターフェイスを使用して、現在 catch ブロックにあるコード全体を抽象化し、何もしないモックを提供するとします。

しかし、その後、そのインターフェースの実装をテストしたい状況に陥り、上記と同じ質問をすることになります。、 などをモックするDbUpdateConcurrencyExceptionにはどうすればよいですか。GetDatabaseValues

私はモッキングにmoqを使用しています。

ご意見ありがとうございます

4

1 に答える 1

1

何かをモックできない場合は、テストでモックまたはオーバーライドできる他のものの背後に隠す必要があります。テストでは、値をロードしてエントリに設定するために実際にそれらすべてを使用する必要はありません。これはすべて EF に依存しており、EF のロジックを再実装することを意味するため、コンテキストをモックするときにテストしませんSaveChanges。あなたがする必要があるのは次のとおりです。

catch (DbUpdateConcurrencyException ex) {
    RefreshValues(ex);

    // give up after n retries
    if (++iretries == NUMBER_OF_CONC_RETRIES)
        throw;
}

クラスのテストバージョンを提供するか( Moqでこれを実現することもRefreshValuesできprotected virtualます)、またはこのクラスからテストを継承してテストクラスでメソッドを直接オーバーライドすることにより、メソッドをテストでオーバーライドできます。

Moq をセットアップするには、SaveChanges メソッドを公開するコンテキストのインターフェイスが必要です。

var contextMock = new Mock<IContext>();
contextMock.Setup(m => m.SaveChanges())
           .Callback(m => throw new DbUpdateConcurrencyException());

いくつかのスローでも機能することをテストする必要がある場合は、テストでカウンターを保持し、それをコールバックで使用して、スローするかどうかを決定する必要があります。

于 2012-09-07T09:12:08.450 に答える