広範囲かつ詳細な紹介
私が働いているプロジェクトでは、もう1年ほど前にユニットテストの話があります。 会話に加えて、これらの会話を現実のものにする試みが繰り返し行われています。 現時点でのすべての試みは、以前に作成された単体テストが開発プロセスで実行されないという事実で終了しました。 それらはすべて、私たちのシステムの腸内の死んだコードです。 このようなソ連崩壊後の産業を想像したことがありますか? 地面から柱を突き刺す、暗い空に対するさびた補強:)
テストされた機能が何年もの間、ファイルやディレクトリで本番環境とロバに導入されなかったという事実のために使用されないテストもあります。 私たちが生み出したエントロピーはすべて、ユニットテストの無能さと、それらの実装に対する非体系的なアプローチの結果でした。 私たちのテストでは、ユニットテストのほぼすべての原則に違反しています。 それらが互いに依存しているという事実から始まり、準備作業から膨れ上がったsql-syntaxに満ちたテストメソッドなどを見ることができるという事実で終わります。ギャグがあるとすぐにテストがスローされ、テストから独立して機能が記述され始めたため、誰もテストの追加に戻らないことに注意してください。
問題の実際的な側面へのスムーズな移行
これがすべての歌詞です。 今、それは事件についての詳細です。 理解できるように、このプロジェクトは小さくはなくphpで書かれており、少なくとも1年以上(しかし実際には4年)生き続けています。
最も頑固で「技術的に有能な」顧客でさえ、同情的なPMが主張するコードの品質の必要性を理解し始めた時が来ました。 「ポインタ」は、ユニットテストの実際のテストを行うために上から来ました。 脂肪のタスクでさえ適切です。
そのため、以前の失敗の経験に基づいて、いくつかの問題を特定しました。
- フィクスチャの問題。 データベースから取得したすべてを置き換えることはできません
もかみ。 これは、データベースの非体系的な作業が原因です。 ORMはなく、ケース間の後者のすべての機能はモデルによって実行されます。 - テスト実行速度。 最後の試行試行がチョークされました
一部はこのため、一部は... - サポート可能。 ...によるサポート不能のため
テストを装備した項目1の解決から問題になりました
複数行の挿入と更新。
私たちはこの問題に関して有能な同僚、つまりJava開発者に頼りました。 その後、彼はJUnitやDbUnitなどのツールについて教えてくれました。 彼は、このツールキットに参加して使用するプロセスについて説明しました:開発中、メモリデータベースでテストを実行することで、テストの速度の問題を解決し、より頻繁に実行される可能性を高め、コミットする前に同じテストを実際に実行しますベースとし、引き続きバグを継続する統合システムを終了します。
以前にxUnitフレームワーク(SimpleTest)を使用しましたが、DbUnitについて初めて耳にしました。 実際に、それはさらに議論されます。 PHPには、PHPUnitの拡張機能としてDbUnitの実装があります。 その使用の意味は、すべてのフィクスチャがxmlファイルの形式で準備され、各テストメソッドを実行する前にテストデータベースに注入されることです。 私の意見では、これはフィクスチャサポートの問題を解決します。それにもかかわらず、xmlはsqlダンプより読みやすいためです。
実用例
personテーブルがあるとします
CREATE TABLE person ( id INTEGER PRIMARY KEY AUTOINCREMENT , name VARCHAR ( 255 ) , surname VARCHAR ( 255 ) ) ;
CREATE TABLE person ( id INTEGER PRIMARY KEY AUTOINCREMENT , name VARCHAR ( 255 ) , surname VARCHAR ( 255 ) ) ;
CREATE TABLE person ( id INTEGER PRIMARY KEY AUTOINCREMENT , name VARCHAR ( 255 ) , surname VARCHAR ( 255 ) ) ;
CREATE TABLE person ( id INTEGER PRIMARY KEY AUTOINCREMENT , name VARCHAR ( 255 ) , surname VARCHAR ( 255 ) ) ;
CREATE TABLE person ( id INTEGER PRIMARY KEY AUTOINCREMENT , name VARCHAR ( 255 ) , surname VARCHAR ( 255 ) ) ;
その中の何かをテストするために、それはいくつかのデータで満たされる必要があります。 データベース拡張PHPUnitでは、コンテンツはxml形式で記述され、テーブルの各行に1つの要素があります。 要素名はテーブル名に対応し、属性名はテーブルフィールド名に対応する必要があります。
このテーブルのフィクスチャでそのようなperson.xmlを作成します。
- <?xml version = "1.0" encoding = "UTF-8" ?>
- <データセット>
- <人
- id = "1"
- name = "Nalabal"
- 姓 = "Nadjava" />
- </データセット>
今注目テストケース:
- require_once 'PHPUnit / Extensions / Database / TestCase.php' ;
- require_once 'PHPUnit / Framework.php' ;
- クラス PersonTest は PHPUnit_Extensions_Database_TestCaseを拡張します
- {
- パブリック 関数 __construct ( )
- {
- $ this- > connection = new PDO ( 'sqlite :: memory:' ) ;
- $ this- > connection- > query ( "
- CREATE TABLE person(
- id INTEGER PRIMARY KEY AUTOINCREMENT、
- name VARCHAR(255)、
- 姓VARCHAR(255)
- );
- " ) ;
- }
- 保護された関数 getConnection ( )
- {
- return $ this- > createDefaultDBConnection ( $ this- > connection 、 'sqlite' ) ;
- }
- 保護された関数 getDataSet ( )
- {
- return $ this- > createFlatXMLDataSet ( dirname ( __ FILE__ ) 。 '/persons.xml' ) ;
- }
- }
明らかに、テストは何もテストしませんが、私たちはすでにそこで何かをしました:
これはPHPUnit_Extensions_Database_TestCaseから継承されており、setUp(データベースをクリーンアップして再度入力する)やtearDownを含むいくつかのメソッドが既に実装されています。 PHPUnitドックでは、これらのメソッドがテストメソッドとどのように相互作用するかについて詳しく説明しています。 簡単にするために、コンストラクターで上記のプレートとインメモリーデータベースを作成します。 2つの抽象Database_TestCaseメソッドを再定義した後。 1つ目はデータベースに接続するオブジェクトを返し、2つ目はXMLフィクスチャから作成されます。 これらのメソッドは、定義されたdatabase_TestCase setUpおよびtearDownメソッドで使用されます。
新しいメソッドを追加して確認してください
- パブリック 関数 testPerson ( )
- {
- $ sql = "SELECT * FROM person" ;
- $ステートメント =
- $ this- > getConnection ( ) -> getConnection ( ) -> query ( $ sql ) ;
- $ result = $ statement- > fetchAll ( ) ;
- $ this- > assertEquals ( 1 、 sizeof ( $ result ) ) ;
- $ this- > assertEquals ( 'Nalabal' 、 $ result [ 0 0 ] [ 'name' ] ) ;
- }
結果:OK(1テスト、2アサーション)
メソッドで、personsテーブルからすべてのエントリを選択し、選択がフィクスチャに準拠しているかどうかを確認します
特定のケースでは、特定のテストメソッドの実行中にデータを追加する必要がありましたが、 ドックで何も見つけることなく、コードをさらに掘り下げて、次の解決策を見つけました。
新しいフィクスチャadditionalPersons.xmlを追加します
- <?xml version = "1.0" encoding = "UTF-8" ?>
- <データセット>
- <人
- id = "2"
- 名前 = "Nageneril"
- 姓 = "マドラプラグラム" />
- <人
- id = "3"
- name = "Strudomprassal"
- 姓 = "Vashapragram" />
- </データセット>
そして、そのようなテストメソッドを書きます:
- パブリック 関数 testAdditionalPerson ( )
- {
- $ insertOperation = PHPUnit_Extensions_Database_Operation_Factory :: INSERT ( ) ;
- $ insertOperation- > execute ( $ this- > getConnection ( ) 、 $ this- > createFlatXMLDataSet ( dirname ( __ FILE__ ) 。 '/additionalPersons.xml' ) ) ;
- $ sql = "SELECT * FROM person" ;
- $ statement = $ this- > getConnection ( ) -> getConnection ( ) -> query ( $ sql ) ;
- $ result = $ statement- > fetchAll ( ) ;
- $ this- > assertEquals ( 3 、 sizeof ( $ result ) ) ;
- $ this- > assertEquals ( 'Nalabal' 、 $ result [ 0 0 ] [ 'name' ] ) ;
- $ this- > assertEquals ( 'Nageneril' 、 $ result [ 1 ] [ 'name' ] ) ;
- $ this- > assertEquals ( 'Strudomprassal' 、 $ result [ 2 ] [ 'name' ] ) ;
- }
3行目では、Operation_Factoryを使用して( 公式ドックの操作に関する質問はまだ説明されていません)、次のシグネチャを持つメソッドが1つしかないPHPUnit_Extensions_Database_Operation_IDatabaseOperationインターフェイスを実装する挿入オブジェクトを作成します。
- パブリック 関数 execute ( PHPUnit_Extensions_Database_DB_IDatabaseConnection $ connection 、 PHPUnit_Extensions_Database_DataSet_IDataSet $ dataSet ) ;
次に、前のリストの4行目で呼び出し、さらに、personals.xmlの初期フィクスチャをadditionalPersons.xmlのデータで補完します
起動の結果は次のようになります。OK(2つのテスト、6つのアサーション)
おわりに
人生のすべてがこの例ほど複雑ではない可能性があります。 したがって、私はあなたが記述された批判の人為性に屈しないようにお願いします。
状況が発展するにつれて、ドキュメントに記載されていない発掘されたすべてのものを思い出す機会として、すべての容認できる詳細に専念したいと思います。
もちろん、誰かがそれを必要とするという条件でのみです(私はすぐにこれを理解します)。