Wie schreibe ich Unit-Tests in PHP? [geschlossen]

97

Ich habe überall gelesen, wie großartig sie sind, aber aus irgendeinem Grund kann ich nicht herausfinden, wie genau ich etwas testen soll. Könnte jemand vielleicht einen Beispielcode posten und wie er ihn testen würde? Wenn es nicht zu viel Mühe gibt :)

Lass los
quelle
5
Aus Gründen des Gleichgewichts gibt es keine 2 oder 3 Unit-Testing-Frameworks für PHP - hier finden Sie eine Liste: en.wikipedia.org/wiki/List_of_unit_testing_frameworks#PHP
Fenton

Antworten:

36

Es gibt ein drittes "Framework", das weitaus einfacher zu erlernen ist - sogar einfacher als Simple Test, es heißt phpt.

Eine Grundierung finden Sie hier: http://qa.php.net/write-test.php

Bearbeiten: Ich habe gerade Ihre Anfrage nach Beispielcode gesehen.

Nehmen wir an, Sie haben die folgende Funktion in einer Datei namens lib.php :

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

Der Parameter, den Sie übergeben, ist sehr einfach und unkompliziert und wird zurückgegeben. Schauen wir uns also einen Test für diese Funktion an. Wir rufen die Testdatei foo.phpt auf :

--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"

Kurz gesagt, wir geben dem Parameter den $barWert "Hello World"und var_dump()die Antwort des Funktionsaufrufs an foo().

Verwenden Sie zum Ausführen dieses Tests: pear run-test path/to/foo.phpt

Dies erfordert eine funktionierende Installation von PEAR auf Ihrem System, was in den meisten Fällen häufig vorkommt. Wenn Sie es installieren müssen, empfehle ich, die neueste verfügbare Version zu installieren. Wenn Sie Hilfe beim Einrichten benötigen, wenden Sie sich bitte an uns (geben Sie jedoch das Betriebssystem usw. an).

Bis
quelle
Sollte es nicht sein run-tests?
Dharman
30

Es gibt zwei Frameworks, die Sie für Unit-Tests verwenden können. Simpletest und PHPUnit , die ich bevorzuge. Lesen Sie die Tutorials zum Schreiben und Ausführen von Tests auf der Homepage von PHPUnit. Es ist ganz einfach und gut beschrieben.

Okoman
quelle
21

Sie können Unit-Tests effektiver gestalten, indem Sie Ihren Codierungsstil ändern, um ihn zu berücksichtigen.

Ich empfehle, im Google Testing Blog zu stöbern , insbesondere im Beitrag zum Schreiben von testbarem Code .

Preston
quelle
7
Ich denke, Sie haben einen großartigen Beitrag erwähnt. Das Starten Ihrer Antwort mit "Unit-Tests sind nicht sehr effektiv" hat mich beinahe dazu veranlasst, als Test-Adept abzustimmen ... Möglicherweise würde eine positive Umformulierung die Leute dazu ermutigen, den Artikel zu lesen.
xtofl
2
@xtofl bearbeitet es, um die "Positivität" leicht zu erhöhen :)
icc97
13

Ich habe meine eigenen gerollt, weil ich keine Zeit hatte, jemanden zu lernen, wie man andere Dinge macht. Das Schreiben dauerte ungefähr 20 Minuten, 10 Minuten, um es für die Veröffentlichung hier anzupassen.

Unittesting ist für mich sehr nützlich.

Das ist ein bisschen lang, aber es erklärt sich von selbst und es gibt ein Beispiel unten.

/**
 * 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();

Dies gibt aus:

Test: TestOne war ein Fehler 
/ **
* Dieser Test schlägt fehl
** / (Zeilen: 149-152; Datei: /Users/kris/Desktop/Testable.php)
Test: TestTwo war ein Erfolg 
Kris
quelle
7

Holen Sie sich PHPUnit. Es ist sehr einfach zu bedienen.

Beginnen Sie dann mit sehr einfachen Aussagen. Sie können viel mit AssertEquals tun, bevor Sie sich auf etwas anderes einlassen. Das ist ein guter Weg, um Ihre Füße nass zu machen.

Möglicherweise möchten Sie auch zuerst versuchen, Ihren Test zu schreiben (da Sie Ihrer Frage das TDD-Tag gegeben haben) und dann Ihren Code schreiben. Wenn Sie dies noch nicht getan haben, ist es ein Augenöffner.

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);
}
Teilbestellung
quelle
5

Für einfache Tests UND Dokumentation ist php-doctest sehr nett und ein sehr einfacher Einstieg, da Sie keine separate Datei öffnen müssen. Stellen Sie sich die folgende Funktion vor:

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

Wenn Sie diese Datei jetzt über phpdt (Befehlszeilen-Runner von php-doctest) ausführen, wird 1 Test ausgeführt. Der Doctest ist im <code> -Block enthalten. Doctest stammt ursprünglich aus Python und ist gut geeignet, um nützliche und ausführbare Beispiele dafür zu geben, wie der Code funktionieren soll. Sie können es nicht ausschließlich verwenden, da der Code selbst mit Testfällen übersät ist, aber ich habe festgestellt, dass es neben einer formaleren tdd-Bibliothek nützlich ist - ich verwende phpunit.

Diese erste Antwort hier fasst es gut zusammen (es ist nicht Einheit gegen Doktest).

Sofia
quelle
1
macht es die Quelle nicht ein wenig unübersichtlich?
Ali Ghanavatian
es kann. Es sollte nur für einzelne einfache Tests verwendet werden. dient auch als Dokumentation. Wenn Sie mehr benötigen, verwenden Sie den Unit-Test.
Sofia
2

phpunit ist so ziemlich das defacto Unit Testing Framework für PHP. Es gibt auch DocTest (als PEAR-Paket erhältlich) und einige andere. PHP selbst wird über PHpt-Tests, die auch über Birne ausgeführt werden können, auf Regressionen und dergleichen getestet .

kguest
quelle
2

Codeception-Tests ähneln gängigen Unit-Tests, sind jedoch in Dingen, in denen Sie sich lustig machen und stubben müssen, sehr leistungsfähig.

Hier ist der Beispiel-Controller-Test. Beachten Sie, wie einfach Stubs erstellt werden. Wie einfach Sie überprüfen, wurde die Methode aufgerufen.

<?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');
}
}

Es gibt auch andere coole Dinge. Sie können den Datenbankstatus, das Dateisystem usw. testen.

Davert
quelle
1

Erstellen Sie Ihre Anwendung neben den bereits gegebenen hervorragenden Vorschlägen zu Test-Frameworks mit einem der PHP-Web-Frameworks, in die automatisierte Tests integriert sind, z. B. Symfony oder CakePHP ? Manchmal verringert ein Platz, an dem Sie einfach Ihre Testmethoden ablegen können, die Startreibung, die manche Leute mit automatisierten Tests und TDD verbinden.

Bradheintz
quelle
1

Viel zu viel, um es hier erneut zu veröffentlichen, aber hier ist ein großartiger Artikel über die Verwendung von phpt . Es behandelt eine Reihe von Aspekten rund um PHP , die oft übersehen werden. Es kann sich also lohnen, diese zu lesen, um Ihr Wissen über PHP über das Schreiben eines Tests hinaus zu erweitern. Glücklicherweise behandelt der Artikel auch das Schreiben von Tests!

Die Hauptdiskussionspunkte

  1. Entdecken Sie, wie marginal dokumentierte Aspekte von PHP funktionieren (oder so ziemlich jeder Teil in dieser Angelegenheit).
  2. Schreiben Sie einfache Unit-Tests für Ihren eigenen PHP-Code
  3. Schreiben Sie Tests als Teil einer Erweiterung oder um den Interna oder QS-Gruppen einen potenziellen Fehler mitzuteilen
Quickshiftin
quelle
1

Ich weiß, dass es hier bereits viele Informationen gibt, aber da diese immer noch in der Google-Suche angezeigt werden, kann ich die Chinook Test Suite genauso gut zur Liste hinzufügen . Es ist ein einfaches und kleines Test-Framework.

Sie können damit problemlos Ihre Klassen testen und auch Scheinobjekte erstellen. Sie führen die Tests über einen Webbrowser und (noch nicht) über eine Konsole aus. Im Browser können Sie angeben, welche Testklasse oder sogar welche Testmethode ausgeführt werden soll. Oder Sie können einfach alle Tests ausführen.

Ein Screenshot von der Github-Seite:

Chinook Unit Test Framework

Was ich daran mag, ist die Art und Weise, wie Sie Tests behaupten. Dies geschieht mit sogenannten "fließenden Behauptungen". Beispiel:

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

Und das Erstellen von Scheinobjekten ist ebenfalls ein Kinderspiel (mit einer fließenden Syntax):

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

Weitere Informationen finden Sie auf der Github-Seite mit einem Codebeispiel:

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

Vivendi
quelle