A follow step by step guide introducing automated testing via PHPUnit
Find a file
2018-07-15 11:10:08 +02:00
Classes TASK: Last changes for first talk 2018-07-15 11:10:08 +02:00
Resources/Private/CodeExamples/Tests/Unit TASK: Last changes for first talk 2018-07-15 11:10:08 +02:00
.gitignore TASK: Last changes for first talk 2018-07-15 11:10:08 +02:00
composer.json TASK: Last changes for first talk 2018-07-15 11:10:08 +02:00
readme.rst TASK: Last changes for first talk 2018-07-15 11:10:08 +02:00

Testing Talk

This is about automated testing, for PHP.

Everyone is testing already, by hand. This involves:

  • Unit testing
  • Functional testing
  • Acceptance testing
  • Browser testing

All of the above is already done by you, so … NO PANIC!

This is "The Hitchhiker's Guide to […]" testing.

Table of contents:

local

Start

Clean everything:

rm -rf composer.lock vendor web Tests phpunit.xml.dist infection.json.dist

Install dependencies using composer:

composer install --no-dev

Installation development dependencies using composer:

composer install

This also includes composer, see: composer.json

Why 6.x? We use 6.x to support PHP 7.0.

Check installation:

./vendor/bin/phpunit --version

./vendor/bin/phpunit Tests/Unit/

Links:

Create first test

Hands on! Let's write a first basic test.

E.g. a small model with a bit logic:

mkdir -p Tests/Unit/Domain/Model
cp Resources/Private/CodeExamples/Tests/Unit/Domain/Model/AddressTest.php \
   Tests/Unit/Domain/Model/AddressTest.php

Execute the first test:

./vendor/bin/phpunit Tests/Unit/

Execute with colors:

./vendor/bin/phpunit --color Tests/Unit/

Execute with info about executed tests:

./vendor/bin/phpunit --color --debug Tests/Unit/

What's in the test?

  1. We have one PHP class AddressTest.
  2. Two public methods.
  3. The methods are annotated with @test.
  4. Methods create an instance of the class to test.
  5. Methods call an assert*() method.

Create test for controller

Introduction to mocking

What is mocking, or a mock?

In object-oriented programming, mock objects are simulated objects that mimic the behavior of real objects in controlled ways.

A programmer typically creates a mock object to test the behavior of some other object, in much the same way that a car designer uses a crash test dummy to simulate the dynamic behavior of a human in vehicle impacts.

https://en.wikipedia.org/wiki/Mock_object

Example mock:

<?php

use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use TYPO3\CMS\Extbase\Mvc\View\ViewInterface;

class Test extends TestCase
{
    public function someTest()
    {
        $viewMock = $this->getMockBuilder(ViewInterface::class)->getMock();

        $viewMock->expects($this->once())
            ->method('assign')
            ->with('frontendUser', $frontendUserMock);
    }
}

?>

Add the test

We want to test the controller now:

mkdir -p Tests/Unit/Controller
cp Resources/Private/CodeExamples/Tests/Unit/Controller/FrontendUserControllerTest.php \
   Tests/Unit/Controller

Execute all tests:

./vendor/bin/phpunit --color --debug Tests/Unit/

Alternative output

testdox

Used as "agile" output:

./vendor/bin/phpunit Tests/Unit/ --color --testdox-html Results/testdox.html
xdg-open Results/testdox.html
xml

Used in CI to parse results:

./vendor/bin/phpunit --log-junit Results/junit.xml Tests/Unit
html Coverage

Used to check which methods still need testing:

./vendor/bin/phpunit --coverage-html Results/Coverage/ --whitelist Classes Tests/Unit
xdg-open Results/Coverage/index.html

Benefits of tests

  1. Detect new bugs.
  2. Make sure the same bug does not occur a 2nd time.
  3. Reproduce bug.
  4. Speed up development.
  5. Show how to use the written code.
  6. Allow co-worker, in pull request, to see what you expect. And how you understood the feature-request.
  7. Write code without working system, by using tests instead.
  8. Allow more secure refactoring.

Automate test execution

Existing tests are great. If they are executed.

Tests which exist are code, if they are not executed, they are dead code.

Tests costs money, so get the money back by executing tests.

The easiest way is to have an CI (=Continuous Integration).

E.g.:

  • Jenkins
  • Travis
  • Gitlab CI
  • Bitbucket Pipelines
  • Bamboo
  • Circle CI

See: https://awesomelists.top/#/repos/ciandcd/awesome-ciandcd

Use `phpunit.xml.dist`:

cp Resources/Private/Configs/phpunit.xml.dist phpunit.xml.dist

./vendor/bin/phpunit

Metrics

Code Coverage

Most of the time counts only number of executed lines.

This helps to find untested code, nothing more! 100% covered lines does not mean you are testing all circumstances, just every line at least once.

E.g.:

<?php

    if ($var1 || $var2) {
        echo 'test';
    }

?>

Will have 100% if all lines are executed, that is even if we do not provide $var2. We have to test the possible cases, not only all lines.

Crap

Is not:

https://img.devrant.com/devrant/rant/r_1046201_T68wf.jpg

https://devrant.com/search?term=code+reviews

Is: change risk anti pattern score

Combines complexity and test coverage.

Different kinds of tests

Unit Tests

What we did above. White box test of small pieces of code.

Functional Tests

Involves multiple code parts, database, file system and further components, e.g. web server.

Acceptance Tests

Tests from user view, e.g. via browser.

Mutation testing

Tests how easy it is to break test:

cp Resources/Private/Configs/infection.json.dist infection.json.dist

./vendor/bin/infection

Summary

Start writing tests, small unit tests.

Automate execution of tests.

Improve.

Further reading