php-vcr behat case study
TRANSCRIPT
Setup
Big Symfony2 project for large Swiss company
Multiple attached APIs
Tests in both PHPUnit and Behat/PhantomJS
Gitlab CI and vagrant for development
History
Testing with manually created fixtures
Mocks had to be adjusted when APIs changed
Using Guzzle's mock plugin
Symfony's profiler helped out quite a lot...
Mocking:
• Do API call you would like to mock
• Copy/paste response to JSON/XML file
• Adjust to test cases
• Re-do whole fixture when small things changed
History
Behat tests couldn't really be mocked
Built an application that proxies API calls and runs in the back in an own container
Loads of problems with that solution
History
Switching to PHP-VCR
Behat testing and mocking became very well possible
Old way of creating fixtures was no longer necessary
History
Behat
Testing framework
Works with different so called drivers
Tests are written in Gherkin
We're using the Selenium2 and Symfony Web drivers
Selenium
<?phpclass AcmeContext extends MinkContext { const COOKIE_NAME = 'BehatCassetteName';!// ...! public function before(BeforeScenarioScope $scope) { $this->getSession()->setCookie( self::COOKIE_NAME, $this->getCassetteName($scope) ); }!// ...!}
Selenium<?phpclass AcmeContext extends MinkContext {!// ...! public function getCasetteName($scope) { return 'fixtures/' . self::normalize($scope->getFeature()->getTitle()) . '/' . self::normalize($scope->getScenario()->getTitle()) ; }! public static function normalize($string) { return trim(preg_replace( '/[^a-zA-Z0-9]+/', '_', $string ), '_'); }!// ...!}
GherkinFeature: 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! Additional text...! 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 ...
Selenium<?php// web/app_test.php - Remove this file while deployment!!// ...!// Execute standard SF2 stuff$kernel = new AppKernel('test', true);$kernel->loadClassCache();Request::enableHttpMethodParameterOverride();$request = Request::createFromGlobals();!// Set up VCR with fixture name from cookie$fixture = $request->cookies->get(AcmeContext::COOKIE_NAME, null);if (!$fixture) { $fixture = AcmeContext::normalize($request->getUri());}VCR::turnOn(); VCR::insertCassette('../tests/' . $fixture . '.json');!// Execute standard SF2 stuff$response = $kernel->handle($request);$response->send();$kernel->terminate($request, $response);!// Save the fixtureVCR::eject();VCR::turnOff();
Minor pitfall:
• PhantomJS doesn't send this cookie when doing AJAX
• Fixtures for AJAX would be named after the URL, which works but isn't as nice
• You have to set the cookie manually in JS again
Selenium
WebDriver
We want this in the SF WebDriver as well
Move it from app_test to AppKernel
Keep app_test.php as entry point for PhantomJS
WebDriver<?phpclass AppKernel extends Kernel{ // ... public function handle(Request $request, ...) { if ('test' === $this->getEnvironment()) { $fixture = $request->cookies->get( AcmeContext::COOKIE_NAME, null );! if (!$fixture) { $fixture = AcmeContext::normalize( $request->getUri() ); }! VCR::turnOn(); VCR::insertCassette('../tests/' . $fixture . '.json'); }! return parent::handle($request, ...); } // ...}
GitlabCI
CI is executing tests as well
Tests might not be mocked
Test will be mocked
Conflicts while making a new build
GitlabCI
<?php// ...!if ($container->hasParameter('is_ci')) { VCR::configure()->setMode(VCR::MODE_NONE);}!// ...
Wrap up
PHP-VCR is awesome
But switching all fixtures to PHP-VCR might be a lot of work
But you don't need to write them yourselves anymore
Wrap up
Some pitfalls:
• Tests without fixtures
• Setup depending on framework might be difficult
• Not very transparent where data comes from inside tests
• First test run to write all the fixtures is slow
Info
Info and documentation: http://php-vcr.github.io
Source: https://github.com/php-vcr/php-vcr
VCR (ruby): https://github.com/vcr/vcr
Follow: @pthormeier and @adrian_philipp