31

私は単体テストの利点についてほぼ確信しており、PHP で書かれた既存の大規模なコードベースにこの概念を適用したいと考えています。このコードの 10% 未満はオブジェクト指向です。

いくつかの単体テスト フレームワーク (PHPUnit、SimpleTest、および phpt) を見てきました。ただし、手続き型コードをテストするこれらの例は見つかりませんでした。私の状況に最適なフレームワークは何ですか? 非 OOP コードを使用して PHP の単体テストを行う例はありますか?

4

3 に答える 3

39

手続き型 PHP を単体テストできますが、問題ありません。また、コードが HTML と混ざっていても、決して運が悪いわけではありません。

アプリケーションまたは受け入れテストのレベルでは、手続き型 PHP はおそらくスーパーグローバル ( など) の値に依存して$_POST, $_GET, $_COOKIE動作を決定し、テンプレート ファイルを含めて出力を吐き出します。

アプリケーション レベルのテストを行うには、スーパーグローバル値を設定するだけです。出力バッファーを開始します (大量の html が画面にあふれないようにするため); ページを呼び出します。バッファー内のものに対してアサートします。最後にバッファを破棄します。したがって、次のようなことができます。

public function setUp()
{
    if (isset($_POST['foo'])) {
        unset($_POST['foo']);
    }
}

public function testSomeKindOfAcceptanceTest()
{
    $_POST['foo'] = 'bar';
    ob_start();
    include('fileToTest.php');
    $output = ob_get_flush();
    $this->assertContains($someExpectedString, $output);
}

多くのインクルードを含む巨大な「フレームワーク」の場合でも、この種のテストにより、アプリケーション レベルの機能が機能しているかどうかがわかります。これは、コードの改善を開始する際に非常に重要になります。なぜなら、データベース コネクタがまだ機能し、以前よりも見栄えが良いと確信している場合でも、ボタンをクリックして、はい、まだできることを確認したくなるからです。データベースを介してログインおよびログアウトします。

下位レベルでは、変数のスコープと、関数が副作用 (true または false を返す) によって機能するか、結果を直接返すかによって、わずかな違いがあります。

変数は、関数間でパラメーターまたはパラメーターの配列として明示的に渡されますか? それとも、変数はさまざまな場所に設定され、グローバルとして暗黙的に渡されますか? (良い) 明示的なケースである場合は、(1) 関数を保持するファイルを含め、(2) 関数テスト値を直接入力し、(3) 出力をキャプチャしてそれに対してアサートすることにより、関数を単体テストできます。グローバルを使用している場合は、(上記の $_POST の例のように) テスト間ですべてのグローバルを慎重に無効にするように特に注意する必要があります。多くのグローバルをプッシュおよびプルする関数を扱う場合、テストを非常に小さく保つことも特に役立ちます (5 ~ 10 行、1 ~ 2 アサート)。

もう 1 つの基本的な問題は、関数が出力を返すことによって機能するか、渡されたパラメーターを変更して代わりに true/false を返すことによって機能するかです。最初のケースでは、テストはより簡単ですが、繰り返しますが、両方のケースで可能です:

// assuming you required the file of interest at the top of the test file
public function testShouldConcatenateTwoStringsAndReturnResult()
{
  $stringOne = 'foo';
  $stringTwo = 'bar';
  $expectedOutput = 'foobar';
  $output = myCustomCatFunction($stringOne, $stringTwo);
  $this->assertEquals($expectedOutput, $output);
}

コードが副作用によって機能し、true または false を返す悪いケースでも、非常に簡単にテストできます。

/* suppose your cat function stupidly 
 * overwrites the first parameter
 * with the result of concatenation, 
 * as an admittedly contrived example 
 */
public function testShouldConcatenateTwoStringsAndReturnTrue()
    {
      $stringOne = 'foo';
      $stringTwo = 'bar';
      $expectedOutput = 'foobar';
      $output = myCustomCatFunction($stringOne, $stringTwo);
      $this->assertTrue($output);
      $this->Equals($expectedOutput, $stringOne);
    }

お役に立てれば。

于 2009-06-18T17:07:26.367 に答える
6

単体テストがうまく機能し、それらを使用する必要があるのは、いくつかの入力を与えるコード片があり、いくつかの出力が返されることを期待している場合です。後で機能を追加するときに、テストを実行して、古い機能を同じように実行していることを確認できます。

したがって、手続き型のコードベースがある場合は、テスト メソッドで関数を呼び出してこれを実現できます。

require 'my-libraries.php';
class SomeTest extends SomeBaseTestFromSomeFramework {
    public function testSetup() {
        $this->assertTrue(true);
    }

    public function testMyFunction() {
        $output = my_function('foo',3);

        $this->assertEquals('expected output',$output);
    }
}

コードベースとテスト フレームワークには、Web ブラウザー (セッション、共有グローバル変数など)。ライブラリ コードをインクルードして簡単なテスト (上記の testSetup 関数) を実行できるようになるまで、ある程度の時間を費やすことを期待してください。

コードに関数がなく、HTML ページを出力する単なる一連の PHP ファイルである場合は、運が悪いと言えます。コードを個別のユニットに分割することはできません。つまり、ユニット テストはあまり役に立ちません。SeleniumWatirなどの製品を使用して、「受け入れテスト」レベルで時間を費やす方がよいでしょう。これらにより、ブラウザを自動化し、ページのコンテンツを特定の場所/フォームでチェックできます。

于 2009-05-22T20:09:09.480 に答える
1

を使用して、非 oop コードをテスト クラスに含めることができます。

require_once 'your_non_oop_file.php' # Contains fct_to_test()

phpUnit を使用して、テスト関数を定義します。

testfct_to_test() {
   assertEquals( result_expected, fct_to_test(), 'Fail with fct_to_test' );
}
于 2009-05-22T18:58:53.753 に答える