Discovering the unknown
Brussels php meetup
July 2016
hosted in mvillage / BePark
Discovering the unknown
...
and
get some news
http://slides.com/grummfy/atoum-brussels-php-meetup-july-2016
https://joind.in/talk/55c0bb
name of an Egyptian god
but we will speak about unit test ;)
Now focus on it
.atoum.php
.bootstrap.atoum.php
$ vendor/bin/atoum
...
load file /.../.atoum.php
load file /.../test/.atoum.php
<?php
namespace A\B {
class C {
public function iMReturningABool() {
return false;
}
}
}
namespace A\test\unit\B {
class C extends \atoum {
public function testIMReturningABool() {
$this
->given($this->newTestedInstance())
->assert('Will return false')
->boolean($this->testedInstance->iMReturningABool())
->isFalse;
}
}
}
✓
✓
✓
✓
✓
Now, let see what's available!
=> Storytelling!
test is pleasant, readable, understandable
->isEqualTo(/*...*/);
->isNotEqualTo(/*...*/);
->isIdenticalTo(/*...*/);
->isNotIdenticalTo(/*...*/);
->isGreaterThan(/*...*/);
->isLessThan(/*...*/);
->isGreaterThanOrEqualTo(/*...*/);
->isLessThanOrEqualTo(/*...*/);
->{‘==’}(/*...*/);
->{‘!=’}(/*...*/);
->{‘===‘}(/*...*/);
->{‘!==‘}(/*...*/);
->{‘>’}(/*...*/);
->{‘<‘}(/*...*/);
->{‘>=‘}(/*...*/);
->{‘<=‘}(/*...*/);
Assertion chain the asserter
can be use with or without "()"
some rely on specific asserter
extension give some more
not empty by default!
keep the feature in the mock
mock everything : interface, class, abstract, ...
Several ways to obtain a mock
new \mock\foo\Bar($at, $oum)
$this->newMockInstance('\foo\Bar', null, null, [$at, oum])
...
<?php
class klass {
public function __construct(at $at, oum $oum) {/*...*/}
public function foo($arg) {/*...*/}
}
$mock = new \mock\klass($at, $oum);
// the created mock will look like if we write this
namespace mock {
class klass extends \klass {
public function __construct(at $at, oum $oum) {
parent::__construct($at, $oum);
}
public function foo($arg) {
parent::foo($arg);
}
}
normal mock
<?php
class klass {
public function __construct(at $at, oum $oum) {/*...*/}
public function foo($arg) {/*...*/}
}
$this->mockGenerator->shuntParentClassCalls();
$mock = new \mock\klass($at, $oum);
// the created mock will look like if we write this
namespace mock {
class klass extends \klass {
public function __construct(at $at, oum $oum) {} // no code to parent call
public function foo($arg) {}
}
}
shuntParent mock
<?php
class klass {
public function __construct(at $at, oum $oum) {/*...*/}
public function foo($arg) {/*...*/}
}
$this->mockGenerator->orphanize('__construct'); // can be any method we want empty
$mock = new \mock\klass($at, $oum);
// the created mock will look like if we write this
namespace mock {
class klass extends \klass {
public function __construct() {} // no args and no code
public function foo($arg) {
parent::foo($arg);
}
} }
orphanize mock
Want a PHPUnit mock?
$this->mockGenerator->allIsInterface();
$this->calling($mock)->foo = ‘value’; // or a callable
$this->calling($mock)->foo[3] = 'value on third call'
$this->mock($mock)->call(‘foo’)->once;
->twice
->exactly(3) <=> ->{3}
->atLeast(2)
Read the doc, it will have example and many more
$this->mock($mock)->call(‘foo’)
->withArguments(/*...*/);
->withIdenticalArguments(/*...*/);
->withAtLeastArguments(/*...*/);
->withAtLeastIdenticalArguments(/*...*/);
->withAnyArguments;
->withoutAnyArgument;
->before, ->after
$this->mock($mock)->call(‘foo’)->before(
$this->mock($mock)->call(‘bar’)->after(
$this->mock($mock)->call(‘baz’)->once
)—>once
)->once;
<?php
// ...
$this
->assert('the file exist')
->given($this->newTestedInstance())
->if($this->function->file_exists = true)
->then
->object($this->testedInstance->loadConfigFile())
->isTestedInstance()
->function('file_exists')->wasCalled()->once();
<?php
// ...
public function testFoo(\Foo\MyInterface $fooInterfaceMock) {
$this->mock($fooInterfaceMock);
}
If you need a mock inside your test method...
... but if it required args, use a data provider!
use a method to feed the test
<?php
// ...
/**
* @dataProvider feedFoo
*/
public function testFoo($a, $b) {
$this->integer($a)->isEqualTo($b);
}
public function feedFoo() {
return [42, 42];
}
<?php
// ...
public function testFoo($a, $b) {
$this->integer($a)->isEqualTo($b);
}
// or simply, no annotation with [nameOfTestMethod]DataProvider
public function testFooDataProvider() {
// we can also return an array => 3 elements = 3 test call
return [
[42, 42],
[1, 1],
[1, '1'], // beecause isEquatlTo, not isIdenticalTo
];
}
atoum -d [directory of test]
atoum -f [file with the test]
-ns A\B
-m A\B\C::testFoo
-t myTag
--loop
--debug
...
<?php
// in your workspace/.atoum.php
// loop mode enabled by default for your test \o/
$script->enableLoopMode();
from config file
required xdebug for coverage
<?php
// .atoum.php
$script->addDefaultReport();
$xunitWriter = new atoum\writers\file('atoum.xunit.xml');
$xunitReport = new atoum\reports\asynchronous\xunit(); // real xunit report
$xunitReport->addWriter($xunitWriter);
$runner->addReport($xunitReport);
$cloverWriter = new atoum\writers\file(‘atoum.clover.xml’);
$cloverReport = new atoum\reports\asynchronous\clover();
$cloverReport->addWriter($cloverWriter);
$runner->addReport($cloverReport);
cli realtime & light,
cover, tap, coveralls, sonar, html, nyancat, logo, santa, treemap, ...
<?php
// .atoum.php
use mageekguy\atoum\reports;
use mageekguy\atoum\reports\coverage;
use mageekguy\atoum\writers\std;
$extension = new reports\extension($script);
$extension->addToRunner($runner);
$script->addDefaultReport();
$coverage = new coverage\html();
$coverage->addWriter(new std\out());
$coverage->setOutPutDirectory(__DIR__ . '/test/reports/coverage');
$script->enableBranchAndPathCoverage(); // <- lower your coverage score ;)
$runner->addReport($coverage);
Without path coverage
With path coverage
<?php
// .atoum.php
use mageekguy\atoum\reports;
use mageekguy\atoum\reports\telemetry;
use mageekguy\atoum\writers\std;
$script->addDefaultReport();
$telemetry = new telemetry();
$telemetry->addWriter(new std\out());
$runner->addReport($telemetry);
// $telemetry->readProjectNameFromComposerJson(__DIR__ . '/composer.json');
// $telemetry->setProjectName('my/project');
$telemetry->sendAnonymousProjectName();
composer require --dev atoum/reports-extension
vim
netbeans
phpstorm
atom
sublime text
Symfony 1.x, 2.x (3.x ?)
ezPublish
Zend Framework 2.x
honestly you don't really need it
phing
robo
jenkins / hudson, sonar (standard xUnit)
codacy, travis
...
Start with this workshop on TDD:
<?php
/**
* @requires t : array([to integer()],boundinteger(5,10)) and
* i : integer();
* @ensures \result : boolean();
*/
public function find($t, $i)
{
return false;
}