Laravel Dusk Test Multi Threading - laravel

I have a series of Laravel Dusk tests as shown below ,,
when I run php laravel dusk
each of them work correctly but one test after another
how can I run more than one test in the same time each test in separate browser ??
and how to control the order of the tests to run ?
here is my DuskTestCase options :
protected function driver()
{
$options = (new ChromeOptions)->addArguments([
'--disable-gpu',
'--window-size=1920,1080',
]);
return RemoteWebDriver::create(
'http://localhost:9515',
DesiredCapabilities::chrome()->setCapability(
ChromeOptions::CAPABILITY,
$options
),
90000,
90000
);
}

Related

Laravel Dusk 2.0 / Laravel 5.5 returns an empty page

I'm in the midst of upgrading my Laravel 5.3 website to 5.5 and unable to get Dusk working properly against my localhost. I have other unit tests working properly against my localhost but for some reason, Dusk returns "<html xmlns="http://www.w3.org/1999/xhtml"><head></head><body></body></html>" for any local page. Local pages seem to work fine browsing it on my desktop.
My DuskTestCase
protected function driver()
{
$options = (new ChromeOptions)->addArguments([
'--disable-gpu',
'--headless',
'--no-sandbox',
'--ignore-certificate-errors'
]);
return RemoteWebDriver::create(
'http://localhost:9515',
DesiredCapabilities::chrome()
->setCapability(WebDriverCapabilityType::ACCEPT_SSL_CERTS, true)
->setCapability('acceptInsecureCerts', true)
->setCapability(
ChromeOptions::CAPABILITY, $options
)
);
}
My Sample Test
public function testBasicExample()
{
$this->browse(function (Browser $browser) {
$result = $browser->visit('http://localhost');
$browser->screenshot('test');
$browser->dump();
});
}
chrome driver works fine calling google.com and dumps content
hardcoded the url to localhost for testing
page is http not https
tried php artisan serve before calling test, same result
tried cache / config clear
created an .env.dusk.local file, didn't seem to affect it
chromedriver -v is ChromeDriver 2.44.609551
tried 127.0.0.1 no dice
The only way I could make it working:
public function testBasicExample()
{
$this->browse(function (Browser $browser) {
$browser->visit(env('APP_URL').'/home')
->assertSee('Laravel');
});
}

Laravel Dusk screenshot

I'm using laravel 5.6 and Dusk for running some tests.
I'm always taking my screenshot like this
...
use Facebook\WebDriver\WebDriverDimension;
...
class LoginTest extends DuskTestCase
{
public function testLogin()
{
$user = User::first();
$this->browse(function ($browser) use ( $user ) {
$test = $browser->visit( new Login)
->resize(1920,1080)
...
->driver->takeScreenshot(base_path('tests/Browser/screenshots/testLogin.png'));
});
}
}
But as my tests will be more and more used, I don't want to continue to write everytime ->resize(X,Y) and base_path('bla/blab/bla').
I wanted to define the size and path for every tests that will be written.
I guess I should define some function in tests/DesukTestCase.php but I'm not even aware of how I can retrieve the driver and so on.
Have you got some guidance or documentation about this? Because I can't find anything...
In my DuskTestCase file I have the below in my driver() function.
protected function driver()
{
$options = (new ChromeOptions())->addArguments([
'--disable-gpu',
'--headless',
]);
$driver = RemoteWebDriver::create(
'http://selenium:4444/wd/hub',
DesiredCapabilities::chrome()->setCapability(
ChromeOptions::CAPABILITY,
$options
)
);
$size = new WebDriverDimension(1280, 2000);
$driver->manage()->window()->setSize($size);
return $driver;
}
You should just be able to configure it with the right dimensions you need.
You only need to add '--window-size=1920,1080' in $options. This will apply a 1920x1080 screen resolution to all your Dusk tests. Feel free to adjust to whatever window size you want.
So your DuskTestCase.php file should look like this:
protected function driver()
{
$options = (new ChromeOptions())->addArguments([
'--disable-gpu',
'--headless',
'--window-size=1920,1080',
]);
$driver = RemoteWebDriver::create(
'http://selenium:4444/wd/hub',
DesiredCapabilities::chrome()->setCapability(
ChromeOptions::CAPABILITY,
$options
)
);
}
Regarding the path issue, you can set it with Browser::$storeScreenshotsAt in setUp method of your test case class.
protected function setUp()
{
parent::setUp();
Browser::$storeScreenshotsAt = '/path/to/your/screenshots';
}
Default location of Browser::$storeScreenshotsAt is set in setUp method of the grand parent test case class.
So, make sure that you set Browser::$storeScreenshotsAt after calling parent::setUp(), otherwise it will be overwritten by the default.

How To Test Artisan Commands with ask() in Laravel 5.4

Trying to write a test for laravel php artisan command with ask() function. I have never used mockery before, but when i try to run test, it freezes, so i guess, i'm doing something wrong.
MyCommand.php:
public function handle()
{
$input['answer1'] = $this->ask('Ask question 1');
$input['answer2'] = $this->ask('Ask question 2');
$input['answer3'] = $this->ask('Ask question 3');
//--- processing validation
$validator = Validator::make($input, [
'answer1' => 'required',
'answer2' => 'required',
'answer3' => 'required',
]);
if ($validator->fails()) {
// processing error
}
} else {
// saving to DB
}
}
And my unit test:
$command = m::mock('\App\Console\Commands\Questions');
$command->shouldReceive('ask')
->andReturn('Answer 1')
->shouldReceive('ask')
->andReturn('Answer 2')
->shouldReceive('ask')
->andReturn('Answer 3')
$this->artisan('myCommand:toRun');
$this->assertDatabaseHas('myTable', [
'question1' => 'answer1'
]);
Laravel 5.4 - 5.6
The actual issue here is that running the console command is waiting for user input, however we are running this through PHPUnit so we are unable to enter anything.
Bumping up against limitations in unit testing can initially be frustrating, however limitations you find can end up being a blessing in disguise.
Currently, your implementation is tightly coupled to a view (a console command, so a view to an admin, but still a view none-the-less.) What could be done here is place any logic within a separate class which MyCommand can utilize, and which PHPUnit can actually test on their own. We know that the fundamentals of running a custom command work, as demonstrated in Laravel unit tests, so we can offload our logic in a separate, testable class.
Your new class might look something like this:
class CommandLogic
{
public function getQuestion1Text()
{
return 'Ask question 1';
}
public function getQuestion2Text()
{
return 'Ask question 2';
}
public function getQuestion3Text()
{
return 'Ask question 3';
}
public function submit(array $input)
{
$validator = \Illuminate\Support\Facades\Validator::make($input, [
'answer1' => 'required',
'answer2' => 'required',
'answer3' => 'required',
]);
if ($validator->fails()) {
// processing error
} else {
// saving to DB
}
}
}
...your actual unit test, something like this:
$commandLogic = new CommandLogic();
$sampleInput = [
'answer1' => 'test1',
'answer2' => 'test2',
'answer3' => 'test3',
];
$commandLogic->submit($sampleInput);
$this->assertDatabaseHas('myTable', [
'question1' => 'test1'
]);
...and your console command, something like this:
public function handle()
{
$commandLogic = new CommandLogic();
$input['answer1'] = $this->ask($commandLogic->getQuestion1Text());
$input['answer2'] = $this->ask($commandLogic->getQuestion2Text());
$input['answer3'] = $this->ask($commandLogic->getQuestion3Text());
$commandLogic->submit($input);
}
This enforces the single responsibility principle and separates the moving pieces in your codebase. I know this may feel like a bit of a cop out, but testing this stuff in Laravel 5.4 is tough. If you are willing to upgrade to 5.7 or higher, read below...
Laravel 5.7+
Laravel 5.7 introduced being able to run console tests, which satisfies the exact requirement this question is asking - https://laravel.com/docs/5.7/console-tests. This is more of a full integration test rather than a unit test.

Laravel dusk chrome driver timeout

Can anyone help, I am unable to get Laravel dusk to run the default sample test in my current Laravel 5.6 project on mac high sierra.
Error message
Time: 2.5 minutes, Memory: 14.00MB
There was 1 error:
1) Tests\Browser\ExampleTest::testBasicExample
Facebook\WebDriver\Exception\WebDriverCurlException: Curl error thrown for http POST to /session with params: {"desiredCapabilities":{"browserName":"chrome","platform":"ANY","chromeOptions":{"binary":"/Users/keith/Desktop/dusk/vendor/laravel/dusk/bin/chromedriver-mac","args":["--disable-gpu"]}}}
Operation timed out after 30002 milliseconds with 0 bytes received
/Users/keith/Desktop/dusk/vendor/facebook/webdriver/lib/Remote/HttpCommandExecutor.php:286
/Users/keith/Desktop/dusk/vendor/facebook/webdriver/lib/Remote/RemoteWebDriver.php:126
/Users/keith/Desktop/dusk/tests/DuskTestCase.php:40
/Users/keith/Desktop/dusk/vendor/laravel/dusk/src/Concerns/ProvidesBrowser.php:189
/Users/keith/Desktop/dusk/vendor/laravel/framework/src/Illuminate/Support/helpers.php:770
/Users/keith/Desktop/dusk/vendor/laravel/dusk/src/Concerns/ProvidesBrowser.php:190
/Users/keith/Desktop/dusk/vendor/laravel/dusk/src/Concerns/ProvidesBrowser.php:92
/Users/keith/Desktop/dusk/vendor/laravel/dusk/src/Concerns/ProvidesBrowser.php:64
/Users/keith/Desktop/dusk/tests/Browser/ExampleTest.php:21
I have already done the following :
added the following to app\Providers\AppServiceProvider.php
use Laravel\Dusk\DuskServiceProvider;
...
public function register()
{
if ($this->app->environment('local', 'testing')) {
$this->app->register(DuskServiceProvider::class);
}
}
ran 'php artisan dusk:install' in terminal
set App_URL in .env to http://localhost:8000
specified the location of chromedriver in DuskTestCase
start 'php artisan serve' before running 'php artisan dusk'
Repository : https://github.com/KKOA/dusk
If your function extends from DuskTestCase.php, then you need to increase connection_timeout_in_ms.
Do this by changing driver method to the following:
DuskTestCase.php
protected function driver()
{
$options = (new ChromeOptions)->addArguments([
'--disable-gpu',
'--headless',
'--window-size=1920,1080',
]);
return RemoteWebDriver::create(
'http://localhost:9515', DesiredCapabilities::chrome()->setCapability(
ChromeOptions::CAPABILITY, $options
), 90000, 90000
);
}
If that dosen't work for some reason then try to set_time_limit before $this->browse
set_time_limit(0);
try this while you instantiate your browser.. the most important is
$driver = retry(5, function () use ($capabilities) {
return RemoteWebDriver::create('http://localhost:9515', $capabilities, 60000, 60000);
Below is how i instantiate my browser
$options = (new ChromeOptions)->addArguments(['--disable-gpu', '--headless', '--no-sandbox']);
$capabilities = DesiredCapabilities::chrome()
->setCapability(ChromeOptions::CAPABILITY, $options)
->setPlatform('Linux');
$driver = retry(5, function () use ($capabilities) {
return RemoteWebDriver::create('http://localhost:9515', $capabilities, 60000, 60000);
}, 50);
$browser = new Browser($this->driver, new ElementResolver($driver, ''));
You really need to have latest google-chrome and latest chrome driver installed. I used to have only chromium browser and it doesn't work with it. To install latest chrome driver run php artisan dusk:chrome-driver.

Laravel Dusk how to show the browser while executing tests

I am writing some tests and I want to see whether Dusk correctly fills in the input fields but Dusk doesn't show the browser while running the tests, is there any way to force it to do that?
Disable the headless mode in tests\DuskTestCase.php file driver() function:
$options = (new ChromeOptions)->addArguments([
//'--disable-gpu',
//'--headless'
]);
Updated (2021):
You can disable headless with 2 methods:
Method 1: Add this to your .env
DUSK_HEADLESS_DISABLED=true
Method 2: Add this to your special test case if you don't need to show the browser for all tests
protected function hasHeadlessDisabled(): bool
{
return true;
}
Btw, I don't know why these are not mentioned in the documentation. I found the above methods myself from DuskTestCase.php.
Answer for Laravel 8 & UP
You can use php artisan dusk --browse to force showing the browser.
Near the top of your tests/DuskTestCase.php file, add:
use Facebook\WebDriver\Chrome\ChromeOptions;
In that same file, replace the entire driver() function with:
/**
* Create the RemoteWebDriver instance.
*
* #return \Facebook\WebDriver\Remote\RemoteWebDriver
*/
protected function driver() {
$options = (new ChromeOptions)->addArguments([
//'--disable-gpu',
//'--headless'//https://stackoverflow.com/q/49938673/470749
]);
return RemoteWebDriver::create(
'http://localhost:9515', DesiredCapabilities::chrome()->setCapability(
ChromeOptions::CAPABILITY, $options
)
);
}

Resources