Laravel Behat and Selenium
Laracasts has some great videos and libraries for Laravel 5 and Behat integration.
Examples
https://github.com/laracasts/Behat-Laravel-Extension
and
https://laracasts.com/lessons/laravel-5-and-behat-bffs
Two things that I still need and get from this though that I do not think I can get from those are
- Laravel 4.2 support which obviously is not going to work with the above L5 libraries :)
- Mocking APIs when running under APP_ENV=local or testing
Also I think with the libraries above only goutte drivers work for the APP_ENV setting.
Dealing with APIs
We use a lot of APIs. One for example is Github so make a provider like this
The Provider
I register an API Provider like this
<?php
namespace BehatEditor\Services;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\ServiceProvider;
class GitApiServiceProvider extends ServiceProvider {
/**
* Register the service provider.
*
* @return void
*/
public function register()
{
if(App::environment() == 'testing' || Config::get('app.mock') == true)
{
$this->app->singleton('GithubClientInstance', function($app){
$git = new GithubApiMockService();
$username = getenv('GIT_USERNAME');
$token = getenv('GIT_TOKEN');
$git->setUsername($username);
$git->setToken($token);
$git->setLogger($app['log']);
$git->setLogging(true);
$git->authenticate();
return $git;
});
} else
{
$this->app->singleton('GithubClientInstance', function($app){
$git = new GithubApiService();
$username = getenv('GIT_USERNAME');
$token = getenv('GIT_TOKEN');
$git->setUsername($username);
$git->setToken($token);
$git->setLogger($app['log']);
$git->setLogging(true);
$git->authenticate();
return $git;
});
}
}
}
The “app.mock” I set in two places
config/local/app.php
and
config/testing/app.php
<?php
return array(
'chat_on' => false,
'mock' => true,
'debug' => true,
'providers' => append_config(array(
'GuilhermeGuitte\BehatLaravel\BehatLaravelServiceProvider'
))
);
Testing works well for Codeship.
Then if we have mock as true for local and I run
php artisan behat:run --profile=als_local_ui --stop-on-failure ui/people_ui.feature
I can test all my Angular ui for People
Here is the behat.yml for that, keep in mind I run this from inside of Vagrant (Homestead) and Selenium is running on my Mac thanks to “webdriver-manager” and brew install chromedriver you can see more on that here
default:
filters:
tags: "~@wip"
formatter:
name: pretty
parameters:
decorated: true
verbose: false
time: true
language: en
output_path: null
multiline_arguments: true
paths:
features: app/tests/acceptance/features
bootstrap: app/tests/acceptance/contexts
context:
parameters:
base_url: http://behat.dev
asset_path: '/tmp/'
als_local_ui:
extensions:
Behat\MinkExtension\Extension:
default_session: selenium2
goutte:
guzzle_parameters:
curl.options:
CURLOPT_SSL_VERIFYPEER: false
CURLOPT_CERTINFO: false
CURLOPT_TIMEOUT: 120
ssl.certificate_authority: false
selenium2:
wd_host: "http://192.168.33.1:4444/wd/hub"
base_url: 'https://admin:foo@behat.dev:44300'
browser_name: chrome
The Mock Class
The mock class just extends the real class but takes over
If mock is on it looks for a matching fixture file and uses that, else it makes one real call, saves the fixture and then uses that next time.
<?php
namespace BehatEditor\Services;
use AlfredNutileInc\Fixturizer\FixturizerReader;
use BehatEditor\Exceptions\ModelException;
use BehatEditor\Helpers\BuildFileObject;
use BehatEditor\Helpers\ThrowAndLogErrors;
use BehatEditor\Providers\GithubClientInterface;
use Github\Client;
use Github\ResultPager;
use BehatEditor\Interfaces\BehatUIInterface;
use BehatEditor\Repositories\ProjectsRepository;
use Illuminate\Support\Facades\Log;
class GithubApiMockService extends GithubApiService implements GithubClientInterface {
public $sha;
protected $application;
/**
* @var \Github\Client
*/
public $client;
protected $username;
protected $token;
protected $branch;
protected $parent_file;
protected $reponame;
protected $folder;
protected $logging = false;
protected $logger;
/**
* @var RepoSettingRepository
*/
private $repoSettingRepository;
public function __construct(Client $client)
{
$this->client = $client;
$this->path = base_path() . '/tests/fixtures/';
}
public function seeIfRepoHasCustomSteps()
{
$this->logMock('repo_has_custom_steps');
$results = FixturizerReader::getFixture('git_show_repo_custom_steps.yml', $this->path);
return $results;
}
I am using this library to quickly make fixtures https://packagist.org/packages/alfred-nutile-inc/fixturizer
That makes our tests super fast since we are never hitting out APIs like Github, Pusher, etc.
I cover it Mocking Queue Service for faster Behat Testing as well.
API Testing
We use Behat to test our API endpoints as seen in the book Build APIs You Won’t Hate
For hitting the API we use basic.once
#filter.php
Route::filter('basic.once', function()
{
if(Auth::guest())
{
/**
* First authenticate as normal
*/
if ($results = Auth::onceBasic() )
{
return $results;
}
}
});
And the route would be
Route::group(['prefix' => 'api/v1', 'before' => 'basic.once|auth'], function() {
///routes
}
This allows our Angular app which happens to live inside the same codebase of the API to login using a standar Laravel Form but also allows other apps to access the API (Oauth coming soon)
Reseeding the DB
This step helps with that
/**
* @Given /^I reseed the database$/
*/
public function iReseedTheDatabase()
{
$env = getenv('APP_ENV');
if(getenv('APP_ENV') != 'production')
{
try
{
if(getenv('APP_ENV') == 'testing')
{
copy(__DIR__ . '/../../../../app/database/stubdb.sqlite', __DIR__ . '/../../../../app/database/testing.sqlite');
}
else
{
exec("php artisan migrate:refresh --seed -n --env=$env");
}
}
catch(\Exception $e)
{
throw new \Exception(sprintf("Error seeding the database %s", $e->getMessage()));
}
} else {
throw new \Exception(sprintf("You can not seed production"));
}
}
I cover more on that PHP quick fixture data for phpunit testing
Loading APP
FeatureContext has a BaseContext that has these methods
public function setApp()
{
$app = new Illuminate\Foundation\Application;
$env = $app->detectEnvironment(
function()
{
if(!getenv('APP_ENV'))
{
Dotenv::load(__DIR__ .'/../../../../');
}
return getenv('APP_ENV');
}
);
$app->bindInstallPaths(require __DIR__ . '/../../../../bootstrap/paths.php');
$framework = $app['path.base'].
'/vendor/laravel/framework/src';
require $framework.'/Illuminate/Foundation/start.php';
$this->app = $app;
$this->app->boot();
$this->env = $env;
}
public function getApp()
{
return $this->app;
}
On the __construct it does
public function __construct(array $parameters) {
$config = isset($parameters['guzzle']) && is_array($parameters['guzzle']) ? $parameters['guzzle'] : [];
$config['base_url'] = (isset($parameters['base_url'])) ? $parameters['base_url'] : false;
$this->parameters = $parameters;
$this->client = new Client($config);
$this->iSetCredentials();
Factory::$factoriesPath = 'app/tests/factories';
$this->setApp();
}
Laracast TestDummy / Factories
One example of using factories is a step like this
/**
* @Given /^I create person fixture with "([^"]*)" id$/
*/
public function iCreatePersonFixtureWithId($arg1)
{
Factory::create('TheHub\Profile\User', [ 'id' => $arg1 ]);
}
Using the Laracast TestDummy library I can quickly stub out data for the test.
comments powered by Disqus