Laravel Testing - How to run command with RefreshDatabase with seed - laravel

in Laravel I need to run MY CUSTOM COMMAND after refreshdatabase and db:seed
new database
migration
seed
MY COMMAND
this test finish successfully when i run all these steps manually
I write this test code.
class ExampleTest extends TestCase
{
use RefreshDatabase;
public function testDbSeed()
{
Artisan::call('db:seed');
$resultAsText = Artisan::output();
$this->assertTrue(true);
}
all tables deleted and then run all my migrations successfully.
My question is How to run my CUSTOM command after seed?
php artisan permission:sync
public function testPermissionSync()
{
Artisan::call('permission:sync');
$resultAsText = Artisan::output();
$this->assertTrue(true);
}
after this command we can open main page in ourlocalsite.local/
/**
* A basic test example.
*
* #return void
*/
public function testBasicTest()
{
$response = $this->get('/');
$response->assertStatus(200);
}
but this test not passed and assert error is 403
(when i do these steps manually this command run)

you can use setUp() method that triggered before any test begins.
but don not forget to call the parent setup
public function setUp(): void
{
parent::setUp();
Artisan::call('db:seed');
}
there is also tearDown() function that triggered after test ended

Related

Laravel actingAs guest

Laravel provides a way to authenticate a given user during HTTP testing with
$this->actingAs($user);
Is there a way to unauthenticate that $user within the same test?
Yes, you can unauthenticate using this:
Auth::logout();
https://laravel.com/docs/7.x/authentication#logging-out
Warning: above does far more than just forgetting (acting as if the login did not happen), for example, when using JWT above should invalidate token.
Yes, define new actingAsGuest method in base TestCase class
in file tests/TestCase.php
<?php
namespace Tests;
use Illuminate\Auth\RequestGuard;
use Illuminate\Foundation\Testing\TestCase as BaseTestCase;
abstract class TestCase extends BaseTestCase
{
use CreatesApplication;
protected function setUp(): void
{
parent::setUp();
// add logout method to RequestGuard
RequestGuard::macro('logout', function() {
$this->user = null;
});
}
// add method to base TestCase class
public function actingAsGuest(): void
{
$this->app['auth']->logout();
}
}
And then in your test class you can use it:
<?php
namespace Tests\Feature;
use App\Models\User;
use Tests\TestCase;
class ExampleTest extends TestCase
{
/**
* A basic test example.
*
* #return void
*/
public function test_example()
{
// acting as authenticated user
$this->actingAs(User::factory()->create());
$this->assertAuthenticated();
// acting as unauthenticated user
$this->actingAsGuest();
$this->assertGuest();
}
}
I had same requirements as OP did, but wanted actingAsGuest() to completely reset everything, except Database state.
Full App reset (except DB)
For Laravel 7 (and maybe newer or older)
I toke a look at Laravel's tearDown() and setUp() methods.
And came up with helper method like:
use Illuminate\Support\Facades\Facade;
// ...
function actingAsGuest()
{
// Backup database state.
/** #var \Illuminate\Database\MySqlConnection $connection */
$connection = app('db.connection');
// Reset everything else.
/** #var \Illuminate\Foundation\Application $app */
$app = $this->app;
$app->flush();
$this->app = null;
Facade::clearResolvedInstances();
$this->refreshApplication();
// Restore database state.
app('db')->extend($connection->getName(), function () use ($connection) {
return $connection;
});
}
WARNING !!
Above works fine unless your test's logic caches any of above discarded objects somewhere.
For example, Laravel's DatabaseTransactions trait did cache db facade (in their App-Destroyed-listener).
Which we fixed by overriding said trait's logic.
Like we changed:
// ...
$this->beforeApplicationDestroyed(function () use ($database) {
// ...
Into:
// ...
$this->beforeApplicationDestroyed(function () {
$database = app('db');
// ...

PhpStorm Laravel Dusk w/ a testing database

I currently have PhpStorm running Dusk Test successfully however, I would like it to use the testing database I have set up. Per other threads and resources online, I have created the .env.dusk.local and phpunit.dusk.xml that points to the testing database I have created. When I run the dusk tests in PhpStorm the application that is rendered in Chromium doesn't use the testing database that is described in these files but when I run them using php artisan dusk in the terminal it uses the correct databases.
It seems like I need make phpstorm aware of what env file to use when running the tests. Any clues on how to make this work.
If you're running the tests using artisan dusk, make sure that the APP_ENV setting you are running Dusk in matches the .env.dusk.[environment] setting.
The Dusk browser instance always use the current .env file so...
From the Laravel Dusk docs:
When running tests, Dusk will back-up your .env file and rename your Dusk environment to .env. Once the tests have completed, your .env file will be restored.
If you're not running the artisan dusk command to run your Dusk tests, I suspect that you would have to do something similar to this code before and after running the test suite:
https://github.com/laravel/dusk/blob/2.0/src/Console/DuskCommand.php#L136
If you get this working I'd be very interested in how you did it.
I found this article that works well and describes what the issue is.
https://harings.be/running-laravel-dusk-tests-from-phpstorm-atf2v
tests/DuskTestCase.php
tests/DuskTestCase.php
<?php
namespace Tests;
use Dotenv\Dotenv;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Laravel\Dusk\TestCase as BaseTestCase;
use Facebook\WebDriver\Chrome\ChromeOptions;
use Facebook\WebDriver\Remote\RemoteWebDriver;
use Facebook\WebDriver\Remote\DesiredCapabilities;
abstract class DuskTestCase extends BaseTestCase
{
use CreatesApplication;
use DatabaseMigrations;
public static function basePath($path = '')
{
return __DIR__ . '/../' . ($path ? DIRECTORY_SEPARATOR . $path : $path);
}
/**
* Prepare for Dusk test execution.
*
* #beforeClass
* #return void
*/
public static function prepare()
{
static::startChromeDriver();
}
public static function setUpBeforeClass()
{
copy(self::basePath('.env'), self::basePath('.env.backup'));
copy(self::basePath('.env.dusk.local'), self::basePath('.env'));
(new Dotenv(self::basePath()))->overload();
parent::setUpBeforeClass();
}
public static function tearDownAfterClass(): void
{
copy(self::basePath('.env.backup'), self::basePath('.env'));
unlink(self::basePath('.env.backup'));
(new Dotenv(self::basePath()))->overload();
parent::tearDownAfterClass();
}
/**
* Create the RemoteWebDriver instance.
*
* #return \Facebook\WebDriver\Remote\RemoteWebDriver
*/
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
)
);
}
}
You need to add to DuskTestCase.php something like this:
/**
* #beforeClass
* #return void
*/
public static function prepare()
{
//static::startChromeDriver();
copy(base_path('.env'), base_path('.env.backup'));
copy(base_path('.env.dusk.local'), base_path('.env'));
(new Dotenv(base_path()))->overload();
}
/**
* #afterClass
* #return void
*/
public static function finish()
{
copy(base_path('.env.backup'), base_path('.env'));
unlink(base_path('.env.backup'));
(new Dotenv(base_path()))->overload();
}
Thx Andriy, I improved your code, this works for me :
use Dotenv\Dotenv;
public static function basePath($path = '') {
return __DIR__. '/../' . ($path ? DIRECTORY_SEPARATOR.$path : $path);
}
/**
* Prepare for Dusk test execution.
*
* #beforeClass
* #return void
*/
public static function prepare()
{
copy(DuskTestCase::basePath('.env'), DuskTestCase::basePath('.env.backup'));
copy(DuskTestCase::basePath('.env.dusk.local'), DuskTestCase::basePath('.env'));
(new Dotenv(DuskTestCase::basePath()))->overload();
static::startChromeDriver();
}
public static function closeAll()
{
copy(DuskTestCase::basePath('.env.backup'), DuskTestCase::basePath('.env'));
unlink(DuskTestCase::basePath('.env.backup'));
(new Dotenv(DuskTestCase::basePath()))->overload();
return parent::closeAll();
}
..since base_path() and finish() weren't working in this DuskTestCase class
This worked for me
<?php
namespace Tests;
use Dotenv\Dotenv;
use Facebook\WebDriver\Chrome\ChromeOptions;
use Facebook\WebDriver\Remote\DesiredCapabilities;
use Facebook\WebDriver\Remote\RemoteWebDriver;
use Laravel\Dusk\TestCase as BaseTestCase;
abstract class DuskTestCase extends BaseTestCase
{
use CreatesApplication;
public static function basePath($path = '') {
return __DIR__. '/../' . ($path ? DIRECTORY_SEPARATOR.$path : $path);
}
// [ ... ]
public static function setUpBeforeClass(): void
{
if (file_get_contents(self::basePath('.env')) !== file_get_contents(self::basePath('.env.dusk.local'))) {
copy(self::basePath('.env'), self::basePath('.env.backup'));
}
copy(self::basePath('.env.dusk.local'), self::basePath('.env'));
Dotenv::createMutable(self::basePath())->load();
parent::setUpBeforeClass();
}
public static function tearDownAfterClass(): void
{
copy(self::basePath('.env.backup'), self::basePath('.env'));
unlink(self::basePath('.env.backup'));
Dotenv::createMutable(self::basePath())->load();
parent::tearDownAfterClass();
}
// [ ... ]
}
Found it at https://github.com/laravel/dusk/issues/883

Selenium and Laravel 5.2

I'm getting sad,
I use Laravel 5.2 and I am developping my unit tests.
In Laravel 5.1, you could use the great Integrated lib to use selenium, but it doesn't seem to work in Laravel 5.2
So basically, Is there any kind of integration between L5.2 and Selenium, or is it imposible to use it nicely?
In this case, I should definitively have stayed in L5.1 as testing is a fundamental part of my app :(
You need to install PHPUnit_selenium package using composer
composer require --dev phpunit/phpunit-selenium
Create Selenium Test Case class inside laravel/tests/
<?php
class SeleniumTestCase extends PHPUnit_Extensions_Selenium2TestCase
{
/**
* The base URL to use while testing the application.
*
* #var string
*/
protected function setUp()
{
$this->setBrowser('firefox');
$this->setBrowserUrl('http://localhost:8000/');
}
protected function visit($path)
{
$this->url($path);
return $this;
}
protected function see($text, $tag = 'body')
{
print_r(request()->session()->all());
//method call by tag name;
$this->assertContains($text,$this->byTag($tag)->text());
return $this;
}
protected function pressByName($text){
$this->byName($text)->click();
return $this;
}
protected function pressByTag(){
$this->byTag('button')->click();
return $this;
}
protected function type($value, $name)
{
$this->byName($name)->value($value);
return $this;
}
protected function hold($seconds){
sleep($seconds);
return $this;
}
}
and Create new test case for visiting home page url
<?php
class ExampleTest extends SeleniumTestCase
{
/**
* A basic functional test example.
*
* #return void
*/
public function testTitle()
{
$this->visit('/')
->see('Site title','title');
}
}
and Run command PHPunit test from terminal
java -jar /usr/local/bin/selenium-server-standalone-2.35.0.jar
Reference document:
https://gist.github.com/dhavalv/85cd0e8a9a5355543787f882dca0b7cf
https://www.leaseweb.com/labs/2013/09/testing-your-project-with-phpunit-and-selenium/
https://www.sitepoint.com/using-selenium-with-phpunit/

How to schedule Artisan commands in a package?

I have a package that contains Artisan commands. I’ve registered these commands with Artisan via my service provider like so:
/**
* Register the application services.
*
* #return void
*/
public function register()
{
// Register Amazon Artisan commands
$this->commands([
'App\Marketplace\Amazon\Console\PostProductData',
'App\Marketplace\Amazon\Console\PostProductImages',
'App\Marketplace\Amazon\Console\PostProductInventory',
'App\Marketplace\Amazon\Console\PostProductPricing',
]);
}
However, these commands need to be scheduled to run daily.
I know in app/Console/Kernel.php there is the schedule() method where you can register commands and their frequency, but how can I schedule commands in my package’s service provider instead?
(see Dave's answer below for Laravel 6.10+)
The trick is to wait until after the Application has booted to schedule the commands, since that is when Laravel defines the Schedule instance and then schedules commands internally. Hope this saves someone a few hours of painful debugging!
use Illuminate\Support\ServiceProvider;
use Illuminate\Console\Scheduling\Schedule;
class ScheduleServiceProvider extends ServiceProvider
{
public function boot()
{
$this->app->booted(function () {
$schedule = $this->app->make(Schedule::class);
$schedule->command('some:command')->everyMinute();
});
}
public function register()
{
}
}
In Laravel 6.10 and above:
use Illuminate\Support\ServiceProvider;
use Illuminate\Console\Scheduling\Schedule;
class ScheduleServiceProvider extends ServiceProvider
{
public function boot()
{
$this->callAfterResolving(Schedule::class, function (Schedule $schedule) {
$schedule->command('some:command')->everyMinute();
});
}
public function register()
{
}
}

Create a Custom Controller by artisan command laravel 5

I am developing a package, I need to create a command for it which creates a controller the code looks like this:
class MyCommand extends Command {
protected $name = 'package:mycommand';
public function __construct()
{
parent::__construct();
}
public function fire()
{
$this->call('vendor:publish');
$argName = $this->argument('name');
$this->call('make:controller', ['name' => $argName.'Controller']);
}
This command works and it creates a controller which looks like this:
class wwController extends Controller {
public function index()
{
}
}
and also some other functions are there but their are not implemented,
Question:
Is there anyway to create a custom controller which extends from one of the controller in my package, let's say MyController and also has the functions of MyController
or if this is not possible with this command, is there any other command suitable for this purpose?
I need to create a controller with command like this:
class newController extends MyController {
public function myFunction()
{
}
}
If you want to scaffold a controller you may want to checkout Laracast/Generator project for an example of how this sort of thing could be done.
In the MigrationMakeCommand.php Jeffrey (I assume the author here) creates a command that generates a migration file based on some command line parameters you pass into that command. This is fairly similar to what you're trying to do.
As far as I can tell you'll wan to inject the file system and composer as the author did on line 56
/**
* Create a new command instance.
*
* #param Filesystem $files
* #param Composer $composer
*/
public function __construct(Filesystem $files, Composer $composer)
{
parent::__construct();
$this->files = $files;
$this->composer = $composer;
}
Then you'll want to pay particularly close attention to the chain method calls on line 156 that's pretty close to the steps you'll need to take to make a new controller file.
protected function compileMigrationStub()
{
$stub = $this->files->get(__DIR__ . '/../stubs/migration.stub');
$this->replaceClassName($stub)
->replaceSchema($stub)
->replaceTableName($stub);
return $stub;
}

Resources