PHPunitの実装プラクティス

自動テストの利点については十分に説明されていますが( ここここなど )、これまでのところ多くはテストを書いていません。 理由の1つは、テスト自動化の提案された方法がほとんどの場合に必要以上に複雑であることです。 今日は、これがどのように行われるかについてお話したいと思います。



この記事は実用的な目的のみを目的としており、phpunitの概念を理解できるように設計されています。 実装には、ZF、mysql + innodbが使用されますが、必要に応じて、任意のツールキットで使用できます。 さらに、 dklab.ru / lib / PHP_Exceptionizerが使用されます(通知と警告を実行に変換します)。

準備する



テストフォルダは、プロジェクトツリーに次の構造で作成されます。

d-アプリケーション
d-モデル
d-トリガー
...
run.php

アプリケーションおよびモデルフォルダーの内容は、プロジェクト内のコントローラーおよびモデルの構造に従います。 テストスイートは使用しないため、テストを含む新しいファイルをシステムに追加するときに、このテストをスイートに追加する必要はありません。 トリガーフォルダーには、テーブルの名前のフォルダーがあり、その中にはトリガーとして名前が付けられたファイルがあります。
run.phpファイルには、テストの実行に必要なコード(クラスの自動ロード、データベースへの接続など)が含まれています。 スクリプトは、悪意のある環境と戦闘環境の両方で動作するはずです。 これは、Zend_Console_Getoptを使用して実行できます。 行ファイルの最後

PHPUnit_Util_Filter::addFileToFilter(__FILE__, 'PHPUNIT' );
PHPUnit_TextUI_Command::main();


* This source code was highlighted with Source Code Highlighter .


それらが機能するためには、phpunitをシステムにインストールし、起動用に設定する必要があります。

testsフォルダーからテストを実行するには、次のようにします。
php run.php models / Article(Articleフォルダーのすべてのテストが起動されます)
php run.php models / ArticleTest.php(ArticleTest.phpファイルのすべてのテストが実行されます)

モデルテスト



すべてのテストは、データベースへの接続を含むExt_Db_Table_Test_Abstractクラスから継承されます。 setUpおよびTearDownメソッドはfinalとして宣言され、_startおよび_finishに置き換えられます。 setUpではトランザクションが開始され、tearDownではロールバックされます。 したがって、各テストは独自のトランザクションで実行されるため、オブジェクトを監視してデータベースをクリーンアップする必要がなくなります。

<?
  1. <?php
  2. 抽象 クラス Ext_Db_Table_Test_AbstractはPHPUnit_Framework_TestCaseを拡張します
  3. {
  4. / **
  5. * @var Zend_Db_Adapter_Abstract
  6. * /
  7. 保護された static $ _db;
  8. パブリック 静的 関数 setDbAdapter(Zend_Db_Adapter_Abstract $ db = null
  9. {
  10. if (empty($ db)){
  11. $ db = Zend_Db_Table_Abstract :: getDefaultAdapter();
  12. }
  13. self :: $ _ db = $ db;
  14. }
  15. / **
  16. * Zend_Db_Profiler
  17. *
  18. * @return Zend_Db_Profiler
  19. * /
  20. パブリック 関数 getProfiler()
  21. {
  22. return self :: $ _ db-> getProfiler();
  23. }
  24. 最終公開 関数 setUp()
  25. {
  26. if (empty(self :: $ _ db)){
  27. self :: setDbAdapter();
  28. }
  29. self :: $ _ db-> beginTransaction(); //トランザクション内の各テスト!
  30. $ this-> _ start();
  31. }
  32. 最終公開 関数 tearDown()
  33. {
  34. $ this-> _ finish();
  35. self :: $ _ db-> rollBack();
  36. }
  37. 保護された 関数 _start()
  38. {
  39. }
  40. 保護された 関数 _finish()
  41. {
  42. }
  43. }
*このソースコードは、 ソースコードハイライターで強調表示されました。


次の重要なポイントは、フィクスチャの作成です。 テストに関するすべての記事は、このためにPHPUnit拡張機能とそのデータベース拡張機能を使用することを推奨しています。 一般に、これは正しいですが、もっと簡単にしたかったのです。

テストデータを作成するために、シングルトーンとして表示される特別なクラスTest_Objectがあります。
記事が必要な場合は、次のように記述します$ article = Test_Object :: getInstance()-> addArticle();

  1. パブリック 関数 addArticle(array $ data = array())
  2. {
  3. $ base = array(
  4. 'key' => md5(mt_rand())、
  5. 'content' => md5(mt_rand())、
  6. 'name' => md5(mt_rand())、
  7. 「公開済み」 => 1
  8. 'file_id' => $ this-> addFile()-> file_id、 //依存オブジェクトを作成します
  9. 「作成済み」 =>今()
  10. );
  11. $ article_table = Article :: getInstance();
  12. if (empty($ data [ 'article_category_id' ])){ //カテゴリIDを渡さなかった場合、カテゴリは自動的に作成されます
  13. $ base [ 'article_category_id' ] = $ this-> addArticleCategory($データ)-> article_category_id; // $データ配列は、カテゴリ作成メソッドにも渡されます。
  14. }
  15. return $ this-> _ createRow($ article_table、$ base 、$ data);
  16. }
  17. 保護された 関数 _createRow(Ext_Db_Table_Abstract $テーブル、配列$ ベース 、配列$データ=配列())
  18. {
  19. $ data = array_merge($ base 、$ data);
  20. $ row = $ table-> createRow($ data);
  21. $ row-> save();
  22. $行を返します。
  23. }
*このソースコードは、 ソースコードハイライターで強調表示されました。


ここで、addArticleに渡すことで必要なデータを簡単に再定義できることがわかります。 また、このメソッドは、それ自体が依存するオブジェクトを作成します($データで渡さない場合)。 現時点では、Test_Objectには、システムで作成されたエンティティが多数あるため、約4,000行のコードが含まれています。 xmlファイルを使用することを想像してください。 このアプローチのもう1つの利点は、オブジェクトの作成が1箇所に集中し、オブジェクトのグラフを簡単に作成できることです。 たとえば、記事へのコメントが必要です;このために、addArticleComment()メソッドが記述され、必要なすべてのaddUser、AddArticleオブジェクトなどが内部的に作成されます。 必要なコメントを返します。

そして今、テスト自体。

  1. <?php
  2. ArticleTest クラスは Ext_Db_Table_Test_Abstractを拡張します
  3. {
  4. パブリック 関数 testFindByKey()
  5. {
  6. $ article = Test_Object :: getInstance()-> addArticle();
  7. $ row = Article :: getInstance()-> findByKey($ article-> key);
  8. $ this-> assertEquals($記事、$行);
  9. }
  10. }
*このソースコードは、 ソースコードハイライターで強調表示されました。


php run.phpモデルの実行とチェック/ Article / ArticleTest.php

以下は、すべてのモデルテストを実行する例です。 合計で、システムには約1200のテストと約4000のテストがあります(合計カバレッジは90%以上です)。

php run.php model/
PHPUnit 3.3.12 by Sebastian Bergmann.

E........................................................... 60 / 356
............................................................ 120 / 356
............................................................ 180 / 356
F..........................................................I 240 / 356
............................................................ 300 / 356
........................................................

Time: 42 seconds
FAILURES!
Tests: 356, Assertions: 800, Failures: 1, Errors: 1, Incomplete: 1.


コントローラーのテスト



例を使用するとわかりやすくなります。
  1. <?php
  2. class Frontend_Tender_EditControllerTest extends ControllerTestCase //環境acl、ルーティングなどが発生するクラス。
  3. {
  4. public function testAddAction() //ページが開くことを確認します。
  5. {
  6. $ this- > dispatch( '/ tender / add' );
  7. $ this-> assertNoErrors(); //プラグインエラーがエラーを登録しなかったことを確認します。
  8. $ this-> assertModuleFromParams( 'tender' );
  9. $ this-> assertControllerFromParams( 'edit' );
  10. $ this-> assertActionFromParams( 'add' );
  11. }
  12. public function testAddActionWithPost() //フォームを確認します
  13. {
  14. $データ=配列(
  15. 'name' => 'name'
  16. 'content' => md5(mt_rand())、
  17. 'phone' => '12345'
  18. 'country_id' => 3159、
  19. 'region_id' => 4312、
  20. 'city_id' => 4400、
  21. 「メール」 => md5(mt_rand())。 '@ testemail.ru'
  22. );
  23. $ this-> getRequest()-> setMethod( 'post'
  24. -> setParams($データ);
  25. $ this- > dispatch( '/ tender / add' );
  26. $ this-> assertNoErrors();
  27. $ table = Tender :: getInstance();
  28. $ row = $ table-> selectByEmail($ data [ 'email' ])-> fetchRow(); //これについては次のトピックで説明します)
  29. foreach ($キーとしての $データ=> $値){
  30. $ this-> assertEquals($値、$行-> $キー); //データベースがデータベースにあることを確認します
  31. }
  32. }
  33. }
*このソースコードは、 ソースコードハイライターで強調表示されました。


メソッドassertModuleFromParams、assertControllerFromParams、assertActionFromParamsは$ request-> getParam(blabla)をチェックします。
私たちは積極的にactionStackを使用しており、リクエストを常に変更しているため、$ request-> getModuleName()の確認は実用的ではありません。

おわりに



ここで紹介する方法は、可能な方法の1つにすぎません。 私たちにとって、それは非常に便利なように思え、時の試練をパスしたと言えます。 また、テストを作成していない場合は、テストを開始します。

psハブの最初の投稿 興味深い場合は、私が参加しているプロジェクトのアーキテクチャについて書きます。

pssこの回路はwww.okinfo.ruでテストおよび使用されています

Source: https://habr.com/ru/post/J72716/


All Articles