Richtig testen - thephp.cc · Richtig testen 21.02.2019 | Sebastian Bergmann Die richtige Soware...

Post on 12-Oct-2020

4 views 0 download

Transcript of Richtig testen - thephp.cc · Richtig testen 21.02.2019 | Sebastian Bergmann Die richtige Soware...

Richtig testen

Sebastian Bergmann

21. Februar 2019 | München

 

Sebastian Bergmann

Interessengemeinscha� PHP e.V.

Als gemeinnütziger Verein möchten wir die Akzeptanz, Verbreitung und

Weiterentwicklung der Programmiersprache PHP und das Wissen um die

Programmiersprache PHP in Wissenscha�, Forschung und Ausbildung

�ördern.

https://igphp.de/

@igphp

Richtig testen

21.02.2019 | Sebastian Bergmann

Die richtige So�ware entwickeln. Das Richtige testen. Zum richtigen Zeitpunkt.

Mit dem passenden Werkzeug sowie den richtigen Kni�en, wie man es e�ektiv und e�zient

einsetzt. In diesem Vortrag gehen wir unter anderem den folgenden Fragen nach:

Was will ich testen? In welchem Rahmen muss ich es testen? Wie formuliere ich den Test?

https://thephp.cc/termine/2019/02/tech-and-drinks-at-myposter/richtig-testen

Richtig testen

21.02.2019 | Sebastian Bergmann

Die richtige So�ware entwickeln. Das Richtige testen. Zum richtigen Zeitpunkt.

Mit dem passenden Werkzeug sowie den richtigen Kni�en, wie man es e�ektiv und e�zient

einsetzt. In diesem Vortrag gehen wir unter anderem den folgenden Fragen nach:

Was will ich testen? In welchem Rahmen muss ich es testen? Wie formuliere ich den Test?

https://thephp.cc/termine/2019/02/tech-and-drinks-at-myposter/richtig-testen

Die �ünf W des Testens

Was wollen wir mit dem Test erreichen?

Was wollen wir mit dem Test erreichen?

Wieviel Code müssen wir aus�ühren?

Was wollen wir mit dem Test erreichen?

Wieviel Code müssen wir aus�ühren?

Wie formulieren wir den Test?

Was wollen wir mit dem Test erreichen?

Wieviel Code müssen wir aus�ühren?

Wie formulieren wir den Test?

Wann schreiben wir den Test?

Was wollen wir mit dem Test erreichen?

Wieviel Code müssen wir aus�ühren?

Wie formulieren wir den Test?

Wann schreiben wir den Test?

Wann �ühren wir den Test aus?

Unit Test

Unit Test

Funktionieren die kleinsten Einheiten der So�ware isoliert

voneinander?

Unit Test

Funktionieren die kleinsten Einheiten der So�ware isoliert

voneinander?

Sind die kleinsten Einheiten der So�ware einfach zu testen?

Unit Test

Funktionieren die kleinsten Einheiten der So�ware isoliert

voneinander?

Sind die kleinsten Einheiten der So�ware einfach zu testen?

Sind die kleinsten Einheiten der So�ware einfach zu verwenden?

Unit Test

Funktionieren die kleinsten Einheiten der So�ware isoliert

voneinander?

Sind die kleinsten Einheiten der So�ware einfach zu testen?

Sind die kleinsten Einheiten der So�ware einfach zu verwenden?

Ist die So�ware handwerklich sauber implementiert?

"Sauberer Code kann von anderen Entwicklern gelesen und verbessert

werden. Er ver�ügt über Unit- und Acceptance-Tests. Er enthält

bedeutungsvolle Namen. Er stellt zur Lösung einer Aufgabe nicht

mehrere, sondern eine Lösung zur Ver�ügung. Er enthält minimale

Abhängigkeiten, die ausdrücklich de�niert sind, und stellt ein klares

und minimales API zur Ver�ügung."

— Dave Thomas

"For a class to be easy to unit-test, the class must have explicit

dependencies that can easily be substituted and clear responsibilities

that can easily be invoked and veri�ed. In so�ware-engineering

terms, that means that the code must be loosely coupled and highly

cohesive — in other words, well-designed."

— Steve Freeman und Nat Pryce

Unit Test

public function testIsInitiallyEmpty(): void{ $cart = new ShoppingCart;

$this->assertEmpty($cart->items());}

Unit Test

public function testTotalForEmptyShoppingCartIs0(): void{ $cart = new ShoppingCart;

$this->assertEquals(EUR::fromCents(0), $cart->total());}

Unit Test

public function testItemCanBeAdded(): void{ $item = $this->createMock(ShoppingCartItem::class);

$cart = new ShoppingCart; $cart->addItem($item);

$this->assertCount(1, $cart->items()); $this->assertContains($item, $cart->items());}

Integrationstest

Integrationstest

Funktioniert die Kommunikation zwischen den Komponenten?

Integrationstest

Funktioniert die Kommunikation zwischen den Komponenten?

Funktioniert die Kommunikation mit anderen Systemen?

Integrationstest

Funktioniert die Kommunikation zwischen den Komponenten?

Funktioniert die Kommunikation mit anderen Systemen?

Ist die So�ware richtig integriert?

Akzeptanztest

Akzeptanztest

Er�üllt die So�ware die Akzeptanzkriterien?

Akzeptanztest

Er�üllt die So�ware die Akzeptanzkriterien?

Funktioniert die So�ware als Ganzes?

Akzeptanztest (End-to-End)

public function testShoppingCartPageCanBeDisplayed(): void{ $page = $this->visit('https://my.shop/cart');

$this->assertCount(1, $page->findAll('css', '.cart_item')); $this->assertEquals('Foo', $page->find('css', '.cart_item_name')->getHtml()); $this->assertEquals('1,23 EUR', $page->find('css', '.cart_item_unit_price')->getHtml()); $this->assertEquals('2,46 EUR', $page->find('css', '.cart_item_total_price')->getHtml());}

Akzeptanztest (Edge-to-Edge)

public function testShoppingCartPageCanBeDisplayed(): void{ $_SERVER['REQUEST_METHOD'] = 'GET'; $_SERVER['REQUEST_URI'] = '/cart'; $_GET = [];

$factory = new Factory; $application = $factory->createApplication(); $request = Request::fromSuperGlobals(); $response = $application->run($request); $content = $response->getContent();

$this->assertShoppingCartItemCount(1, $content); $this->assertShoppingCartTotal('1,23 EUR', $content);

// ...}

Akzeptanztest (Edge-to-Edge)

public function testShoppingCartPageCanBeDisplayed(): void{ $this->getRequest('/cart');

$content = $this->run();

$this->assertShoppingCartItemCount(1, $content); $this->assertShoppingCartTotal('1,23 EUR', $content);

// ...}

https://thephp.cc/termine/2017/11/php-ruhr/domain-speci�c-assertions

https://www.youtube.com/watch?v=FC7Sz8gwbuc

Systemtest

Systemtest

Sind die Qualitätsziele erreicht?

"�unning end-to-end tests tells us about the the external quality of

our system, and writing them tells us something about how well we

[. . .] understand the domain, but end-to-end tests don't tell us how

well we've written the code. Writing unit tests gives us a lot of

feedback about the quality of our code, and running them tell us that

we haven't broken any classes [. . .]"

— Steve Freeman und Nat Pryce

Richtig testen

21.02.2019 | Sebastian Bergmann

Die richtige So�ware entwickeln. Das Richtige testen. Zum richtigen Zeitpunkt.

Mit dem passenden Werkzeug sowie den richtigen Kni�en, wie man es e�ektiv und e�zient

einsetzt. In diesem Vortrag gehen wir unter anderem den folgenden Fragen nach:

Was will ich testen? In welchem Rahmen muss ich es testen? Wie formuliere ich den Test?

https://thephp.cc/termine/2019/02/tech-and-drinks-at-myposter/richtig-testen

Richtig testen

21.02.2019 | Sebastian Bergmann

Die richtige So�ware entwickeln. Das Richtige testen. Zum richtigen Zeitpunkt.

Mit dem passenden Werkzeug sowie den richtigen Kni�en, wie man es e�ektiv und e�zient

einsetzt. In diesem Vortrag gehen wir unter anderem den folgenden Fragen nach:

Was will ich testen? In welchem Rahmen muss ich es testen? Wie formuliere ich den Test?

https://thephp.cc/termine/2019/02/tech-and-drinks-at-myposter/richtig-testen

$ phive install --copy phpunit Phive 0.11.0 - Copyright (C) 2015-2019 by Arne Blankerts, Sebastian Heuer and Contributors Copying phpunit-8.0.4.phar to /home/sb/project/tools/phpunit $ ./tools/phpunit --version PHPUnit 8.0.4 by Sebastian Bergmann and contributors.

PHA�? Phive? Warum nicht Composer?

https://twitter.com/s_bergmann/status/999635212723212288

$ tree . ├── phive.xml ├── src │ ├── autoload.php │ ├── BillingAddress.php │ ├── CreditCard.php │ ├── Email.php │ ├── ShippingAddress.php │ ├── ShoppingCartItem.php │ ├── ShoppingCart.php │ └── ShoppingCartRepository.php ├── tests │ ├── acceptance │ │ └── CheckoutProcessTest.php │ ├── integration │ │ └── ShoppingCartRepositoryTest.php │ └── unit │ └── ShoppingCartTest.php └── tools └── phpunit 6 directories, 13 files

$ ./tools/phpunit --generate-configuration PHPUnit 8.0.4 by Sebastian Bergmann and contributors. Generating phpunit.xml in /home/sb/project Bootstrap script (relative to path shown above; default: vendor/autoload.php): src/autoload.php Tests directory (relative to path shown above; default: tests): Source directory (relative to path shown above; default: src): Generated phpunit.xml in /home/sb/project

phpunit.xml

<?xml version="1.0" encoding="UTF-8"?><phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/8.0/phpunit.xsd" bootstrap="src/autoload.php" executionOrder="depends,defects" forceCoversAnnotation="true" beStrictAboutCoversAnnotation="true" beStrictAboutOutputDuringTests="true" beStrictAboutTodoAnnotatedTests="true" verbose="true"> <testsuites> <testsuite name="default"> <directory suffix="Test.php">tests</directory> </testsuite> </testsuites>

<filter> <whitelist processUncoveredFilesFromWhitelist="true"> <directory suffix=".php">src</directory> </whitelist> </filter></phpunit>

phpunit.xml

<?xml version="1.0" encoding="UTF-8"?><phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/8.0/phpunit.xsd" bootstrap="src/autoload.php" executionOrder="depends,defects" forceCoversAnnotation="true" beStrictAboutCoversAnnotation="true" beStrictAboutOutputDuringTests="true" beStrictAboutTodoAnnotatedTests="true" verbose="true"> <testsuites> <testsuite name="default"> <directory suffix="Test.php">tests</directory> </testsuite> </testsuites>

<filter> <whitelist processUncoveredFilesFromWhitelist="true"> <directory suffix=".php">src</directory> </whitelist> </filter></phpunit>

phpunit.xml

<?xml version="1.0" encoding="UTF-8"?><phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/8.0/phpunit.xsd" bootstrap="src/autoload.php" executionOrder="depends,defects" forceCoversAnnotation="true" beStrictAboutCoversAnnotation="true" beStrictAboutOutputDuringTests="true" beStrictAboutTodoAnnotatedTests="true" verbose="true"> <testsuites> <testsuite name="default"> <directory suffix="Test.php">tests</directory> </testsuite> </testsuites>

<filter> <whitelist processUncoveredFilesFromWhitelist="true"> <directory suffix=".php">src</directory> </whitelist> </filter></phpunit>

phpunit.xml

<?xml version="1.0" encoding="UTF-8"?><phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/8.0/phpunit.xsd" bootstrap="src/autoload.php" executionOrder="depends,defects" forceCoversAnnotation="true" beStrictAboutCoversAnnotation="true" beStrictAboutOutputDuringTests="true" beStrictAboutTodoAnnotatedTests="true" verbose="true"> <testsuites> <testsuite name="default"> <directory suffix="Test.php">tests</directory> </testsuite> </testsuites>

<filter> <whitelist processUncoveredFilesFromWhitelist="true"> <directory suffix=".php">src</directory> </whitelist> </filter></phpunit>

phpunit.xml

<?xml version="1.0" encoding="UTF-8"?><phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/8.0/phpunit.xsd" bootstrap="src/autoload.php" executionOrder="depends,defects" forceCoversAnnotation="true" beStrictAboutCoversAnnotation="true" beStrictAboutOutputDuringTests="true" beStrictAboutTodoAnnotatedTests="true" verbose="true"> <testsuites> <testsuite name="default"> <directory suffix="Test.php">tests</directory> </testsuite> </testsuites>

<filter> <whitelist processUncoveredFilesFromWhitelist="true"> <directory suffix=".php">src</directory> </whitelist> </filter></phpunit>

phpunit.xml

<?xml version="1.0" encoding="UTF-8"?><phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/8.0/phpunit.xsd" bootstrap="src/autoload.php" executionOrder="depends,defects" forceCoversAnnotation="true" beStrictAboutCoversAnnotation="true" beStrictAboutOutputDuringTests="true" beStrictAboutTodoAnnotatedTests="true" verbose="true"> <testsuites> <testsuite name="default"> <directory suffix="Test.php">tests</directory> </testsuite> </testsuites>

<filter> <whitelist processUncoveredFilesFromWhitelist="true"> <directory suffix=".php">src</directory> </whitelist> </filter></phpunit>

phpunit.xml

<?xml version="1.0" encoding="UTF-8"?><phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/8.0/phpunit.xsd" bootstrap="src/autoload.php" executionOrder="depends,defects" forceCoversAnnotation="true" beStrictAboutCoversAnnotation="true" beStrictAboutOutputDuringTests="true" beStrictAboutTodoAnnotatedTests="true" verbose="true"> <testsuites> <testsuite name="default"> <directory suffix="Test.php">tests</directory> </testsuite> </testsuites>

<filter> <whitelist processUncoveredFilesFromWhitelist="true"> <directory suffix=".php">src</directory> </whitelist> </filter></phpunit>

phpunit.xml

<?xml version="1.0" encoding="UTF-8"?><phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/8.0/phpunit.xsd" bootstrap="src/autoload.php" executionOrder="depends,defects" forceCoversAnnotation="true" beStrictAboutCoversAnnotation="true" beStrictAboutOutputDuringTests="true" beStrictAboutTodoAnnotatedTests="true" verbose="true"> <testsuites> <testsuite name="unit"> <directory suffix="Test.php">tests/unit</directory> </testsuite> <testsuite name="integration"> <directory suffix="Test.php">tests/integration</directory> </testsuite> <testsuite name="acceptance"> <directory suffix="Test.php">tests/acceptance</directory> </testsuite> </testsuites>

<filter> <whitelist processUncoveredFilesFromWhitelist="true"> <directory suffix=".php">src</directory> </whitelist> </filter></phpunit>

$ ./tools/phpunit PHPUnit 8.0.4 by Sebastian Bergmann and contributors. Runtime: PHP 7.3.2 with Xdebug 2.7.0RC2 Configuration: /home/sb/project/phpunit.xml ........... 11 / 11 (100%) Time: 83 ms, Memory: 12.00MB OK (11 tests, 11 assertions)

$ ./tools/phpunit --testsuite unit PHPUnit 8.0.4 by Sebastian Bergmann and contributors. Runtime: PHP 7.3.2 with Xdebug 2.7.0RC2 Configuration: /home/sb/project/phpunit.xml ... 3 / 3 (100%) Time: 69 ms, Memory: 10.00MB OK (3 tests, 3 assertions)

$ ./tools/phpunit --testdox PHPUnit 8.0.4 by Sebastian Bergmann and contributors. Runtime: PHP 7.3.2 with Xdebug 2.7.0RC2 Configuration: /home/sb/project/phpunit.xml ShoppingCart ✔ Is initially empty [0.15 ms] ✔ Total for empty shopping cart is 0 [0.16 ms] ✔ Item can be added [0.15 ms] ShoppingCartRepository ✔ Can save shopping cart [0.17 ms] ✔ Can find shopping cart by id [0.15 ms] CheckoutProcess ✔ Item can be added to shopping cart [0.32 ms] ✔ Shopping cart page can be displayed [0.16 ms] ✔ Shipping address can be entered [0.16 ms] ✔ Billing address can be entered [0.16 ms] ✔ Payment information can be entered [0.16 ms] ✔ Purchase can be completed [0.16 ms] Time: 83 ms, Memory: 12.00MB OK (11 tests, 11 assertions)

<?php declare(strict_types=1);

use PHPUnit\Framework\TestCase;

final class RouterTest extends TestCase{ /** * @dataProvider routesProvider */ public function testRoutesRequest(string $url, string $handler): void { // ... }

public function routesProvider(): array { return [ '/foo/bar' => [ '/foo/bar', FooBarHandler::class, // ... ] ]; }}

<?php declare(strict_types=1);

use PHPUnit\Framework\TestCase;

final class RouterTest extends TestCase{ /** * @dataProvider routesProvider */ public function testRoutesRequest(string $url, string $handler): void { // ... }

public function routesProvider(): array { return [ '/foo/bar' => [ '/foo/bar', FooBarHandler::class, // ... ] ]; }}

Router ✔ Routes request data set "/foo/bar"

<?php declare(strict_types=1);

use PHPUnit\Framework\TestCase;

final class RouterTest extends TestCase{ /** * @dataProvider routesProvider * @testdox Routes $url to $handler */ public function testRoutesRequest(string $url, string $handler): void { // ... }

public function routesProvider() { return [ '/foo/bar' => [ '/foo/bar', FooBarHandler::class, // ... ] ]; }}

Router ✔ Routes /foo/bar to FooBarHandler

https://thephp.cc/neuigkeiten/2019/01/faster-code-coverage

https://blog.krakjoe.ninja/2019/01/running-for-coverage.html

Code Coverage Performance

  Code Coverage Time Memory

PHP 7.3.2   22 seconds 81 MB

PHP 7.3.2 + Xdebug 2.7.0�C2   3 minutes 81 MB

PHP 7.3.2 + Xdebug 2.7.0�C2 ✓ 16 minutes 97 MB

PHP 7.3.2 + Xdebug 2.7.0�C2 with native �ltering ✓ 15 minutes 95 MB

PHPDBG 7.3.2 ✓ 3 minutes 7.5 GB

PHP 7.3.2 + PCOV 1.0.0 ✓ 1.5 minutes 3.8 GB

Lies, damn lies, and benchmarks

$ git clone https://github.com/sebastianbergmann/diff.git $ cd diff $ composer install $ ./vendor/bin/phpunit

Kontakt

https://thephp.cc

https://talks.thephp.cc

sebastian@thephp.cc

@s_bergmann