Sök…


Syntax

  • Komplett lista med påståenden . Exempel:
  • assertTrue(bool $condition[, string $messageIfFalse = '']);
  • assertEquals(mixed $expected, mixed $actual[, string $messageIfNotEqual = '']);

Anmärkningar

Unit används för att testa källkoden för att se om den innehåller behandlar ingångar som vi förväntar oss. Unit stöds av de flesta ramar. Det finns flera olika PHPUnit-test och de kan skilja sig åt i syntax. I det här exemplet använder vi PHPUnit .

Testa klassregler

Låt oss säga, vi har en enkel LoginForm klass med metod för regler () (används på inloggningssidan som rammall):

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;
    }
}

För att utföra tester i den här klassen använder vi enhetstester (kontrollera källkoden för att se om det passar våra förväntningar):

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;
    }
}

Hur exakt Unit tester kan hjälpa till med (exklusive allmänna exempel) i här? Det passar till exempel mycket bra när vi får oväntade resultat. Låt oss till exempel ta denna regel från tidigare:

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

Om vi istället missade en viktig sak och skrev detta:

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

Med dussintals olika regler (förutsatt att vi inte bara använder e-post och lösenord) är det svårt att upptäcka misstag. Denna enhetstest:

// 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");

Kommer att passera vårt första exempel men inte andra . Varför? Eftersom vi i det andra exemplet skrev ett mönster med en skrivfel (missat + tecken), vilket betyder att det bara accepterar en bokstav / siffra.

Enhetstester kan köras i konsolen med kommando: phpunit [path_to_file] . Om allt är OK bör vi kunna se att alla tester är i OK läge, annars ser vi antingen Error (syntaxfel) eller Fail (minst en rad i den metoden passerade inte).

Med ytterligare parametrar som - --coverage vi också se visuellt hur många rader i backendkoden som testades och vilka som passerade / misslyckades. Detta gäller alla ramverk som har installerat PHPUnit .

Exempel på hur PHPUnit testet ser ut i konsolen (generellt utseende, inte enligt detta exempel):

ange bildbeskrivning här

PHPUnit Data Providers

Testmetoder behöver ofta data testas med. För att testa vissa metoder fullständigt måste du ange olika datamängder för alla möjliga testvillkor. Naturligtvis kan du göra det manuellt med slingor, så här:

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

Och någon kan hitta det bekvämt. Men det finns vissa nackdelar med denna strategi. Först måste du utföra ytterligare åtgärder för att extrahera data om din testfunktion accepterar flera parametrar. För det andra skulle det vid svårigheter vara svårt att skilja den felaktiga datauppsättningen utan ytterligare meddelanden och felsökning. För det tredje tillhandahåller PHPUnit ett automatiskt sätt att hantera testdatauppsättningar med dataleverantörer .

Dataföretag är en funktion som ska returnera data för ditt specifika testfall.

En dataleverantörsmetod måste vara offentlig och antingen returnera en matris med matriser eller ett objekt som implementerar Iterator- gränssnittet och ger en matris för varje iterationssteg. För varje matris som ingår i samlingen kommer testmetoden att kallas med innehållet i matrisen som dess argument.

Om du @dataProvider använda en dataleverantör med ditt test använder du @dataProvider kommentar med namnet på dataförsörjarfunktionen angiven:

/**
* @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 av matriser

Observera att dataProviderForTest() returnerar array av matriser. Varje kapslad matris har två element och de kommer att fylla nödvändiga parametrar för testEquals() en efter en. Fel som detta kommer att kastas Missing argument 2 for Test::testEquals() om det inte finns tillräckligt med element. PHPUnit kommer automatiskt att gå igenom data och köra test:

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)
    ];
}

Varje datauppsättning kan namnges av bekvämlighet. Det kommer att vara lättare att upptäcka felaktiga data:

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)
    ];
}

iteratorer

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]
        ]);
    }
}

Som du ser fungerar enkel iterator också.

Observera att även för en enda parameter måste dataleverantör returnera en matris [$parameter]

För om vi ändrar vår current() -metod (som faktiskt returnerar data för varje iteration) till detta:

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

Eller ändra faktiska data:

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

Vi får ett fel:

There was 1 warning:

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

Naturligtvis är det inte användbart att använda Iterator objekt över en enkel matris. Det borde implementera viss specifik logik för ditt fall.

generatorer

Det noteras inte uttryckligen och visas i manualen, men du kan också använda en generator som dataleverantör. Observera att Generator klassen faktiskt implementerar Iterator gränssnittet.

Så här är ett exempel på att använda DirectoryIterator kombination med 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.
        }
    }
}

Noteringsleverantör yield en matris. Du får istället en varning för ogiltig dataförsörjare.

Testundantag

Låt oss säga att du vill testa metoden som kastar ett undantag

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

Du kan göra det genom att lägga in metodsamtalet i ett try / catch-block och göra påståenden om execptionobjektets egenskaper, men mer bekvämt kan du använda metoder för undantagspåståenden. Från PHPUnit 5.2 har du förväntade X-metoder () för att hävda undantagstyp, meddelande och kod

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();
    }
}

Om du använder tidigare version av PHPUnit, kan metoden setExpectedException användas i stället för metoderna förväntas (), men kom ihåg att den är uttagen och kommer att tas bort i version 6.

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
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow