Suche…


Syntax

  • Vollständige Liste der Zusicherungen . Beispiele:
  • assertTrue(bool $condition[, string $messageIfFalse = '']);
  • assertEquals(mixed $expected, mixed $actual[, string $messageIfNotEqual = '']);

Bemerkungen

Unit werden zum Testen des Quellcodes verwendet, um zu sehen, ob er erwartungsgemäß Eingaben mit Eingaben enthält. Unit Tests werden von der Mehrheit der Frameworks unterstützt. Es gibt verschiedene PHPUnit-Tests , die sich in der Syntax unterscheiden können. In diesem Beispiel verwenden wir PHPUnit .

Klassenregeln testen

Nehmen wir an, wir haben eine einfache LoginForm Klasse mit der rules () -Methode (in der Anmeldeseite als Framework-Vorlage verwendet):

class LoginForm {
    public $email;
    public $rememberMe;
    public $password;

    /* rules() method returns an array with what each field has as a requirement.
     * Login form uses email and password to authenticate user.
     */
    public function rules() {
        return [
            // Email and Password are both required
            [['email', 'password'], 'required'],

            // Email must be in email format
            ['email', 'email'],

            // rememberMe must be a boolean value
            ['rememberMe', 'boolean'],

            // Password must match this pattern (must contain only letters and numbers)
            ['password', 'match', 'pattern' => '/^[a-z0-9]+$/i'],
        ];
    }

    /** the validate function checks for correctness of the passed rules */
    public function validate($rule) {
        $success = true;
        list($var, $type) = $rule;
        foreach ((array) $var as $var) {
            switch ($type) {
                case "required":
                    $success = $success && $this->$var != "";
                    break;
                case "email":
                    $success = $success && filter_var($this->$var, FILTER_VALIDATE_EMAIL);
                    break;
                case "boolean":
                    $success = $success && filter_var($this->$var, FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE) !== null;
                    break;
                case "match":
                    $success = $success && preg_match($rule["pattern"], $this->$var);
                    break;
                default:
                    throw new \InvalidArgumentException("Invalid filter type passed")
            }
        }
        return $success;
    }
}

Um Tests für diese Klasse durchzuführen, verwenden wir Unit- Tests (Überprüfen des Quellcodes, ob er unseren Erwartungen entspricht):

class LoginFormTest extends TestCase {
    protected $loginForm;

    // Executing code on the start of the test
    public function setUp() {
        $this->loginForm = new LoginForm;
    }

    // To validate our rules, we should use the validate() method

    /**
     * This method belongs to Unit test class LoginFormTest and
     * it's testing rules that are described above.
     */
    public function testRuleValidation() {
        $rules = $this->loginForm->rules();

        // Initialize to valid and test this
        $this->loginForm->email = "[email protected]";
        $this->loginForm->password = "password";
        $this->loginForm->rememberMe = true;
        $this->assertTrue($this->loginForm->validate($rules), "Should be valid as nothing is invalid");

        // Test email validation
        // Since we made email to be in email format, it cannot be empty
        $this->loginForm->email = '';
        $this->assertFalse($this->loginForm->validate($rules), "Email should not be valid (empty)");

        // It does not contain "@" in string so it's invalid
        $this->loginForm->email = 'invalid.email.com';
        $this->assertFalse($this->loginForm->validate($rules), "Email should not be valid (invalid format)");

        // Revert email to valid for next test
        $this->loginForm->email = '[email protected]';

        // Test password validation
        // Password cannot be empty (since it's required)
        $this->loginForm->password = '';
        $this->assertFalse($this->loginForm->validate($rules), "Password should not be valid (empty)");

        // Revert password to valid for next test
        $this->loginForm->password = 'ThisIsMyPassword';

        // Test rememberMe validation
        $this->loginForm->rememberMe = 999;
        $this->assertFalse($this->loginForm->validate($rules), "RememberMe should not be valid (integer type)");

        // Revert remeberMe to valid for next test
        $this->loginForm->rememberMe = true;
    }
}

Wie genau können Unit Tests hier helfen (ausgenommen allgemeine Beispiele)? Zum Beispiel passt es sehr gut, wenn wir unerwartete Ergebnisse erhalten. Nehmen wir zum Beispiel diese Regel von früher:

['password', 'match', 'pattern' => '/^[a-z0-9]+$/i'],

Wenn wir jedoch eine wichtige Sache verpasst haben und dies geschrieben haben:

['password', 'match', 'pattern' => '/^[a-z0-9]$/i'],

Bei Dutzenden verschiedener Regeln (vorausgesetzt, wir verwenden nicht nur E-Mail und Passwort), ist es schwierig, Fehler zu erkennen. Dieser Gerätetest:

// Initialize to valid and test this
$this->loginForm->email = "[email protected]";
$this->loginForm->password = "password";
$this->loginForm->rememberMe = true;
$this->assertTrue($this->loginForm->validate($rules), "Should be valid as nothing is invalid");

Wird unser erstes Beispiel übergeben, aber nicht das zweite . Warum? Im zweiten Beispiel haben wir ein Muster mit einem Tippfehler geschrieben (fehlendes Zeichen + ), dh es akzeptiert nur einen Buchstaben / eine Zahl.

: Unit - Tests können mit dem Befehl in der Konsole ausgeführt werden phpunit [path_to_file] . Wenn alles in Ordnung ist, sollten wir in der Lage sein zu sehen, dass alle Tests im OK Zustand sind. Andernfalls werden entweder Error (Syntaxfehler) oder Fail (mindestens eine Zeile in dieser Methode wurde nicht bestanden).

Mit zusätzlichen Parametern wie --coverage wir auch visuell sehen, wie viele Zeilen im Backend-Code getestet wurden und welche bestanden / nicht bestanden haben. Dies gilt für alle Frameworks, auf denen PHPUnit installiert ist.

Beispiel, wie PHPUnit test in Console aussieht (allgemeines Aussehen, nicht in diesem Beispiel):

Geben Sie hier die Bildbeschreibung ein

PHPUnit-Datenprovider

Bei Testmethoden müssen Daten häufig getestet werden. Um einige Methoden vollständig zu testen, müssen Sie für jede mögliche Testbedingung unterschiedliche Datensätze bereitstellen. Natürlich können Sie dies auch manuell mit Schleifen tun:

...
public function testSomething()
{
    $data = [...];
    foreach($data as $dataSet) {
       $this->assertSomething($dataSet);
    }
}
... 

Und jemand kann es bequem finden. Es gibt jedoch einige Nachteile dieses Ansatzes. Zunächst müssen Sie zusätzliche Aktionen zum Extrahieren von Daten durchführen, wenn Ihre Testfunktion mehrere Parameter akzeptiert. Zweitens wäre es bei einem Ausfall schwierig, den fehlerhaften Datensatz ohne zusätzliche Meldungen und Debugging zu unterscheiden. Drittens bietet PHPUnit eine automatische Methode für den Umgang mit Testdatensätzen mithilfe von Datenprovidern .

Datenanbieter ist eine Funktion, die Daten für Ihren speziellen Testfall zurückgeben soll.

Eine Datenanbietermethode muss public sein und entweder ein Array von Arrays oder ein Objekt zurückgeben, das die Iterator- Schnittstelle implementiert und für jeden Iterationsschritt ein Array liefert . Für jedes Array, das Teil der Auflistung ist, wird die Testmethode mit dem Inhalt des Arrays als Argumente aufgerufen.

Um einen Datenprovider für Ihren Test zu verwenden, verwenden Sie die @dataProvider Annotation mit dem Namen der angegebenen Datenproviderfunktion:

/**
* @dataProvider dataProviderForTest
*/
public function testEquals($a, $b)
{
    $this->assertEquals($a, $b);
}

public function dataProviderForTest()
{
    return [
        [1,1],
        [2,2],
        [3,2] //this will fail
    ];
}

Array von Arrays

Beachten Sie, dass dataProviderForTest() Array von Arrays zurückgibt. Jedes verschachtelte Array hat zwei Elemente und füllt die erforderlichen Parameter für testEquals() nacheinander aus. Ein Fehler wie dieser wird Missing argument 2 for Test::testEquals() wenn nicht genügend Elemente vorhanden sind. PHPUnit durchläuft automatisch Daten und führt Tests durch:

public function dataProviderForTest()
{
    return [
        [1,1], // [0] testEquals($a = 1, $b = 1)
        [2,2], // [1] testEquals($a = 2, $b = 2)
        [3,2]  // [2] There was 1 failure: 1) Test::testEquals with data set #2 (3, 4)
    ];
}

Jeder Datensatz kann der Einfachheit halber benannt werden. Fehler werden leichter erkannt:

public function dataProviderForTest()
{
    return [
        'Test 1' => [1,1], // [0] testEquals($a = 1, $b = 1)
        'Test 2' => [2,2], // [1] testEquals($a = 2, $b = 2)
        'Test 3' => [3,2]  // [2] There was 1 failure: 
                           //     1) Test::testEquals with data set "Test 3" (3, 4)
    ];
}

Iteratoren

class MyIterator implements Iterator {
    protected $array = [];

    public function __construct($array) {
        $this->array = $array;
    }

    function rewind() {
        return reset($this->array);
    }

    function current() {
        return current($this->array);
    }

    function key() {
        return key($this->array);
    }

    function next() {
        return next($this->array);
    }

    function valid() {
        return key($this->array) !== null;
    }
}
...

class Test extends TestCase
{
    /**
     * @dataProvider dataProviderForTest
     */
    public function testEquals($a)
    {
        $toCompare = 0;

        $this->assertEquals($a, $toCompare);
    }

    public function dataProviderForTest()
    {
        return new MyIterator([
            'Test 1' => [0],
            'Test 2' => [false],
            'Test 3' => [null]
        ]);
    }
}

Wie Sie sehen, funktioniert der einfache Iterator auch.

Beachten Sie, dass der Datenanbieter auch für einen einzelnen Parameter ein Array [$parameter]

Denn wenn wir unsere current() -Methode ändern (die tatsächlich bei jeder Iteration Daten zurückgibt):

function current() {
    return current($this->array)[0];
}

Oder aktuelle Daten ändern:

return new MyIterator([
            'Test 1' => 0,
            'Test 2' => false,
            'Test 3' => null
        ]);

Wir erhalten einen Fehler:

There was 1 warning:

1) Warning
The data provider specified for Test::testEquals is invalid.

Natürlich ist es nicht sinnvoll, das Iterator Objekt über ein einfaches Array zu verwenden. Es sollte eine bestimmte Logik für Ihren Fall implementieren.

Generatoren

Es ist nicht explizit vermerkt und wird im Handbuch gezeigt, Sie können aber auch einen Generator als Datenprovider verwenden. Beachten Sie, dass die Generator die Iterator Schnittstelle tatsächlich implementiert.

Hier ist ein Beispiel für die Verwendung von DirectoryIterator Kombination mit generator :

/**
 * @param string $file
 *
 * @dataProvider fileDataProvider
 */
public function testSomethingWithFiles($fileName)
{
    //$fileName is available here
    
    //do test here
}

public function fileDataProvider()
{
    $directory = new DirectoryIterator('path-to-the-directory');

    foreach ($directory as $file) {
        if ($file->isFile() && $file->isReadable()) {
            yield [$file->getPathname()]; // invoke generator here.
        }
    }
}

Note Provider yield s ein Array. Stattdessen wird eine Warnung zu einem ungültigen Datenanbieter angezeigt.

Testen Sie Ausnahmen

Angenommen, Sie möchten die Methode testen, die eine Ausnahme auslöst

class Car
{
    /**
     * @throws \Exception
     */
    public function drive()
    {
        throw new \Exception('Useful message', 1);
    }
}

Sie können dies tun, indem Sie den Methodenaufruf in einen try / catch-Block einschließen und Assertions für die Eigenschaften des Ausführungsobjekts vornehmen. Praktischerweise können Sie jedoch auch Exception-Assertions-Methoden verwenden. Seit PHPUnit 5.2 stehen ExpectX () - Methoden zur Verfügung, um den Ausnahmetyp, die Nachricht und den Code geltend zu machen

class DriveTest extends PHPUnit_Framework_TestCase
{
    public function testDrive()
    {
        // prepare
        $car = new \Car();
        $expectedClass = \Exception::class;
        $expectedMessage = 'Useful message';
        $expectedCode = 1;

        // test
        $this->expectException($expectedClass);
        $this->expectMessage($expectedMessage);
        $this->expectCode($expectedCode);

        // invoke
        $car->drive();
    }
}

Wenn Sie eine frühere Version von PHPUnit verwenden, kann die Methode setExpectedException anstelle von expectX () - Methoden verwendet werden. Beachten Sie jedoch, dass sie nicht mehr unterstützt wird und in Version 6 entfernt wird.

class DriveTest extends PHPUnit_Framework_TestCase
{
    public function testDrive()
    {
        // prepare
        $car = new \Car();
        $expectedClass = \Exception::class;
        $expectedMessage = 'Useful message';
        $expectedCode = 1;

        // test
        $this->setExpectedException($expectedClass, $expectedMessage, $expectedCode);

        // invoke
        $car->drive();
    }
}


Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow