PHPで単体テストを作成するにはどうすればよいですか?[閉まっている]


97

それらの素晴らしさについてはどこでも読んだことがありますが、何らかの理由で何かをテストする必要があるかどうかを正確に理解できないようです。誰かがサンプルコードの一部を投稿して、どうやってそれをテストするのでしょうか?それほど問題ではない場合:)


5
バランスのために、PHP用の2つまたは3つの単体テストフレームワークはありません-ここにリストがあります:en.wikipedia.org/wiki/List_of_unit_testing_frameworks#PHP
Fenton

回答:


36

3番目の「フレームワーク」があります。これは、はるかに簡単に学習できます。シンプルよりもさらに簡単です。テスト、それがPHPTと呼ばれています。

入門書はここにあります:http : //qa.php.net/write-test.php

編集:サンプルコードのリクエストを確認しました。

lib.phpというファイルに次の関数があるとします。

<?php
function foo($bar)
{
  return $bar;
}
?>

本当にシンプルで単純な、渡したパラメーターが返されます。この関数のテストを見てみましょう。テストファイルfoo.phptを呼び出します。

--TEST--
foo() function - A basic test to see if it works. :)
--FILE--
<?php
include 'lib.php'; // might need to adjust path if not in the same dir
$bar = 'Hello World';
var_dump(foo($bar));
?>
--EXPECT--
string(11) "Hello World"

簡単に言うと、パラメーター$barに値"Hello World"を指定しvar_dump()、関数呼び出しの応答をfoo()

このテストを実行するには、次を使用します。 pear run-test path/to/foo.phpt

これにはシステムにPEARインストールする必要があります。これは、ほとんどの状況でかなり一般的です。インストールする必要がある場合は、入手可能な最新バージョンをインストールすることをお勧めします。設定の手助けが必要な場合は、遠慮なく尋ねてください(ただし、OSなどを提供してください)。


そうじゃないのrun-tests
ダーマン

30

単体テストに使用できるフレームワークは2つあります。SimpletestPHPUnitです。PHPUnitのホームページでテストを作成して実行する方法に関するチュートリアルを読んでください。とても簡単で、よく説明されています。


21

コーディングスタイルを変更してそれに対応することで、ユニットテストをより効果的にすることができます。

Google Testing Blog、特にWriting Testable Codeに関する投稿を閲覧することをお勧めします。


7
あなたは素晴らしい投稿について言及したと思います。答えを「ユニットテストはあまり効果的ではない」から始めると、ほとんどの場合、私はテストに熟達していることに反対票を投じました...おそらく、肯定的な方法で言い換えると、人々が記事を読むようになるでしょう。
xtofl 2013年

2
@xtoflは「ポジティブ」をわずかに上げるように編集しました:)
icc97

13

他の人のやり方を学ぶ時間がなかったので、自分で転がしました。これを書くのに約20分、ここに投稿するのに10分かかりました。

ユニットテストは私にとって非常に便利です。

これは少し長いですが、それ自体が説明されており、下部に例があります。

/**
 * Provides Assertions
 **/
class Assert
{
    public static function AreEqual( $a, $b )
    {
        if ( $a != $b )
        {
            throw new Exception( 'Subjects are not equal.' );
        }
    }
}

/**
 * Provides a loggable entity with information on a test and how it executed
 **/
class TestResult
{
    protected $_testableInstance = null;

    protected $_isSuccess = false;
    public function getSuccess()
    {
        return $this->_isSuccess;
    }

    protected $_output = '';
    public function getOutput()
    {
        return $_output;
    }
    public function setOutput( $value )
    {
        $_output = $value;
    }

    protected $_test = null;
    public function getTest()
    {
        return $this->_test;
    }

    public function getName()
    {
        return $this->_test->getName();
    }
    public function getComment()
    {
        return $this->ParseComment( $this->_test->getDocComment() );
    }

    private function ParseComment( $comment )
    {
        $lines = explode( "\n", $comment );
        for( $i = 0; $i < count( $lines ); $i ++ )
        {
            $lines[$i] = trim( $lines[ $i ] );
        }
        return implode( "\n", $lines );
    }

    protected $_exception = null;
    public function getException()
    {
        return $this->_exception;
    }

    static public function CreateFailure( Testable $object, ReflectionMethod $test, Exception $exception )
    {
        $result = new self();
        $result->_isSuccess = false;
        $result->testableInstance = $object;
        $result->_test = $test;
        $result->_exception = $exception;

        return $result;
    }
    static public function CreateSuccess( Testable $object, ReflectionMethod $test )
    {
        $result = new self();
        $result->_isSuccess = true;
        $result->testableInstance = $object;
        $result->_test = $test;

        return $result;
    }
}

/**
 * Provides a base class to derive tests from
 **/
abstract class Testable
{
    protected $test_log = array();

    /**
     * Logs the result of a test. keeps track of results for later inspection, Overridable to log elsewhere.
     **/
    protected function Log( TestResult $result )
    {
        $this->test_log[] = $result;

        printf( "Test: %s was a %s %s\n"
            ,$result->getName()
            ,$result->getSuccess() ? 'success' : 'failure'
            ,$result->getSuccess() ? '' : sprintf( "\n%s (lines:%d-%d; file:%s)"
                ,$result->getComment()
                ,$result->getTest()->getStartLine()
                ,$result->getTest()->getEndLine()
                ,$result->getTest()->getFileName()
                )
            );

    }
    final public function RunTests()
    {
        $class = new ReflectionClass( $this );
        foreach( $class->GetMethods() as $method )
        {
            $methodname = $method->getName();
            if ( strlen( $methodname ) > 4 && substr( $methodname, 0, 4 ) == 'Test' )
            {
                ob_start();
                try
                {
                    $this->$methodname();
                    $result = TestResult::CreateSuccess( $this, $method );
                }
                catch( Exception $ex )
                {
                    $result = TestResult::CreateFailure( $this, $method, $ex );
                }
                $output = ob_get_clean();
                $result->setOutput( $output );
                $this->Log( $result );
            }
        }
    }
}

/**
 * a simple Test suite with two tests
 **/
class MyTest extends Testable
{
    /**
     * This test is designed to fail
     **/
    public function TestOne()
    {
        Assert::AreEqual( 1, 2 );
    }

    /**
     * This test is designed to succeed
     **/
    public function TestTwo()
    {
        Assert::AreEqual( 1, 1 );
    }
}

// this is how to use it.
$test = new MyTest();
$test->RunTests();

これは出力します:

テスト:TestOneは失敗でした 
/ **
*このテストは失敗するように設計されています
** /(行:149-152;ファイル:/Users/kris/Desktop/Testable.php)
テスト:TestTwoは成功した 

7

PHPUnitを取得します。とても使いやすいです。

次に、非常に単純なアサーションから始めます。他のことに入る前に、AssertEqualsを使ってたくさんすることができます。それはあなたの足を濡らす良い方法です。

(質問にTDDタグを付けたため)最初にテストを作成してから、コードを作成することもできます。あなたがこれまでにこれを行わなかった場合、それは目を見張るものです。

require_once 'ClassYouWantToTest';
require_once 'PHPUnit...blah,blah,whatever';

class ClassYouWantToTest extends PHPUnit...blah,blah,whatever
{
    private $ClassYouWantToTest;

   protected function setUp ()
    {
        parent::setUp();
        $this->ClassYouWantToTest = new ClassYouWantToTest(/* parameters */);
    }

    protected function tearDown ()
    {
        $this->ClassYouWantToTest = null;
        parent::tearDown();
    }

    public function __construct ()
    {   
        // not really needed
    }

    /**
     * Tests ClassYouWantToTest->methodFoo()
     */
    public function testMethodFoo ()
    {
        $this->assertEquals(
            $this->ClassYouWantToTest->methodFoo('putValueOfParamHere), 'expectedOutputHere);

    /**
     * Tests ClassYouWantToTest->methodBar()
     */
    public function testMethodFoo ()
    {
        $this->assertEquals(
            $this->ClassYouWantToTest->methodBar('putValueOfParamHere), 'expectedOutputHere);
}

5

単純なテストとドキュメンテーションの場合、php-doctestは非常に優れており、別のファイルを開く必要がないため、簡単に始めることができます。以下の関数を想像してみてください:

/**
* Sums 2 numbers
* <code>
* //doctest: add
* echo add(5,2);
* //expects:
* 7
* </code>
*/
function add($a,$b){
    return $a + $b;   
}

このファイルをphpdt(php-doctestのコマンドラインランナー)で実行すると、1つのテストが実行されます。doctestは<code>ブロック内に含まれています。Doctestはpythonで始まり、コードがどのように機能するかについての有用で実行可能な例を提供するのに適しています。コード自体がテストケースに散らばるので、それを独占的に使用することはできませんが、より正式なtddライブラリと一緒に使用すると便利です-私はphpunitを使用しています。

この最初の答えは、ここでうまくまとめられています(ユニット対doctestではありません)。


1
それはソースを少し乱雑にしませんか?
Ali Ghanavatian、2015

できる。単一の単純なテストにのみ使用してください。ドキュメントとしても使用されます。より多くの使用ユニットテストが必要な場合。
ソフィア

2

phpunitは、ほぼphpのデファクトユニットテストフレームワークです。また、そこにあるのDocTest(PEARパッケージとして入手可能)と、いくつか他の人が。php自体は、pearを介して実行できるphptテストを介して、回帰などがテストされます。


2

Codeceptionテストは一般的な単体テストによく似ていますが、モックやスタブが必要な場合には非常に強力です。

これは、コントローラーのサンプルテストです。スタブがいかに簡単に作成されるかに注意してください。メソッドが呼び出されたことを簡単に確認できます。

<?php
use Codeception\Util\Stub as Stub;

const VALID_USER_ID = 1;
const INVALID_USER_ID = 0;

class UserControllerCest {
public $class = 'UserController';


public function show(CodeGuy $I) {
    // prepare environment
    $I->haveFakeClass($controller = Stub::makeEmptyExcept($this->class, 'show'));
    $I->haveFakeClass($db = Stub::make('DbConnector', array('find' => function($id) { return $id == VALID_USER_ID ? new User() : null ))); };
    $I->setProperty($controller, 'db', $db);

    $I->executeTestedMethodOn($controller, VALID_USER_ID)
        ->seeResultEquals(true)
        ->seeMethodInvoked($controller, 'render');

    $I->expect('it will render 404 page for non existent user')
        ->executeTestedMethodOn($controller, INVALID_USER_ID)
        ->seeResultNotEquals(true)
        ->seeMethodInvoked($controller, 'render404','User not found')
        ->seeMethodNotInvoked($controller, 'render');
}
}

他にもクールなものがあります。データベースの状態、ファイルシステムなどをテストできます。


1

すでに与えられているテストフレームワークに関する優れた提案に加えて、SymfonyCakePHPなどの自動テストが組み込まれたPHP Webフレームワークの1つを使用してアプリケーションを構築していますか?場合によっては、テストメソッドを単にドロップする場所があると、一部の人々が自動テストとTDDに関連する起動時の摩擦を減らすことができます。


1

ここに再投稿するには多すぎますが、ここにphptの使用に関する素晴らしい記事があります。多くの場合見過ごされがちなphptに関する多くの側面をカバーしているため、単にテストを作成するだけでなく、phpの知識を広げるのも一読の価値があります。幸い、この記事ではテストの作成についても説明しています。

議論の主なポイント

  1. わずかに文書化されたPHPの側面がどのように機能するかを発見します(またはそのほとんどすべての部分)
  2. 独自のPHPコードの簡単な単体テストを作成する
  3. 拡張機能の一部として、または内部またはQAグループに潜在的なバグを伝えるためにテストを作成する

1

私はここに多くの情報があることを知っていますが、これはまだGoogle検索に表示されるため、Chinook Test Suiteをリストに追加することもできます。シンプルで小さなテストフレームワークです。

これを使用してクラスを簡単にテストし、モックオブジェクトを作成することもできます。テストは、Webブラウザーから(まだ)コンソールから実行します。ブラウザーでは、実行するテストクラスまたは実行するテストメソッドを指定できます。または、単にすべてのテストを実行することもできます。

githubページのスクリーンショット:

チヌークユニットテストフレームワーク

私がそれについて好きなのは、あなたがテストを主張する方法です。これは、いわゆる「流暢な表明」で行われます。例:

$this->Assert($datetime)->Should()->BeAfter($someDatetime);

そして、モックオブジェクトの作成も簡単です(流暢な構文のように):

$mock = new CFMock::Create(new DummyClass());
$mock->ACallTo('SomeMethod')->Returns('some value');

とにかく、より多くの情報がgithubページでコード例とともに見つかります:

https://github.com/w00/Chinook-TestSuite

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.