2015.02.05 alexis von glasow - faster php testing process with atoum

49
5th of February 2015 Faster PHP testing process with atoum? by Alexis von Glasow

Upload: inovia

Post on 07-Aug-2015

253 views

Category:

Software


0 download

TRANSCRIPT

5th of February 2015

Faster PHP testing process with atoum?

by Alexis von Glasow

5th of February 2015

Table of contents

● Why● Install● Atoum in figure● Assertions● Syntax● Features● Config● Reporting● Extensions● Feedback

5th of February 2015

5th of February 2015

INSTALL

5th of February 2015

Install

5th of February 2015

atoum in figure

221

175026168

5th of February 2015

atoum in figure

5th of February 2015

atoum in figure

2039

1329

5th of February 2015

Atoum221 classes tests1750 methods26168 assertions

PHPUnit293 classes tests1329 methods2039 assertions

atoum in figure

5th of February 2015

atoum in figure

5th of February 2015

ASSERTIONS

5th of February 2015

Assertions

datetime

hash

constinteger

stringoutput

object mock

array

5th of February 2015

Assertions

isEqualTo

isGreaterThanmatches

contains

hasLengthisZero

isNotEmpty

5th of February 2015

SYNTAX

5th of February 2015

Syntax

<?php

namespace estvoyage\statsd\tests\units\probe;

require __DIR__ . '/../../runner.php';

use …;

class timer extends atoum\test{ function testValueGoesInto() {

// … }}

5th of February 2015

Syntax

function testValueGoesInto(){ $this ->object($this->testedInstance->bucketIs($bucket)) ->isTestedInstance ->string('a')->isEqualTo('a') ->integer(1)->isEqualTo(1) ->integer(0)->isZero() ->array(array())->isEmpty() ;}

5th of February 2015

Syntax

public function testException(){ $this ->object($this->newTestedInstance)->isInstanceOf('\Skeleton\Skeleton') ->exception(function() { $this->testedInstance->implodeArray(true); }) ->isInstanceOf('\Exception') ->hasMessage('Must be an array') ->hasCode(42) ;}

5th of February 2015

Syntax with sugar

given

and

then

when

if

5th of February 2015

Syntax with sugar

class timer extends atoum\test{ function testValueGoesInto() { $this ->given( $client = new statsd\client, $bucket = new metric\bucket(uniqid()) ) ->if($this->newTestedInstance($client)) ->then ->object($this->testedInstance->bucketIs($bucket)) ->isTestedInstance ; }}

5th of February 2015

Syntax mock

$this ->if($generator = new testedClass()) //… ->and($testClassesDirectoryPath = new \mock\atoum\fs\path('/a/b/c')) ->and($this->calling($testClassesDirectoryPath)->exists = true) ->and($this->calling($testClassesDirectoryPath)->getRealPath = $testClassesDirectoryPath)

->and($testedClassPath = new \mock\atoum\fs\path('/x/y/z/f.php')) ->and($this->calling($testedClassPath)->putContents = $testedClassPath)

->and($testClassPath = new \mock\atoum\fs\path('/a/b/c/d/e/f.php')) ->and($this->calling($testClassPath)->getRealParentDirectoryPath = new \mock\atoum\fs\path('/a/b/c/d/e')) ->and($this->calling($testClassPath)->getRealPath = $testClassPath) ->and($this->calling($testClassPath)->putContents = $testClassPath) //…

5th of February 2015

Syntax mock

//… ->and($this->calling($pathFactory)->build = function($path) use ($testClassesDirectoryPath, $testClassPath, $testedClassPath) { switch ($path) { case (string) $testClassesDirectoryPath . DIRECTORY_SEPARATOR: return $testClassesDirectoryPath; case (string) $testClassPath: return $testClassPath; default: return $testedClassPath; } }) ->then ->object($generator->generate((string) $testClassPath))->isIdenticalTo($generator) ->mock($templateParser) ->call('parseFile') ->withArguments( $generator->getTemplatesDirectory() . DIRECTORY_SEPARATOR . 'testClass.php' )->once();

5th of February 2015

Syntax mock PHP functions

function testValueGoesInto(){ $this ->given( $client = new statsd\client, $bucket = new metric\bucket(uniqid()), $this->function->microtime[1] = $start = 1418733215.0566, $this->function->microtime[2] = $stop = 1418733220.6586 ) ->if( $this->newTestedInstance($client) ) ->then ->object($this->testedInstance->bucketIs($bucket))->isTestedInstance ->mock($client)->call('valueGoesInto') ->withArguments(metric\value::timing(($stop * 10000) - ($start * 10000)), $bucket) ->once ;}

5th of February 2015

ISOLATION

5th of February 2015

LOOP

5th of February 2015

Loop TDD

● Write test● Go to console● Write command or hit ↑● Hit “enter”● See test fail● Write code● Go to console● Hit enter● See test pass

● Write test● Go to console● Write command or hit ↑● Hit “enter”● See test fail● Write code● Go to console● Write command or hit ↑● Hit enter● See test pass

5th of February 2015

Loop

5th of February 2015

CONFIG

5th of February 2015

Config

<?php

use \mageekguy\atoum;

$a = new atoum\reports\asynchronous\xunit;$a->addWriter(new atoum\writers\file('/tmp/nn'));$runner->addReport($a);

$report = $script->addDefaultReport();

$coverageField = new atoum\report\fields\runner\coverage\html('atoum', '/tmp/report');$coverageField->setRootUrl('file:///tmp/report/index.html');$report->addField($coverageField);

5th of February 2015

Config

<?php//TEST GENERATOR SETUP

$testGenerator = new atoum\test\generator();

// Please replace in next line "/path/to/your/tests/units/classes/directory" by your unit test's directory.$testGenerator->setTestClassesDirectory('path/to/your/tests/units/classes/directory');

// Please replace in next line "your\project\namespace\tests\units" by your unit test's namespace.$testGenerator->setTestClassNamespace('your\project\namespace\tests\units');

// Please replace in next line "/path/to/your/classes/directory" by your classes directory.$testGenerator->setTestedClassesDirectory('path/to/your/classes/directory');

// Please replace in next line "your\project\namespace" by your project namespace.$testGenerator->setTestedClassNamespace('your\project\namespace');

// Please replace in next line "path/to/your/tests/units/runner.php" by path to your unit test's runner.$testGenerator->setRunnerPath('path/to/your/tests/units/runner.php');

$script->getRunner()->setTestGenerator($testGenerator);

5th of February 2015

Config bootstrap

require __DIR__ . '/vendor/autoload.php';

// Init project

5th of February 2015

REPORTING

5th of February 2015

5th of February 2015

EXTENSIONS

5th of February 2015

Visibility

class foo{ protected function bar() { return $this; }

protected function baz($arg) { return $arg; }}

5th of February 2015

Visibility

public function testBar(){ $this ->if($sut = new \foo()) ->then ->object($this->invoke($sut)->bar())->isIdenticalTo($sut) ;}

public function testBaz(){ $this ->if($sut = new \foo()) ->and($arg = uniqid()) ->then ->variable($this->invoke($sut)->baz($arg))->isEqualTo($arg) ;}

5th of February 2015

Json-schema

class foo extends atoum\test{ public function testIsJson() { $this ->given($string = '{"foo": "bar"}') ->then ->json($string) ; }

public function testValidatesSchema() { $this ->given($string = '["foo", "bar"]') ->then ->json($string)->validates('{"title": "test", "type": "array"}') ->json($string)->validates('/path/to/json.schema') ; }}

5th of February 2015

Ruler

./vendor/bin/atoum -d tests --filter 'not ("unSuperTagAuNiveauDeLaMethode1" in tags)'

./vendor/bin/atoum -d tests --filter 'method = "testMethod1'

./vendor/bin/atoum -d tests --filter 'method in ["testMethod1"]'

./vendor/bin/atoum -d tests --filter 'class = "mageekguy\atoum\ruler\tests\units\testClass1'

./vendor/bin/atoum -d tests --filter 'namespace = "mageekguy\atoum\ruler\tests\units'

./vendor/bin/atoum -d tests --filter 'testedclass = "mageekguy\atoum\ruler\testClass1'

./vendor/bin/atoum -d tests --filter 'testedclassnamespace = "mageekguy\atoum\ruler'

5th of February 2015

Praspel

$this->sample($this->realdom->boundinteger(7, 13)->or->boundinteger(42, 153));

$data = $this->realdom->regex('/[\w\-_]+(\.[\w\-\_]+)*@\w\.(net|org)/');$this->string($this->sample($data)) ->contains(…)->…;

//Date$data = $this->realdom->date( 'd/m H:i', $this->realdom->boundinteger( $this->realdom->timestamp('yesterday'), $this->realdom->timestamp('next Monday') ));

foreach($this->sampleMany($data, 10) as $date) var_dump($date);

5th of February 2015

BDD

public function should_format_underscore_separated_method_name(){ $this ->given($formatter = new testedClass()) ->then ->invoking->format(__FUNCTION__)->on($formatter) ->shouldReturn('should format underscore separated method name') ;}

public function shouldFormatCamelCaseMethodName(){ $this ->given($formatter = new testedClass()) ->then ->invoking->format(__FUNCTION__)->on($formatter) ->shouldReturn('should format camel case method name') ;}

5th of February 2015

BDD$ vendor/bin/atoum -d specs> jubianchi\example\formatter... ✔ should format underscore separated method name

✘ should format underscore separated method name 256: Tested class 'jubianchi\example\formatter' does not exist for test class 'jubianchi\example\specs\formatter' File: /atoum-bdd-extension/formatter.php

✘ should format mixed method name 256: Tested class 'jubianchi\example\formatter' does not exist for test class 'jubianchi\example\specs\formatter' File: /atoum-bdd-extension/formatter.php

Failure (1 spec, 3/3 examples, 0 void example, 0 skipped example, 0 uncompleted example, 0 failure, 3 errors, 0 exception)!

5th of February 2015

autotoum

autotoum -d -w src,tests/units/subset,tests/units/otherSubset -- -d

tests/units

5th of February 2015

FEEDBACK

5th of February 2015

Feedback

5th of February 2015

Feedback

5th of February 2015

5th of February 2015

Questions ?For online questions, please leave a comment on the article.

5th of February 2015

Praspel

<?php

class Filesystem { const SIZE = 1024; /** * @invariant _usage: boundinteger(0, static::SIZE); */ protected $_usage = 0;

/** * @invariant _map: array([to class('File')], 0..0xffff); */ protected $_map = array(); /** * @ensures \result: this->_usage; */ public function getUsage ( ) { /* ... */ }

/** * @requires file: class('File') and * index: 0..0xffff or void; * @behavior full { * @description 'The filesystem is full.'; * @requires \pred('$this->getUsage() + $file->getSize() * > static::SIZE'); * @ensures \none; * @throwable AllocationException e with * file->isAttached(): false and * e->getFilesystem(): this; * } * @default { * @ensures file->isAttached(): true and * \result: boolean(); * } */ public function store ( File $file, $index = null ) { /* ... */ }}

5th of February 2015

Sources

● https://github.com/atoum/atoum● https://github.com/vonglasow/atoum-skeleton● http://thenounproject.com/

Credits photos: ● Luis Prado from the Noun Project● Darren Barone from the Noun Project● useiconic.com from the Noun Project● iconsmind.com from the Noun Project● SuperAtic LABS from the Noun Project● Julia Stoffer from the Noun Project● CommitStrip.com● Alexander Pretschner

5th of February 2015

Join the community !(in Paris)

Social networks :● Follow us on Twitter : https://twitter.com/steamlearn● Like us on Facebook : https://www.facebook.com/steamlearn

SteamLearn is an Inovia initiative : inovia.fr

You wish to be in the audience ? Join the meetup group! http://www.meetup.com/Steam-Learn/