phpunitRozpoczęcie pracy z phpunit


Uwagi

Ta sekcja zawiera przegląd tego, czym jest phpunit i dlaczego deweloper może chcieć go użyć.

Powinien również wymieniać wszelkie duże tematy w phpunit i linkować do powiązanych tematów. Ponieważ Dokumentacja dla phpunit jest nowa, może być konieczne utworzenie początkowych wersji tych pokrewnych tematów.

Wersje

Wersja Wsparcie kończy się Obsługiwane w tych wersjach PHP Data wydania
5.4 2016-08-05 PHP 5.6, PHP 7. 2016-06-03
4.8 2017-02-03 PHP 5.3, PHP 5.4, PHP 5.5 i PHP 5.6. 07.08.2015

Utwórz pierwszy test PHPUnit dla naszej klasy

Wyobraź sobie, że mamy klasę Math.php z logiką obliczania liczb fiobanacciego i silni. Coś takiego:

<?php    
class Math {
    public function fibonacci($n) {
        if (is_int($n) && $n > 0) {
            $elements = array();
            $elements[1] = 1;
            $elements[2] = 1;
            for ($i = 3; $i <= $n; $i++) {
                $elements[$i] = bcadd($elements[$i-1], $elements[$i-2]);
            }
            return $elements[$n];
        } else {
            throw new 
                InvalidArgumentException('You should pass integer greater than 0');
        }
    }

    public function factorial($n) {
        if (is_int($n) && $n >= 0) {
            $factorial = 1;
            for ($i = 2; $i <= $n; $i++) {
                $factorial *= $i;
            }
            return $factorial;
        } else {
            throw new 
                InvalidArgumentException('You should pass non-negative integer');
        }
    }
}
 

Najprostszy test

Chcemy przetestować logikę metod fibonacci i factorial . MathTest.php plik MathTest.php w tym samym katalogu, w Math.php znajduje się plik Math.php . W naszym kodzie możemy używać różnych twierdzeń . Najprostszy kod będzie podobny do tego (używamy tylko assertEquals i assertTrue ):

<?php
require 'Math.php';

use PHPUNIT_Framework_TestCase as TestCase;
// sometimes it can be
// use PHPUnit\Framework\TestCase as TestCase;

class MathTest extends TestCase{
    public function testFibonacci() {
        $math = new Math();
        $this->assertEquals(34, $math->fibonacci(9));
    }

    public function testFactorial() {
        $math = new Math();
        $this->assertEquals(120, $math->factorial(5));
    }

    public function testFactorialGreaterThanFibonacci() {
        $math = new Math();
        $this->assertTrue($math->factorial(6) > $math->fibonacci(6));
    }
}
 

Możemy uruchomić ten test z konsoli za pomocą polecenia phpunit MathTest a wynikiem będzie:

    PHPUnit 5.3.2 by Sebastian Bergmann and contributors.

...                                                                 3 / 3 (100%)

Time: 88 ms, Memory: 10.50Mb

OK (3 tests, 3 assertions)
 

Korzystanie z dataProviders

Metoda testowa może akceptować dowolne argumenty. Argumenty te należy podać metodą dostawcy danych . Metodę dostawcy danych, która ma zostać użyta, określa się za pomocą adnotacji @dataProvider . :

<?php
require 'Math.php';

use PHPUNIT_Framework_TestCase as TestCase;
// sometimes it can be
// use PHPUnit\Framework\TestCase as TestCase;

class MathTest extends TestCase {
    /**
     * test with data from dataProvider
     * @dataProvider providerFibonacci
     */
    public function testFibonacciWithDataProvider($n, $result) {
        $math = new Math();
        $this->assertEquals($result, $math->fibonacci($n));
    }

    public function providerFibonacci() {
        return array(
            array(1, 1),
            array(2, 1),
            array(3, 2),
            array(4, 3),
            array(5, 5),
            array(6, 8),
        );
    }
}
 

Możemy uruchomić ten test z konsoli za pomocą polecenia phpunit MathTest a wynikiem będzie:

    PHPUnit 5.3.2 by Sebastian Bergmann and contributors.

......                                                              6 / 6 (100%)

Time: 97 ms, Memory: 10.50Mb

OK (6 tests, 6 assertions)


<?php
require 'Math.php';
use PHPUNIT_Framework_TestCase as TestCase;
// sometimes it can be
// use PHPUnit\Framework\TestCase as TestCase;
 

Testuj wyjątki

Możemy przetestować, czy wyjątek jest expectException() przez testowany kod za pomocą metody expectException() . Również w tym przykładzie dodajemy jeden test zakończony niepowodzeniem, aby wyświetlić dane wyjściowe konsoli dla testów zakończonych niepowodzeniem.

<?php
require 'Math.php';
use PHPUNIT_Framework_TestCase as TestCase;
// sometimes it can be
// use PHPUnit\Framework\TestCase as TestCase;

class MathTest extends TestCase {
    public function testExceptionsForNegativeNumbers() {
        $this->expectException(InvalidArgumentException::class);
        $math = new Math();
            $math->fibonacci(-1);
    }

    public function testFailedForZero() {
        $this->expectException(InvalidArgumentException::class);
        $math = new Math();
        $math->factorial(0);
    }
}
 

Możemy uruchomić ten test z konsoli za pomocą polecenia phpunit MathTest a wynikiem będzie:

        PHPUnit 5.3.2 by Sebastian Bergmann and contributors.

.F                                                                  2 / 2 (100%)

Time: 114 ms, Memory: 10.50Mb

There was 1 failure:

1) MathTest::testFailedForZero
Failed asserting that exception of type "InvalidArgumentException" is thrown.

FAILURES!
Tests: 2, Assertions: 2, Failures: 1.
 

SetUp and TearDown

PHPUnit obsługuje także udostępnianie kodu instalacyjnego. Przed uruchomieniem metody testowej wywoływana jest metoda szablonowa o nazwie setUp (). setUp() to miejsce, w którym tworzysz obiekty, na których będziesz testować. Po zakończeniu działania metody testowej, niezależnie od tego, czy się powiedzie, czy nie, wywoływana jest inna metoda szablonowa o nazwie tearDown() . tearDown() to miejsce, w którym tearDown() obiekty, na których testowałeś.

<?php
require 'Math.php';

use PHPUNIT_Framework_TestCase as TestCase;
// sometimes it can be
// use PHPUnit\Framework\TestCase as TestCase;

class MathTest extends TestCase {
    public $fixtures;
    protected function setUp() {
        $this->fixtures = [];
    }

    protected function tearDown() {
        $this->fixtures = NULL;
    }

    public function testEmpty() {
        $this->assertTrue($this->fixtures == []);
    }
}
 

Więcej informacji

PHPUnit znacznie więcej możliwości, które możesz wykorzystać w teście. Więcej informacji znajduje się w oficjalnej instrukcji

Przykład PHPUNIT z APItest przy użyciu Stub And Mock

Klasa, dla której utworzysz test jednostkowy. class Authorization {

/* Observer so that mock object can work. */    
 public function attach(Curl $observer)
{
    $this->observers = $observer;
}

/* Method for which we will create test */
public  function postAuthorization($url, $method) {
    
    return $this->observers->callAPI($url, $method);
}
 

}

Teraz nie chcieliśmy żadnej zewnętrznej interakcji naszego kodu testowego, dlatego musimy utworzyć próbny obiekt dla funkcji callAPI, ponieważ ta funkcja faktycznie wywołuje zewnętrzny adres URL za pomocą curl. class AuthorizationTest extends PHPUnit_Framework_TestCase {

protected $Authorization;

/**
 * This method call every time before any method call.
 */
protected function setUp() {
    $this->Authorization = new Authorization();
}

/**
 * Test Login with invalid user credential
 */
function testFailedLogin() {

    /*creating mock object of Curl class which is having callAPI function*/
    $observer = $this->getMockBuilder('Curl')
                     ->setMethods(array('callAPI'))
                     ->getMock();

    /* setting the result to call API. Thus by default whenver call api is called via this function it will return invalid user message*/
    $observer->method('callAPI')
            ->will($this->returnValue('"Invalid user credentials"'));

    /* attach the observer/mock object so that our return value is used */
    $this->Authorization->attach($observer);

    /* finally making an assertion*/
    $this->assertEquals('"Invalid user credentials"',           $this->Authorization->postAuthorization('/authorizations', 'POST'));
}
 

}

Poniżej znajduje się kod klasy curl (tylko przykładowa) class Curl{ function callAPI($url, $method){

    //sending curl req
}
 

}

Minimalny przykładowy test

Biorąc pod uwagę prostą klasę PHP:

class Car
{
    private $speed = 0;

    public getSpeed() {
        return $this->speed;
    }

    public function accelerate($howMuch) {
        $this->speed += $howMuch;
    }
}
 

Możesz napisać test PHPUnit, który testuje zachowanie testowanej klasy, wywołując metody publiczne i sprawdzając, czy działają zgodnie z oczekiwaniami:

class CarTest extends PHPUnit_Framework_TestCase
{
    public function testThatInitalSpeedIsZero() {
        $car = new Car();
        $this->assertSame(0, $car->getSpeed());
    }

    public function testThatItAccelerates() {
        $car = new Car();
        $car->accelerate(20);
        $this->assertSame(20, $car->getSpeed());
    }

    public function testThatSpeedSumsUp() {
        $car = new Car();
        $car->accelerate(30);
        $car->accelerate(50);
        $this->assertSame(80, $car->getSpeed());
    }
}
 

Ważne części:

  1. Klasa testowa musi pochodzić z PHPUnit_Framework_TestCase.
  2. Każda nazwa funkcji testowej powinna zaczynać się od przedrostka „test”
  3. Użyj funkcji $this->assert... aby sprawdzić oczekiwane wartości.

Kpiące klasy

Praktyka zastępowania obiektu podwójnym testem weryfikującym oczekiwania, na przykład stwierdzenie, że metoda została wywołana, jest nazywana kpiną.

Załóżmy, że mamy SomeService do przetestowania.

class SomeService
{
    private $repository;
    public function __construct(Repository $repository)
    {
        $this->repository = $repository;
    }

    public function methodToTest()
    {
        $this->repository->save('somedata');
    }
}   
 

I chcemy przetestować, czy methodToTest naprawdę wywołuje metodę save repozytorium. Ale nie chcemy tak naprawdę tworzyć repozytorium (a może Repository to tylko interfejs).

W takim przypadku możemy kpić z Repository .

use PHPUnit\Framework\TestCase as TestCase;

class SomeServiceTest extends TestCase
{    
    /**
     * @test
     */
    public function testItShouldCallRepositorySavemethod()
    {
        // create an actual mock
        $repositoryMock = $this->createMock(Repository::class);

        $repositoryMock->expects($this->once()) // test if method is called only once
             ->method('save')                   // and method name is 'save'
             ->with('somedata');                // and it is called with 'somedata' as a parameter

        $service = new SomeService($repositoryMock);
        $service->someMethod();
    }
}