回答:
データベースへの呼び出しをモックアウトすることをお勧めします。モックは基本的に、呼び出し側が利用できる同じプロパティ、メソッドなどを持っているという意味で、メソッドを呼び出そうとしているオブジェクトのように見えるオブジェクトです。しかし、特定のメソッドが呼び出されたときにプログラムが行うようにプログラムされているアクションを実行する代わりに、それを完全にスキップして、結果を返します。その結果は通常、事前に定義されます。
オブジェクトをモック用に設定するには、次の疑似コードのように、制御/依存性注入パターンのある種の反転を使用する必要があります。
class Bar
{
private FooDataProvider _dataProvider;
public instantiate(FooDataProvider dataProvider) {
_dataProvider = dataProvider;
}
public getAllFoos() {
// instead of calling Foo.GetAll() here, we are introducing an extra layer of abstraction
return _dataProvider.GetAllFoos();
}
}
class FooDataProvider
{
public Foo[] GetAllFoos() {
return Foo.GetAll();
}
}
次に、単体テストでFooDataProviderのモックを作成します。これにより、実際にデータベースにアクセスすることなく、GetAllFoosメソッドを呼び出すことができます。
class BarTests
{
public TestGetAllFoos() {
// here we set up our mock FooDataProvider
mockRepository = MockingFramework.new()
mockFooDataProvider = mockRepository.CreateMockOfType(FooDataProvider);
// create a new array of Foo objects
testFooArray = new Foo[] {Foo.new(), Foo.new(), Foo.new()}
// the next statement will cause testFooArray to be returned every time we call FooDAtaProvider.GetAllFoos,
// instead of calling to the database and returning whatever is in there
// ExpectCallTo and Returns are methods provided by our imaginary mocking framework
ExpectCallTo(mockFooDataProvider.GetAllFoos).Returns(testFooArray)
// now begins our actual unit test
testBar = new Bar(mockFooDataProvider)
baz = testBar.GetAllFoos()
// baz should now equal the testFooArray object we created earlier
Assert.AreEqual(3, baz.length)
}
}
一言で言えば、一般的なモックのシナリオです。もちろん、実際のデータベース呼び出しも単体テストする必要があるでしょう。そのため、データベースにアクセスする必要があります。
理想的には、オブジェクトは永続的に無知である必要があります。たとえば、リクエストを行う「データアクセスレイヤー」があり、オブジェクトを返す必要があります。このようにして、その部分を単体テストから除外することも、単独でテストすることもできます。
オブジェクトがデータレイヤーに密結合されている場合、適切な単体テストを行うことは困難です。ユニットテストの最初の部分は「ユニット」です。すべてのユニットを個別にテストできる必要があります。
私のc#プロジェクトでは、NHibernateと完全に別のデータレイヤーを使用しています。私のオブジェクトはコアドメインモデルにあり、アプリケーション層からアクセスされます。アプリケーション層は、データ層とドメインモデル層の両方と通信します。
アプリケーション層は、「ビジネス層」とも呼ばれます。
あなたがPHPを使用している場合、のクラスの特定のセットを作成ONLYデータアクセス。オブジェクトがどのように永続化されているかをオブジェクトが把握していないことを確認し、アプリケーションクラスで2つを関連付けます。
別のオプションは、モック/スタブを使用することです。
データベースアクセスでオブジェクトを単体テストする最も簡単な方法は、トランザクションスコープを使用することです。
例えば:
[Test]
[ExpectedException(typeof(NotFoundException))]
public void DeleteAttendee() {
using(TransactionScope scope = new TransactionScope()) {
Attendee anAttendee = Attendee.Get(3);
anAttendee.Delete();
anAttendee.Save();
//Try reloading. Instance should have been deleted.
Attendee deletedAttendee = Attendee.Get(3);
}
}
これにより、データベースの状態が元に戻ります。基本的にはトランザクションのロールバックのように、副作用なしに何度でもテストを実行できます。このアプローチは、大規模なプロジェクトで成功裏に使用されています。ビルドの実行には少し時間がかかります(15分)が、1800の単体テストを行うのは恐ろしいことではありません。また、ビルド時間が問題になる場合は、ビルドプロセスを変更して複数のビルドを作成できます。1つはsrcのビルド用で、もう1つはユニットテスト、コード分析、パッケージ化などを処理するために後で起動します。
大量の "ビジネスロジック" SQL操作を含む中間層プロセスの単体テストを検討し始めたとき、おそらく私たちの経験を味わうことができます。
最初に、適切なデータベース接続を「組み込む」ことができる抽象化レイヤーを作成しました(この場合は、単一のODBCタイプの接続をサポートしただけです)。
これが整うと、コードで次のようなことができるようになります(C ++で作業しますが、きっとあなたはそのアイデアを理解できます)。
GetDatabase()。ExecuteSQL( "INSERT INTO foo(blah、blah)")
通常の実行時に、GetDatabase()は、ODBCを介してデータベースに直接、すべてのSQL(クエリを含む)を供給したオブジェクトを返します。
その後、インメモリデータベースの調査を開始しました。長い目で見れば、SQLiteが最良のようです。(http://www.sqlite.org/index.html)。設定と使用は非常に簡単で、GetDatabase()をサブクラス化してオーバーライドすることで、SQLをメモリ内のデータベースに転送し、実行されたすべてのテストで作成および破棄できます。
これはまだ初期段階ですが、これまでのところ順調です。ただし、必要なテーブルを作成し、テストデータを入力する必要があります。ただし、ここでは、ヘルパー関数の一般的なセットであり、これらすべてを私たちのために行うことができます。
全体的に、それは私たちのTDDプロセスに非常に役立ちました。なぜなら、特定のバグを修正するためにまったく無害な変更のように見えるものを行うと、システムの他の(検出が困難な)領域に非常に奇妙な影響を与える可能性があるためです-sql /データベースの性質そのものが原因です。
明らかに、私たちの経験はC ++開発環境を中心に扱っていますが、PHP / Pythonでも同様の機能が得られると思います。
お役に立てれば。
クラスを単体テストする場合は、データベースアクセスをモックする必要があります。結局のところ、単体テストでデータベースをテストする必要はありません。それは統合テストになります。
呼び出しを抽象化し、期待されるデータを返すだけのモックを挿入します。クラスがクエリの実行以上のことを行わない場合、それらをテストする価値はないかもしれませんが...
本xUnit Test Patternsは、データベースにヒットするユニットテストコードを処理するいくつかの方法を説明しています。IMO遅いのでやりたくないと言っている他の人たちにも同意します。より高いレベルのものをテストするためにdb接続をモックアウトすることは良い考えですが、実際のデータベースを操作するためにできることについての提案については、この本をチェックしてください。
あなたが持っているオプション:
データベースを注入します。(疑似Javaの例ですが、すべてのオブジェクト指向言語に適用されます)
クラスデータベース{ public Result query(String query){...ここに実際のデータベース...} }現在、本番環境では通常のデータベースを使用し、すべてのテストでアドホックに作成できるモックデータベースを挿入するだけです。MockDatabaseクラスはDatabase { public Result query(String query){ 「模擬結果」を返す; } }
クラスObjectThatUsesDB { public ObjectThatUsesDB(Database db){ this.database = db; } }
User
、タプルの代わりに返す{name: "marcin", password: "blah"}
)「データベース」オブジェクトを作成します。アドホックに構築された実際のオブジェクトですべてのテストを記述し、この変換を確実にするデータベースに依存する1つの大きなテストを記述します。正常に動作します。もちろん、これらのアプローチは相互に排他的ではなく、必要に応じてそれらを組み合わせて組み合わせることができます。
プロジェクトが全体的に高い凝集度と疎結合を持っている場合、データベースアクセスの単体テストは十分に簡単です。このようにして、一度にすべてをテストしなくても、特定の各クラスが行うことだけをテストできます。
たとえば、ユーザーインターフェイスクラスをユニットテストする場合、作成するテストでは、UI内のロジックが期待どおりに機能することを確認するだけで、その機能の背後にあるビジネスロジックやデータベースアクションは確認しないでください。
実際のデータベースアクセスを単体テストしたい場合は、ネットワークスタックとデータベースサーバーに依存するため、実際にはより多くの統合テストが行われますが、SQLコードが要求どおりに機能することを確認できます。行う。
私にとってユニットテストの隠された力は、それがない場合よりもはるかに優れた方法でアプリケーションを設計しなければならないことです。これは、「この機能ですべてをやるべきだ」という考え方から離れることができたからです。
申し訳ありませんが、PHP / Pythonの特定のコード例はありませんが、.NETの例を確認したい場合は、これと同じテストを行うために使用した手法を説明する投稿があります。
私は通常、オブジェクト(およびORM(存在する場合))のテストとデータベースのテストの間にテストを分割しようとします。私はデータアクセス呼び出しをモックすることによってオブジェクト側のことをテストしますが、私の経験では、通常はかなり制限されているdbとのオブジェクトの相互作用をテストすることによって、データベース側をテストします。
私は、データアクセス部分のモックを開始するまで単体テストの作成に不満を感じていたため、テストデータベースを作成したり、その場でテストデータを生成したりする必要はありませんでした。データをモックすることで、実行時にすべてを生成し、オブジェクトが既知の入力で正しく動作することを確認できます。
私はこれをPHPで行ったことはなく、Pythonを使用したこともありませんが、データベースへの呼び出しをモックアウトすることをお勧めします。これを行うには、サードパーティツールまたは自分で管理するIoCを実装できます。次に、偽の呼び出しの結果を制御するデータベース呼び出し元のモックバージョンを実装できます。
インターフェイスにコーディングするだけで、IoCの単純な形式を実行できます。これには、コードで何らかのオブジェクト指向を実行する必要があるため、あなたの行動に適用されない可能性があります(私が続ける必要があるのは、PHPとPythonについての言及だけであるためです)
それが役に立てば幸いです。他に何もない場合は、今検索する用語がいくつかあります。
私は最初の投稿に同意します-データベースアクセスは、インターフェイスを実装するDAOレイヤーに取り除かれるべきです。次に、DAOレイヤーのスタブ実装に対してロジックをテストできます。
単体テストのテストデータを設定するのは難しい場合があります。
Javaの場合、Spring APIを使用してユニットテストを行うと、ユニットレベルでトランザクションを制御できます。つまり、データベースの更新/挿入/削除を含む単体テストを実行し、変更をロールバックできます。実行の最後に、実行を開始する前の状態にすべてをデータベースに残します。私にとって、それは可能な限り良いことです。