Open leitom opened 9 years ago
I also see that in my local environment with mysql the db transactions not work... :-/
I'm finding that when I run test without javascript , my test sqlite database gets used, and transactions work. When I run a test with javascript, the development database gets used and the transactions dont work. It seems like when running test with javascript the .env
file is used instead of the .env.behat
file. Here is my behat.yml
default:
extensions:
Laracasts\Behat:
env_path: .env.behat
Behat\MinkExtension:
default_session: laravel
laravel: ~
base_url: http://aws-transcriptions.app/
javascript_session: selenium2
browser_name: firefox
selenium2: ~
I wonder if this is related.
Has anyone solved this problem?
Perhaps the problem is that when using the Laravel session, it emulates requests without querying the actual webserver, and keeps the database transaction intact. When using javascript (using selenium), it goes to the server (that is, the server running a development, not behat environment) and does an actual web request, causing non-committed database transactions to be missing / rolled back in subsequent requests in the same test?
I'm having a similar problem with database transactions where my Background
section creates a row for my Scenario's to use, but the row is missing in the tests. When I comment out the use DatabaseTransactions
part of my FeatureContext
, naturally the record will persist and the test succeeds. But then I would have to clean the database again manually somehow.
Does this explain the problems you guys are having? And can anyone offer a better workflow for doing these kinds of multi-request javascript tests with functional database rollbacks?
We were experiencing similar issues as described by @koenvu when running tests with javascript (zombie-mink-driver). For now we managed to work around this by setting transaction isolation level for our MySQL database, before running the suite:
// FeatureContext.php
/**
* @beforeSuite
*/
public static function setTransactionIsolationLevel()
{
DB::statement('SET GLOBAL TRANSACTION ISOLATION LEVEL READ UNCOMMITTED');
}
/**
* @afterSuite
*/
public static function resetTransactionIsolationLevel()
{
DB::statement('SET GLOBAL TRANSACTION ISOLATION LEVEL REPEATABLE READ');
}
yep, this is definetly an issue.
@artaaa you use before suite because your test requires it, right? But If I need the database to be clean on every test, it would work the same?
@JeffreyWay any plans to work on this?
By any chance you are making a call to a url? e.g http://localhost:8888?
I'm adding Illuminate\Foundation\Testing\ApplicationTrait
to FeatureConext and making calls like normal laravel test: $this->call($httpMethod, $this->resource, json_decode($payload->getRaw(), true), [], [], $this->header);
This way the transaction and rollback happens as expected.
You need to make sure that the request you are making does not start a new thread/db connection, or the transaction won't work.
I will try and recreate this issue in a week or so when i get back from holiday but just want to rekindle these older issues to see if they kept you from sticking with behat or if there is a solution you would like to share.
As a note @koenvu noted when using @javascript it will go through Selenium and therefore the .env file of your app is the file setting up the databasse to be used eg "DB_DATABASE=foo" but then .env.behat might be setup for "test" so them behat is talking to the wrong database. For us we set .env.behat to the same database the dev environment. Not ideal but works.
Our CI has no problems with this since the app and behat are set to use the test database. But locally this can be tricky. This even gets harder when we want to use these behat tests to set states on remote sites of ours to do post deployment testing. We have gone through the trouble here of including a route file, controller and behat user we can then log in as, on non Production sites, set state using '$this->visit('someUrlToSetStateForThisScenario')' and then cleanup state when done.
Overall though this works BUT I need to dig in more to the transaction side of things to see if that is working as well.
@andizzle
I try to use your solution, but currently I'm using Laravel 5.2 which doesn't have Illuminate\Foundation\Testing\ApplicationTrait
to use the call()
function.
Any idea for this..?
@praditha It maybe even easier to do this in 5.2. There's a trait Illuminate\Foundation\Testing\Concerns\MakesHttpRequests
which should allow you to make all kinds of requests.
But I haven't tested this yet. Please do let me know whether this works or not.
@andizzle Yes, I've tried it, but no luck.
There is an error like this:
Notice: Undefined property: FeatureContext::$app in vendor/laravel/framework/src/Illuminate/Foundation/Testing/Concerns/MakesHttpRequests.php line 504
I've tried to setup the $app
like in TestCase
class
$app = require __DIR__.'/../../bootstrap/app.php'; $app->make(Illuminate\Contracts\Console\Kernel::class)->bootstrap();
And the result is behat read the .env
file instead of .env.behat
@praditha I'll give it a try over the weekend. In the mean time give this a try in FeatureContext.php
:
public function __construct() {
global $app;
$this->app = app();
$app = $this->app;
}
This works in L5.0.
in 5.1 this works in the FeatureContext.php
public function __construct()
{
parent::setUp();
}
I tend to do what @andizzle did but really want to try what @eriksape did
@andizzle I've tried it, and there is another error
Notice: Undefined property: FeatureContext::$baseUrl in vendor/laravel/framework/src/Illuminate/Foundation/Testing/Concerns/MakesHttpRequests.php line 594
@eriksape It's not working on Laravel 5.2
, it shows an error:
Call to undefined method Behat\MinkExtension\Context\MinkContext::setup()
As on the tutorial, I extends MinkContext
to FeatureContext
class.
Ah finally I can solved it on Laravel 5.2
.
I use the @andizzle solutions with adding some code to register the $baseUrl
, here is a piece of code.
use Illuminate\Foundation\Testing\Concerns\MakesHttpRequests;
Class FeatureContext extends MinkContext implements Context, SnippetAcceptingContext
{
use DatabaseTransactions;
use MakesHttpRequests;
public function __construct()
{
global $app;
$this->app = app();
$app = $this->app;
global $baseUrl;
$this->baseUrl = url('/');
$baseUrl = $this->baseUrl;
}
...
}
Is there a PR for this?
Just to confirm I also experience the same issue reported by @doug7410 regarding of switching on/off the @javascript.
For now I will try your suggestion @alnutile just to make it work in my local machine, Thanks.
+1 ran into this problem today while using Selenium and Behat. Hopefully there will be a PR for this but I will give one of the solutions in this thread a shot.
@alnutile Were you ever able to get this working on Laravel 5.3+?
@koenvu Did you manage to sort your issues with inserting to database in Background? We're facing the same issue, migration and transaction is run after, so won't work correctly :(
@edmundluong did you tru @artaaa solution
Frankly, I can't see how having Laravel to do the request can help here. It's totally irrelevant. The whole point of using Selenium is to enable the tests to exercise applications implemented in Javascript. What if your front is implemented with Vue?
When not using a browser simulator, there's just one instance of Laravel running that uses the .env.behat
file. But when using a Javascript session, requests are dispatched by the browser, not PHP and so there's no way for Laravel or this package to detect the testing environment.
All other workarounds are assuming that local/production and testing environment are both using the same database connection, which is not my case.
To alleviate this, we need to somehow create a link between the Behat testing session and Laravel. For example; passing a querystring, e.g. behat=true
, when dispatching each request. Then on the Laravel side, we need to detect the signal and load the appropriate env file accordingly which will affect the production code. Ah, it's all messed up...
IMO, this is a serious issue. It makes testing Javascript applications literally impossible. And that's not only about databases, but anything that can be related to some kinda state; env configs, databases, caches, queues, etc.
Related: Behat testing in laravel with javascript - not using test environment
Update; It's interesting how Dusk handles this: How Laravel Dusk handles environments
Update; So, inspired by Dusk, to workaround preserving the environment we can use a trait like this:
trait PreserveEnvironment
{
/**
* @BeforeScenario
*/
public static function backupAndPreserve()
{
rename('.env', '.env.bak');
copy('.env.behat', '.env');
}
/**
* @AfterScenario
*/
public static function restore()
{
unlink('.env');
rename('.env.bak', '.env');
}
}
If you use this trait in your FeatureContext
class, then any request dispatched from Selenium or any other browser emulator managed by Mink will meet your .env.behat
file (as it's now renamed to .env
). Then after exercising each scenario, the original .env
file will be restored.
It solves a lot of problems, but still... there are shortcomings. Assume you log a user in using Auth::login()
before you dispatch the request and you want to assert that she can access some protected pages.
Update; Dusk has also a solution for the authentication. See InteractsWithAuthentication trait.
I'm coming up with a quick solution; will update asap.
And here it is:
https://github.com/sepehr/behat-laravel-js
Seems to be a solution for all the problems I was facing; the readme file goes through them all.
As I was in middle of a project and needed to use the traits, I just wrote a separate package instead of submitting a pull request. If you think it could be a good fit for this package, I'll be more than willing to submit a pull request.
@sepehr can you make pull request to this package . because the new package features integrate between laravel and behat plus javascript .
thank you
Sure @osman-mohamad; Actually, it's a long overdue task in my contribution todo list. Will adapt the code to the extension and do a pull request as soon as I find some free time.
Meanwhile, you can install it as a separate package alongside with behat-laravel-extension.
How this is supposed to work? It only makes sure your env.behat will be used but the transactions still won't be visible for browser's sessions since it's a new php process.
Does it use same database? Yes
Can it access data seeded in test preStep/preScenario/preFeature and rolledback on after* No.
Edit: All right, It requires Database Migration so this is a big no,no if you are working on DB size bigger than xGB.
I looks like database transactions don't work when using mysql. I dont know if this is because of laravel 5 latest changes or not.