I'm using Behat and Mink to test an application using the Laravel framework, and I want to use Artisan to set up the database before running the tests.
In order to correctly configure Artisan, I need to know the domain that will be being tested, and therefore which Laravel environment to use. This is listed in behat.yml under the various profiles being used. Eg:
default:
extensions:
Behat\MinkExtension\Extension:
base_url: http://www.example.com
...
daniel:
extensions:
Behat\MinkExtension\Extension:
base_url: http://example.dev
Is it possible to get the value of base_url from #BeforeSuite?
The main problem here seems to be that #BeforeSuite must be static, and therefore has no access to Mink, so I can't just $this->getMinkParameter('base_url').
I can access parameters with $suiteEvent->getContextParameters(), so I could duplicate the url there, but is there a cleaner solution.
The way some hooks work really freaks me out. The parameters are not passed until the context is instantiated, so there's no clean or easy way of getting them in the context before it's actually created. The best thing to do is to use #BeforeScenario event with a databaseReady flag, something like that:
protected static $databaseReady;
/**
* #BeforeScenario
*/
public function setupDatabase()
{
if (!self::$databaseReady) {
// Set it up…
self::$databaseReady = true;
}
}
But then I don't know how it's done in Laravel, but most frameworks have a config and bootstrap, including for different environments. It's a good idea to keep those things in there and use a bootstrap script (that can be run from the #BeforeSuite hook), which sets up the environment, including the database.
This is a dirty hack but if you have to do it from the #BeforeSuite hook you can get it like this:
use Behat\Testwork\ServiceContainer\Configuration\ConfigurationLoader;
$config = new ConfigurationLoader('BEHAT_PARAMS', getcwd() . '/behat.yml'))->loadConfiguration();
$baseUrl = $config[0]['extensions']['Behat\\MinkExtension']['base_url'];
Related
I'm running some unit-tests using PHPUnit, and hack my application pretty hard in those tests (no other way, old code-base). Some parts of the code-base use
Yii::app()->getController()->createUrl(...);
but in this case, there is no controller, so the test fails. Is there a way to add a dummy controller dynamically in my test? Something like
Yii::app()->setController($dummyController);
Or do I have to initiate some kind of fake routing event?
You can simply use:
$ctrl = new CController('whatever you need for the id')
and use its methods. Be careful, construct method sets id only. You didn't provide too much code, so this is a general idea. Look inside createUrl() method and check if it should work.
I used this technique to render pages (and use their contents) under console enviroment.
If you need a controller for multiple tests you can set it once in the setUp method.
public function setUp()
{
parent::setUp();
Yii::app()->controller = new CController('test');
}
And then you can use it in your tests:
Yii::app()->controller->createUrl(...)
I have a personal PHP framework that I maintain and use for most of the work I do. Currently in the main index.php file, I have the following code which provides access to the core application class from anywhere in the project:
/**
* #return App
*/
function app() {
// Returns the main App class instance.
}
It is properly annotated with PHPDoc so that when used, any #property-read declarations or other public members of App will be available in the code hint:
Up until the new release, PhpStorm would always be aware of the contents of App and provide this code hint. Now however, the code hint doesn't work unless you are in the same namespace as App (a top level namespace) or explicitly use App or prefix all calls to app() as \app().
What's weirder is that even though the code hint isn't available outside of the top level, the quick documentation utility is perfectly aware that app() returns an App and links to the correct definition for App:
I've tried marking public as a "Sources Root" and updating my composer.json to include public in the autoload/psr4 block for "" with no change:
"autoload": {
"psr-4" : {
"": ["public/", "app/src/"]
}
}
At this stage I'm not sure whether:
This is a bug with PhpStorm that I should raise with them.
This is the expected behavior and it has just been fortunate that it worked the way I am trying to achieve for so long (use App should in fact be present for the inspector to understand what it is).
There is a trivial way either in the file structure, namespace usage or even PhpStorm settings to have it working the way it did before.
It looks like the behaviour has been corrected in a recent release (I just upgraded to 2016.1.2 Build #PS-154.1616).
I am successfully using Behat 3.0 with the tests defined in Feature files, using Gherkin language. However, in some cases, it would be useful to define the steps programatically - Gherkin is readable, but difficult to define multiple variants.
Is there a way to programatically define the test steps (in PHP classes), so these can be picked up by Behat? I have found ArrayLoader class, which seems to be able to do that. However, I wasn't able to make it working with Behat. It seems Behat is using Gherkin FileLoader by default and I haven't found a way to rewrite this behavior (or rather extend) in the config file.
How can I combine test input from Gherkin files with custom definitions specified in PHP files?
Is there a way to programatically define the test steps (in PHP
classes),...
If I don't misunderstand what you want, you can do like this:
use Behat\Behat\Definition\Call\Then;
use Behat\Behat\Definition\Call\When;
use Behat\MinkExtension\Context\MinkContext;
class FeatureContext extends MinkContext
{
public function iWaitSeconds($second)
{
new Then(.....);
new When(.....);
new Given(.....);
}
}
You need to do a bit of research for more examples.
e.g.: new When("The content is:", new PyStringNode($string));
I've been using the Behat english-like test language (Gherkin?) to write test scripts but have quickly come up it's significant limitations.
If I could execute these tests in PHP within the phpunit test scripts that I have set up I could significant expand the tests that I could add. (I'm using FuelPHP).
I've been tinkering around for a few hours trying to get Behat to execute inside a PHPUNIT test script but have not had much luck.
Is this possible?
I think you are confusing something, because what you are saying doesn't make a lot of sense. If you having a hard time expressing the logic with the code, you should ask a specific question on that.
Behat and Mink are both written in PHP, you write your contexts in PHP, there is a gadzillion of plugins to make the life easier (also written in php). As the matter of fact, all your tests are executed in PHP when you run them… Yup!
If you want to compare data from two pages you can simply create a step like this:
/**
* #Then /^the page "(.+)" and the page "(.+)" content should somehow compare$/
*/
public function assertPageContentCompares($page1, $page2)
{
$session = $this->getSession();
$session->visit($page1);
$page1contents = $session->getPage()->getHtml();
$session->visit($page2);
$page2contents = $session->getPage()->getHtml();
// Compare stuff…
}
Besides the obvious, you can use PHPUnit in with Behat / Mink to make the assertions, i.e., in your step definitions. Most (in not all) PHPUnit assertions are static methods, using them is as simple as this:
PHPUnit_Framework_TestCase::assertSame("", "");
You can use Selenium (probably other frameworks too) with PHPUnit, if this is more about unit testing than functional testing, the official documentation tells how.
If you simply hate Gherkin then there's not much you can do with Behat – it's at the core of it. With PhpStorm 8 out there is a pretty good support for it, you can easily navigate around your code and refactor it quickly. If that doesn't cut it, there's another great alternative to Behat called Codeception, where you use pure PHP to define your tests. Maybe that's what you are looking for.
Yes. You can use the library I created: jonathanjfshaw/phpunitbehat.
Your phpunit tests will looks like this:
namespace MyProject\Tests;
use PHPUnit\Framework\TestCase;
use PHPUnitBehat\TestTraits\BehatTestTrait;
class MyTestBase extends TestCase {
use BehatTestTrait;
}
namespace MyProject\Tests;
class MyTest extends MyTestBase {
protected $feature = <<<'FEATURE'
Feature: Demo feature
In order to demonstrate testing a feature in phpUnit
We define a simple feature in the class
Scenario: Success
Given a step that succeeds
Scenario: Failure
When a step fails
Scenario: Undefined
Then there is a step that is undefined
FEATURE;
/**
* #Given a step that succeeds
*/
public function aStepThatSucceeds() {
$this->assertTrue(true);
}
/**
* #When a step fails
*/
public function aStepFails() {
$this->assertTrue(false);
}
}
I wrote a blog post explaining why I think this is not a bad idea.
I have a test for testing data model:
<?php
namespace Tests\Model\SQL;
/**
* #group Model_Users
* #group Model
*/
class UsersTest extends TestCase {
/** #var Users */
private $model;
public function setUp() {
parent::setUp();
$this->connection->execute( file_get_contents(
__DIR__ . '/fixtures/countries.sql' ) );
$this->model = new \Users(
$this->connection
);
}
public function testUserExists() {
$exists = $this->model->userExists( 'johndoe' );
$this->assertTrue( $exists );
}
}
The Tests\Model\SQL\TestCase uses SQLite as a database to keep tests as fast as possible.
Anyway, as you may expect, SQLite is not the same database as Postgres, therefore there could be some specialites, which will fail on SQLite and pass on PostgreSQL and vice versa.
My question is:
How to effectively (when keeping DRY in mind) design the architecture to be able to run tests only with SQLite and then only with Postgres.
When I am saying DRY, I mean ideally case, when I write only one testcase for one model and the architecture of my tests will take care of that, so I will be able to run tests like this:
php tests/run.php --group=MockedDbWithSqlite # fast way, prefered, default
php tests/run.php --group=RealDb # slower way, but with more precise results
So judging from the comment the issue is rather independent of any concrete storage issue and more about how one can pass a custom parameter to PHPUnit.
Because, using this parameter, you'd switch out DB drivers.
There are a couple of options to do this but no officially supported "custom parameters" objects or something. Even so it would be nice if someone would add this maybe :)
My suggestion would be to use one of the following to options:
Env variables
Make the test/bootstrap code rely on $_ENV variables.
$dbDriverFactory->getInstanceFor(getenv('DB'));
and using one of the ways below to
export DB=postgress
phpunit
or
DB=postpress; phpunit;
or using phpunits xml config to set the env or whatever you like.
By doing so you could provide two xml configs phpunit-sqlite.xml & phpunit-pg.xml and run:
phpunit -c phpunit-sqlite.xml
It doesn't have to be env. Any of the methods listed in the doc section Setting PHP INI settings, Constants and Global Variables will work.
"Custom" cli parameters
Calling phpunit like this:
phpunit --colors --usePostgres
and adding something like this in test cases or bootstrap code:
if (in_array('--usePostgress', $_SERVER['argv'], true) {
$db = $this->setupPG();
} else {
$db = $this->setupSqlite();
}
I read this differently to edorian. I like to use inheritance for this kind of thing. The advantage is you can run both SQLite and Postgres tests in the same test run. The disadvantage is you have to add some code to each test file where you want to run with both databases. And you have to repeat this for each new database you want to test. Here is how it works:
class UsersTestBase extends TestCase {
//Unchanged
}
class UsersTestSQLite extends UsersTestBase{}
class UsersTestPostgres extends UsersTestBase {
function __construct(){
parent::_construct("postgres");
}
}
So, UsersTest is exactly the same, except the name has changed to append "Base".
You then have a derived UsersTestSQLite class. This does nothing. At this point we've not gained or lost anything.
Then we had UsersTestPostgres. This somehow tells the object to use postgres instead of sqlite. In this case I've assumed TestCase has a constructor that can take the database to use as an optional parameter (with the default being "sqlite"). Obviously there are lots of alternatives to that, but I hope you see the general idea.
Finally you need to add the suitable #group tags to the derived classes, to allow you control over when they are run.