Zoeken…


Syntaxis

  • Volledige lijst van beweringen . Voorbeelden:
  • assertTrue(bool $condition[, string $messageIfFalse = '']);
  • assertEquals(mixed $expected, mixed $actual[, string $messageIfNotEqual = '']);

Opmerkingen

Unit worden gebruikt voor het testen van de broncode om te zien of deze deals bevat zoals verwacht. Unit worden door de meeste kaders ondersteund. Er zijn verschillende PHPUnit-tests en deze kunnen verschillen in syntaxis. In dit voorbeeld gebruiken we PHPUnit .

Klasse regels testen

Laten we zeggen dat we een eenvoudige LoginForm klasse hebben met de methode rules () (gebruikt op de inlogpagina als framework-sjabloon):

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

Om tests in deze klasse uit te voeren, gebruiken we Unit- tests (controleren van de broncode om te zien of deze aan onze verwachtingen voldoet):

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

Hoe kunnen Unit tests hier precies helpen (met uitzondering van algemene voorbeelden)? Het past bijvoorbeeld heel goed als we onverwachte resultaten krijgen. Laten we bijvoorbeeld deze regel van eerder nemen:

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

In plaats daarvan, als we een belangrijk ding misten en dit schreven:

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

Met tientallen verschillende regels (ervan uitgaande dat we niet alleen e-mail en wachtwoord gebruiken), is het moeilijk om fouten te detecteren. Deze eenheidstest:

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

Zal ons eerste voorbeeld passeren maar niet de tweede . Waarom? Omdat we in het tweede voorbeeld een patroon met een typfout (gemist + teken) hebben geschreven, wat betekent dat het slechts één letter / cijfer accepteert.

Eenheidstests kunnen in console worden uitgevoerd met opdracht: phpunit [path_to_file] . Als alles in orde is, moeten we kunnen zien dat alle tests in de OK status zijn, anders zien we Error (syntaxisfouten) of Fail (ten minste één regel in die methode is niet geslaagd).

Met extra parameters zoals --coverage we ook visueel zien hoeveel regels in backend-code zijn getest en welke zijn geslaagd / mislukt. Dit is van toepassing op elk framework dat PHPUnit heeft geïnstalleerd.

Voorbeeld van hoe de PHPUnit test eruitziet in de console (algemene weergave, niet volgens dit voorbeeld):

voer hier de afbeeldingsbeschrijving in

PHPUnit-gegevensproviders

Testmethoden hebben vaak gegevens nodig om mee te testen. Om sommige methoden volledig te testen, moet u verschillende gegevenssets bieden voor elke mogelijke testconditie. Natuurlijk kunt u dit handmatig doen met behulp van lussen, zoals hier:

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

En iemand kan het handig vinden. Maar er zijn enkele nadelen van deze aanpak. Eerst moet u extra acties uitvoeren om gegevens te extraheren als uw testfunctie verschillende parameters accepteert. Ten tweede, bij mislukking zou het moeilijk zijn om de falende dataset te onderscheiden zonder extra berichten en foutopsporing. Ten derde biedt PHPUnit een automatische manier om testdatasets met gegevensproviders af te handelen.

Gegevensprovider is een functie die gegevens voor uw specifieke testcase moet retourneren.

Een gegevensprovidersmethode moet openbaar zijn en ofwel een array van arrays retourneren of een object dat de Iterator- interface implementeert en een array oplevert voor elke iteratiestap. Voor elke array die deel uitmaakt van de verzameling wordt de testmethode aangeroepen met de inhoud van de array als argumenten.

Als u een gegevensprovider bij uw test wilt gebruiken, gebruikt @dataProvider annotatie @dataProvider met de naam van de opgegeven gegevensproviderfunctie:

/**
* @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 van arrays

Merk op dat dataProviderForTest() een reeks arrays teruggeeft. Elke geneste array heeft twee elementen en deze vullen de benodigde parameters voor testEquals() één voor één op. Fout zoals deze wordt gegenereerd Missing argument 2 for Test::testEquals() als er onvoldoende elementen zijn. PHPUnit doorloopt automatisch gegevens en voert tests uit:

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

Elke gegevensset kan voor het gemak worden benoemd . Het zal gemakkelijker zijn om falende gegevens te detecteren:

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

iterators

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

Zoals u ziet, werkt eenvoudige iterator ook.

Merk op dat zelfs voor een enkele parameter, gegevensprovider een array moet retourneren [$parameter]

Want als we onze current() -methode (die in feite gegevens over elke iteratie retourneert) hiernaar wijzigen:

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

Of wijzig de feitelijke gegevens:

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

We krijgen een foutmelding:

There was 1 warning:

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

Het is natuurlijk niet handig om het Iterator object over een eenvoudige array te gebruiken. Het zou wat specifieke logica voor uw geval moeten implementeren.

generatoren

Het wordt niet expliciet vermeld en in de handleiding getoond, maar u kunt ook een generator als gegevensprovider gebruiken. Merk op dat de Generator klasse de Iterator interface daadwerkelijk implementeert.

Dus hier is een voorbeeld van het gebruik van DirectoryIterator combinatie met 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.
        }
    }
}

Opmerking provider yield een array op. U krijgt in plaats daarvan een waarschuwing voor een ongeldige gegevensaanbieder.

Test uitzonderingen

Stel dat u de methode wilt testen die een uitzondering veroorzaakt

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

U kunt dat doen door de methode-aanroep in een try / catch-blok te plaatsen en beweringen te doen over de eigenschappen van het execption-object, maar handiger kunt u uitzonderingsbevestigingsmethoden gebruiken. Vanaf PHPUnit 5.2 zijn er expectX () -methoden beschikbaar voor het bevestigen van het uitzonderingstype, bericht en code

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

Als u een eerdere versie van PHPUnit gebruikt, kan methode setExpectedException worden gebruikt in plaats van expectX (), maar houd er rekening mee dat deze is verouderd en in versie 6 wordt verwijderd.

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
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow