Getting Started with PHPUnit

Download

We distribute a PHP Archive (PHAR) that contains everything you need in order to use PHPUnit. Simply download it from here, make it executable, and put it into your $PATH, for instance:

➜ wget https://phar.phpunit.de/phpunit.phar

➜ chmod +x phpunit.phar

➜ sudo mv phpunit.phar /usr/local/bin/phpunit

➜ phpunit --version
PHPUnit 6.4.0 by Sebastian Bergmann and contributors.

You can also immediately use the PHAR after you have downloaded it, of course:

➜ wget https://phar.phpunit.de/phpunit.phar

➜ php phpunit.phar --version
PHPUnit 6.4.0 by Sebastian Bergmann and contributors

Please refer to the documentation for details on how to verify PHAR releases of PHPUnit or how to install PHPUnit using Composer.

Code

src/Email.php

<?php
declare(strict_types=1);

final class Email
{
    private $email;

    private function __construct(string $email)
    {
        $this->ensureIsValidEmail($email);

        $this->email = $email;
    }

    public static function fromString(string $email): self
    {
        return new self($email);
    }

    public function __toString(): string
    {
        return $this->email;
    }

    private function ensureIsValidEmail(string $email): void
    {
        if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
            throw new InvalidArgumentException(
                sprintf(
                    '"%s" is not a valid email address',
                    $email
                )
            );
        }
    }
}

Test Code

tests/EmailTest.php

<?php
declare(strict_types=1);

use PHPUnit\Framework\TestCase;

/**
 * @covers Email
 */
final class EmailTest extends TestCase
{
    public function testCanBeCreatedFromValidEmailAddress(): void
    {
        $this->assertInstanceOf(
            Email::class,
            Email::fromString('user@example.com')
        );
    }

    public function testCannotBeCreatedFromInvalidEmailAddress(): void
    {
        $this->expectException(InvalidArgumentException::class);

        Email::fromString('invalid');
    }

    public function testCanBeUsedAsString(): void
    {
        $this->assertEquals(
            'user@example.com',
            Email::fromString('user@example.com')
        );
    }
}



Code

src/Email.php

<?php
declare(strict_types=1);

final class Email
{
    private $email;

    private function __construct(string $email)
    {
        $this->ensureIsValidEmail($email);

        $this->email = $email;
    }

    public static function fromString(string $email): self
    {
        return new self($email);
    }

    public function __toString(): string
    {
        return $this->email;
    }

    private function ensureIsValidEmail(string $email)
    {
        if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
            throw new InvalidArgumentException(
                sprintf(
                    '"%s" is not a valid email address',
                    $email
                )
            );
        }
    }
}

Test Code

tests/EmailTest.php

<?php
declare(strict_types=1);

use PHPUnit\Framework\TestCase;

/**
 * @covers Email
 */
final class EmailTest extends TestCase
{
    public function testCanBeCreatedFromValidEmailAddress()
    {
        $this->assertInstanceOf(
            Email::class,
            Email::fromString('user@example.com')
        );
    }

    public function testCannotBeCreatedFromInvalidEmailAddress()
    {
        $this->expectException(InvalidArgumentException::class);

        Email::fromString('invalid');
    }

    public function testCanBeUsedAsString()
    {
        $this->assertEquals(
            'user@example.com',
            Email::fromString('user@example.com')
        );
    }
}



Code

src/Email.php

<?php


final class Email
{
    private $email;

    private function __construct($email)
    {
        $this->ensureIsValidEmail($email);

        $this->email = $email;
    }

    public static function fromString($email)
    {
        return new self($email);
    }

    public function __toString()
    {
        return $this->email;
    }

    private function ensureIsValidEmail($email)
    {
        if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
            throw new InvalidArgumentException(
                sprintf(
                    '"%s" is not a valid email address',
                    $email
                )
            );
        }
    }
}

Test Code

tests/EmailTest.php

<?php


use PHPUnit\Framework\TestCase;

/**
 * @covers Email
 */
final class EmailTest extends TestCase
{
    public function testCanBeCreatedFromValidEmailAddress()
    {
        $this->assertInstanceOf(
            Email::class,
            Email::fromString('user@example.com')
        );
    }

    public function testCannotBeCreatedFromInvalidEmailAddress()
    {
        $this->expectException(InvalidArgumentException::class);

        Email::fromString('invalid');
    }

    public function testCanBeUsedAsString()
    {
        $this->assertEquals(
            'user@example.com',
            Email::fromString('user@example.com')
        );
    }
}



If you would like to understand the differences between the examples shown above then the "PHP 7 Explained eBook might help you.

If you want to use the example code (for PHP 5.6) with PHP 5.6 then you need to download https://phar.phpunit.de/phpunit-5.7.phar instead of https://phar.phpunit.de/phpunit.phar.

Test Execution

➜ phpunit --bootstrap src/Email.php tests/EmailTest
PHPUnit 6.4.0 by Sebastian Bergmann and contributors.

...                                                                 3 / 3 (100%)

Time: 70 ms, Memory: 10.00MB

OK (3 tests, 3 assertions)

Lets have a look at what the three parts of above's invokation mean:

  • phpunit invokes the PHPUnit command-line test runner. We assume that you have downloaded phpunit.phar (see above) and put it into your $PATH as phpunit.

  • --bootstrap src/Email.php instructs the PHPUnit command-line test runner to include src/Email.php before the test execution.

    Usually, though, you use --bootstrap to instruct the PHPUnit command-line test runner to include bootstrap script that sets up autoloading for the classes that are to be tested. Such a script is commonly generated using tools such as Composer or phpab.

  • tests/EmailTest instructs the PHPUnit command-line test runner to execute the tests of the EmailTest class that is declared in tests/EmailTest.php.

    Using tests instead of tests/EmailTest would instruct the PHPUnit command-line test runner to execute all tests found declared in *Test.php sourcecode files in the tests directory.

Below you see an alternative output of the test result that is enabled using the --testdox option:

➜ phpunit --bootstrap src/Email.php --testdox tests
PHPUnit 6.4.0 by Sebastian Bergmann and contributors.

Email
 [x] Can be created from valid email address
 [x] Cannot be created from invalid email address
 [x] Can be used as string

This output is based on the idea that the name of a test can be used to document the behavior that is verified by the test.

Conclusion

You have learned how to write simple unit tests as well as how to download and run PHPUnit. For more details please have a look at the documentation.