
setUp
/tearDown
, beforeEach
/afterEach
Support methods for tests lifecycles to prepare repetitive sets of data and to verify and cleanup after the test.
Double has no behaviour, it only extends or implements something
A stub is an object double, which doesn't have any expectations about the object behavior, but when put in specific environment, behaves in specific way.
Similar to Stub, but constains simple logic to decide what to return.
Doubles having a expectation (e.g. a method will be called)
Track method calls for latter examination.
public function __construct($repositoryPath) { $this->repositoryPath = realpath($repositoryPath); } public function checkout($revision) { $this->execute('git checkout --force --quiet ' . $revision . ' 2>&1'); } public function getCurrentBranch() { $output = $this->execute('git symbolic-ref HEAD'); $tmp = explode('/', $output[0]); return $tmp[2]; } /* more commands ... */
--strict
switch and @depends
upgradesclass VWTest extends PHPUnit_Framework_TestCase { private $emissions = 12000; private $legalLimit = 300; public function testEnvironmentalImpactCompliance() { $this->assertLessThan($this->legalLimit, $this->emissions); } }
VW makes failing test cases succeed in continuous integration tools.
Your primary objective is to ship more code to the world. No need to be slowed down by regressions or new bugs that happen during development.
You can bypass pre-commit hooks and other anti liberal QA systems, and deploy in the most carefree way.
Check @new_POPE's slides from last year: webelement.sk/event/webelement-30
$I->wantTo('create a new user by API'); $I->amHttpAuthenticated('davert','123456'); $I->haveHttpHeader('Content-Type','application/x-www-form-urlencoded'); $I->sendPOST('/users', array('name' => 'davert' )); $I->seeResponseCodeIs(200); $I->seeResponseIsJson(); $I->seeResponseContainsJson(array('result' => 'ok'));
describe-it
syntax (know rspec?).describe('TodoRepository', function () { beforeEach(function () { $interface = 'Doctrine\Common\Persistence\ObjectManager'; // lets assume we are using the peridot-prophecy-plugin $this->em = $this->getProphet()->prophesize($interface); $this->repository = new TodoRepository($this->em->reveal()); }); afterEach(function () { $this->getProphet()->checkPredictions(); }); context('when calling ->get()', function () { it('should find the todo', function () { $this->repository->get(1); $this->em->find('Todos\Todo', 1)->shouldBeCalled(); }); }); });
Feature: Some terse yet descriptive text of what is desired In order to realize a named business value As an explicit system actor I want to gain some beneficial outcome which furthers the goal Scenario: Some determinable business situation Given some precondition And some other precondition When some action by the actor And some other action And yet another action Then some testable outcome is achieved And something else we can check happens too Scenario: A different situation ...
*.feature
file conventionally consists of a single “feature”.
Feature:
followed by its title and three indented lines defines the start of a new feature.
Scenario:
keyword followed by a short description of the scenario. Under each scenario is a list of steps, which must start with one of the following keywords: Given
, When
, Then
, But
or And
. Behat treats each of these keywords the same, but you should use them as intended for consistent scenarios./** @Given /^I am in a directory "([^"]*)"$/ */ public function iAmInADirectory($dir) { if (!file_exists($dir)) { mkdir($dir); } chdir($dir); }
class UserTest extends PHPUnit_Framework_TestCase { private $prophet; public function testPasswordHashing() { $hasher = $this->prophet->prophesize('App\Security\Hasher'); $user = new App\Entity\User($hasher->reveal()); $hasher->generateHash($user, 'qwerty')->willReturn('hashed_pass'); $user->setPassword('qwerty'); $this->assertEquals('hashed_pass', $user->getPassword()); } protected function setup() { $this->prophet = new \Prophecy\Prophet; } protected function tearDown() { $this->prophet->checkPredictions(); } }
ReturnPromise
or ->willReturn(1)
- returns a value from a method callReturnArgumentPromise
or ->willReturnArgument($index)
- returns the nth method argument from callThrowPromise
or ->willThrow
- causes the method to throw specific exceptionCallbackPromise
or ->will($callback)
- gives you a quick way to define your own custom logic
CallPrediction
or shouldBeCalled()
- checks that the method has been called 1 or more timesNoCallsPrediction
or shouldNotBeCalled()
- checks that the method has not been calledCallTimesPrediction
or shouldBeCalledTimes($count)
- checks that the method has been called $count timesCallbackPrediction
or should($callback)
- checks the method against your own custom callbackfopen()
, fread()
, file_get_contents()
, ... without any modification\VCR\VCR\turnOn();
in your tests/bootstrap.php\VCR\VCR::turnOn();
in your tests/bootstrap.php"If you need to ask what you'd use a virtual file system for, you probably don't need one, but just in case, I've compiled a small list of examples:"
// Create and mount the file system $fs = FileSystem::factory('vfs://'); $fs->mount(); // Add `/foo` and `/foo/bar.txt` $foo = new Directory(['bar.txt' => new File('Hello, World!')]); $fs->get('/')->add('foo', $foo); // Get contents of `/foo/bar.txt` $fs->get('/foo/bar.txt')->getContent(); // Hello, World! file_get_contents('vfs://foo/bar.txt'); // Hello, World!
Factories for Base, Lorem Ipsum Text, Person, Address, Phone Number, Company, Real Text, Date and Time, Internet, User Agent, Payment, Color, File, Image, Uuid, Barcode, Miscellaneous, Biased
$faker = Faker\Factory::create(); // generate data by accessing properties echo $faker->name; // 'Lucy Cechtelar'; echo $faker->address; // "426 Jordy Lodge // Cartwrightshire, SC 88120-6700" echo $faker->text; // Dolores sit sint laboriosam dolorem culpa et autem. Beatae // nam sunt fugit et sit et mollitia sed.
Nelmio\Entity\User: user{1..10}: username: <username()> fullname: <firstName()> <lastName()> birthDate: <date()> email: <email()> favoriteNumber: 50%? <numberBetween(1, 200)> Nelmio\Entity\Group: group1: name: Admins owner: @user1 members: <numberBetween(1, 10)>x @user* created: <dateTimeBetween('-200 days', 'now')> updated: <dateTimeBetween($created, 'now')>
$objects = \Nelmio\Alice\Fixtures::load(__DIR__.'/fixtures.yml', $objectManager);
They are undeniably convenient, but our experience has been that programmers use them as a crutch to avoid thinking about proper error handling and reporting.
Proper error reporting means that errors are direct and to the point, saving the programmer from interpreting a large crash trace. Precise errors are particularly important when the programmer seeing the errors is not familiar with the code.
Proper error handling means letting other tests run after one has failed, so that the person debugging the failure gets a complete picture of what is wrong. It is more useful for a test to report that isPrime gives the wrong answer for 2, 3, 5, and 7 (or for 2, 4, 8, and 16) than to report that isPrime gives the wrong answer for 2 and therefore no more tests were run. The programmer who triggers the test failure may not be familiar with the code that fails. Time invested writing a good error message now pays off later when the test breaks.
If nothing, go through http://phpmd.org/rules/index.html to read about good practices.
IdentityRestController.php:35 Avoid unused parameters such as '$request'. IdentityRestController.php:35 Avoid unused parameters such as '$paramFetcher'. IdentityRestController.php:71 Avoid variables with short names like $id. Configured minimum length is 3. IdentityRestController.php:116 Avoid variables with short names like $id. Configured minimum length is 3. IdentityRestController.php:123 The method putIdentityAction uses an else expression. Else is never necessary and you can simplify the code to work without else. IdentityRestController.php:150 Avoid variables with short names like $id. Configured minimum length is 3.
slides available @ testing.rootpd.com