アプリケーションのCRUDレイヤーで単体テストを作成する場合、テストを独立させるにはどうすればよいですか?


14

そのため、ユニットテストはできる限り本で作成しようとしていますが、いくつかの単純な追加/削除メソッドをテストするときは面倒になります。

addメソッドの場合、基本的にダミーオブジェクトを作成して追加する必要があり、テストが成功した後、ダミーオブジェクトを削除する必要があります。

削除テストでは、削除できるようにダミーオブジェクトを作成する必要があるのは明らかです。

1つのテストが失敗した場合にわかるように、他のテストも失敗します。どちらも必要なためです。

「注文をキャンセルする」というテストを書く必要があるシステムでも同じです...最初にキャンセルするにはダミーの注文が必要になりますが、これは単体テストのガイドラインに反しませんか?

このようなケースはどのように処理されるのですか?


:あなたはまた、この質問を見てとることをお勧めしますprogrammers.stackexchange.com/questions/115455/...
グベン

回答:


13

まあ、あなたがやっていることには何も悪いことはありません。複数のテストで同じコードをカバーできます。それは、1つの問題が複数のテストの失敗を引き起こすことを意味します。避けたいのは、他のテストの結果に依存するテストです。つまり、削除テストは実行された追加テストに依存するため、追加テストの前に削除テストを実行すると失敗します。その問題を回避するには、各テストの開始時に「空白のスレート」があることを確認して、特定のテストで発生することが後続のテストに影響しないようにします。

これを行うための優れた方法は、インメモリデータベースに対してテストを実行することです。

追加テストで、空のデータベースを作成し、オブジェクトを追加して、実際に追加されたことを表明します。

削除テストでは、既に削除するオブジェクトを含むデータベースを作成します。オブジェクトを削除し、削除されたことを表明します。

分解コードでデータベースを吹き飛ばします。


インメモリデータベースは、高速(メモリ内)でシンプル(処理中)です。任意のデータストアでこれを行うことができます。
ポールドレイパー

3

トランザクションを使用します。

トランザクションをサポートするデータベースを使用している場合、トランザクションで各テストを実行します。テストの終了時にトランザクションをロールバックします。その後、各テストでデータベースは変更されません。

はい。注文をキャンセルするには、まず注文を作成する必要があります。それはいいです。テストでは、まず注文を作成してからキャンセルし、次に注文がキャンセルされたことを確認します。


このアイデアが大好きです。今日、大きな効果でそれを実装しました。
-pimbrouwers

3

あなたはそれをうまくやっています。単体テストの唯一の基本原則は、所有しているすべてのコードパスを網羅することです。したがって、コードが意図したとおりに機能し、変更およびリファクタリング後もそれを実行していることを確信できます。単体テストを小さく、単純で、単一の目的にすることは価値のある目標ですが、それは基本的なことではありません。APIの2つの関連するメソッドを呼び出すテストは、それ自体が疑わしいものではありません。実際、指摘しているように、それはしばしば必要です。冗長なテストを行うことの欠点は、作成に時間がかかることだけですが、開発中のほとんどすべてのこととして、これは常に行う必要があるトレードオフであり、最良のソリューションはほとんど極端なポイントの1つではありません。


2

ソフトウェアテストの手法は非常に多様であり、それらについて自分自身を教育すればするほど、多くの異なる(そして時には矛盾する)ガイダンスを見始めるようになります。行くべき単一の「本」はありません。

私はあなたが次のようなことを言うユニットテストのためのいくつかのガイダンスを見た状況にいると思います

  • 各テストはスタンドアロンであり、他のテストの影響を受けないようにする必要があります
  • 各ユニットテストは1つのことをテストする必要があります。
  • 単体テストはデータベースにヒットしないはずです

等々。'unit test'の定義方法応じて、これらはすべて正しいです

「ユニットテスト」は、「他の依存コンポーネントから分離されたコードの1ユニットに対して1つの機能を実行するテスト」のようなものとして定義します。

その定義の下で、あなたがしていること(テストを実行する前にデータベースにレコードを追加する必要がある場合)は、まったく「単体テスト」ではなく、一般に「統合テスト」と呼ばれるものです。(私の定義では、真の単体テストはデータベースにヒットしないため、削除する前にレコードを追加する必要はありません。)

統合テストは、(例えば、ユーザインタフェースとして複数のコンポーネント使用機能行使するデータベース)、及びユニットテストに適用されるガイダンスは必ずしも統合テストには適用されません。

他の人が答えで述べたように、ユニットテストのガイダンスに反することをしても、あなたがしていることは必ずしも間違っているわけではありません。代わりに、各テストメソッドで実際にテストしているものについて推論し、テストを満たすために複数のコンポーネントが必要であり、一部のコンポーネントが事前設定を必要とする場合は、先に進んでください。

しかし、何よりも、多くの種類のソフトウェアテスト(単体テスト、システムテスト、統合テスト、探索テストなど)があることを理解し、1つのタイプのガイダンスを他のすべてに適用しようとしないでください。


データベースからの削除を単体テストできないと言っているのですか?
ChrisF

データベースにアクセスしている場合、それは(定義により)単体テストではなく統合テストです。だから、その意味で、いいえ。データベースから削除する「単体テスト」はできません。何ができるユニットテストは、あなたがテストしているコードは、いくつかのデータを削除するように求められたときに、それが正しくデータ・アクセス・モジュールと相互作用することです。
エリックキング

しかし、要点は、一部の人々は「単体テスト」を異なる方法で定義する可能性があるため、「単体テスト」ガイダンスを適用する際には注意が必要です。
エリックキング

1

これがまさに、他のガイドラインの1つがインターフェイスを使用することである理由です。メソッドが特定のクラス実装の代わりにインターフェースを実装するオブジェクトを取る場合、残りのコードベースに依存しないクラスを作成できます。

別の方法は、モックフレームワークを使用することです。これらを使用すると、テストするメソッドに渡すことができるこれらのタイプのダミーオブジェクトを簡単に作成できます。ダミークラスのスタブ実装を作成する必要があるかもしれませんが、それでも実際の実装とテストの対象から分離されます。


1

1つのテストが失敗した場合にわかるように、他のテストも失敗します。どちらも必要なためです。

そう?

...これは単体テストのガイドラインに反しませんか?

番号。

このようなケースはどのように処理されるのですか?

いくつかのテストは独立しており、同じバグが原因ですべてが失敗します。それは実際には正常です。多くのテストでは、間接的にいくつかの一般的な機能をテストできます。そして、共通の機能が壊れるとすべてが失敗します。それに問題はありません。

ユニットテストはクラスとして正確に定義されるため、更新や削除のテストに使用される一般的なダミーレコードのように、コードを簡単に共有できます。


1

モックフレームワークを使用するか、メモリ内データベースで「環境」を使用できます。最後のクラスは、テストを実行する前に、テストに合格するために必要なすべてを作成できるクラスです。

私は最後のものを好む-ユーザーはあなたのテストが現実の世界に最も近くなるようにあなたがいくつかのデータを入力するのを手伝うことができる。


True-ただし、実際のデータベース接続を実際にテストしているわけではありません。あなたはそれが常にうまくいくと仮定しない限り-しかし、仮定は危険です。
ChrisF
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.