Testing/Mocking Custom Console commands - php

I constructed my own simple command, that I want to test.
which basically looks like this:
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;
class NeatCommand extends Command
{
protected $signature = 'my:neat:command {input_file : path for a json-formatted file to analyze}';
protected $description = 'analyze an array of values';
public function __construct()
{
parent::__construct();
}
public function handle()
{
$inputFile=$this->argument('input_file');
echo $inputFile;
}
}
So I wrote this simple test:
/**
* #test
* #group neat
*/
public function commandShowsHelloWorld()
{
$defaultCommand=Artisan::call('my:neat:command');
}
I simple want to test at this stage: there are arguments missing. But when I run it right now, phpunit brings it as an error:
There was 1 error:
1) App\Console\Commands\NeatCommandTest::commandShowsHelloWorld
RuntimeException: Not enough arguments.
So my question is. How can i mock the whole thing...or say something like $this->shouldReturn('RuntimeException: Not enough arguments.'); ?

Got it working but adding the #annotation of expected exceptions.
/**
* #test
* #group neat
* #expectedException RuntimeException
* #expectedExceptionMessage Not enough arguments.
*/
public function commandWithoutArgumentsCausesError()
{
$defaultCommand=Artisan::call('my:neat:command');
}

Related

how to hold all artisan command laravel

How to disable all artisan command?
i try with create parent class and extend that class to my artisan commands.
Parent
namespace App\Extenders;
use Illuminate\Console\Command AS BaseCommand;
class Command extends BaseCommand {
public function __construct()
{
parent::__construct();
dump('command disabled');
exit;
}
}
My artisan command:
namespace App\Console\Commands;
use App\Extenders\Command;
class HoldAllArtisanCommand extends Command
{
protected $signature = 'command';
protected $description = 'Command description';
public function __construct()
{
parent::__construct();
}
public function handle()
{
//
}
}
With this code the command not running but all command that not extend my custom parent command is not running as well
I don't know why you want to disable your commands entirely but here is how I would do it:
First I would create an environment variable called COMMANDS_DISABLED and then create a config for it inside config/app.php and call it commands_disabled. OR just create a setting on the database so that I you enable and disable commands as you like.
You can create a BaseCommand class that extends the Illuminate\Console\Command like so:
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
class BaseCommand extends Command
{
public function __construct()
{
if (config('app.commands_disabled')) {
dump('command disabled');
exit;
}
}
}
Then for all your custom commands inside app/Console/Commands you have to extend from this BaseCommand like so:
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
class TestCommand extends BaseCommand
{
/**
* The name and signature of the console command.
*
* #var string
*/
protected $signature = 'testing';
/**
* The console command description.
*
* #var string
*/
protected $description = 'Command description';
/**
* Execute the console command.
*
* #return int
*/
public function handle()
{
return 0;
}
}
now when you run php artisan testing it will show the message command disabled.
And if you want to enable your custom commands again you can remove the constructor code from the BaseCommand class.

Laravel 8: Test MainClassTest from a ChildClassTest

How to test the MainClassTest Methods from a ChildClassTest extending it, since the child class will perform the same test scenarios but given with different data
Sample of code
<?php
namespace Tests\Feature\Modules;
use Tests\TestCase;
class BaseControllerTest extends TestCase
{
/**
* A basic feature test example.
*
* #return void
*/
public function test_common_method_for_each_child()
{
// process
}
}
<?php
namespace Tests\Feature\Modules;
use Tests\TestCase;
class ChildControllerTest extends BaseControllerTest {
public function test_unique_method() {
// process
}
}
<?php
namespace Tests\Feature\Modules;
use Tests\TestCase;
class Child1ControllerTest extends BaseControllerTest {
public function test_unique_method() {
// process
}
}
So when run the php artisan test, should see a list of test
ChildControllerTest->test_common_method_for_each_child
ChildControllerTest->test_unique_method
Child1ControllerTest->test_common_method_for_each_child
Child1ControllerTest->test_unique_method
The tests inside the parent will be ran when you'll run your child class tests, what you did should already work.
If that's not the case, can you edit your question with the PHPUnit command you are running and its output?
However, you should make your base class abstract:
abstract class BaseControllerTest extends TestCase
{
/**
* A basic feature test example.
*
* #return void
*/
public function test_common_method_for_each_child()
{
// process
}
}
If you don't do this, this class will also be called alone, out of the context of a child, during the test suite (which doesn't seem to be what your goal).

PHPUnit|Laravel phpunit running only the first test of class test

Currently, I am trying to run some simple unit tests with phpunit on a laravel project with the command line phpunit tests/Unit/ExampleTest. But it is only running the first method. Anyone has a solution ? Thank you !
namespace Tests\Unit;
use PHPUnit\Framework\TestCase;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Foundation\Testing\DatabaseTransactions;
class ExampleTest extends TestCase
{
/**
* A basic test example.
*
* #return void
*/
public function testBasicTest()
{
$this->assertTrue(true);
}
/**
* An other basic test
*
* #return void
*/
public function secondTestBasicTest()
{
$this->assertTrue(true);
}
public function basicTest(){
$response = $this->action('GET', 'DepartementsController#display', ['id' => 1]);
$view = $response->original;
$this->assertEquals(1, $view['$dpt->iddepartement']);
}
}
PHPUnit by default launches only methods beginning with test prefix, so secondTestBasicTest and basicTest do not fall under that condition. You can explicitly annotate your tests as #test in PHPDoc to override this behavior:
/**
* #test
*/
public function basicTest() {
...
}

How to seed database migrations for laravel tests?

Laravel's documentation recommends using the DatabaseMigrations trait for migrating and rolling back the database between tests.
use Illuminate\Foundation\Testing\DatabaseMigrations;
class ExampleTest extends TestCase
{
use DatabaseMigrations;
/**
* A basic functional test example.
*
* #return void
*/
public function testBasicExample()
{
$response = $this->get('/');
// ...
}
}
However, I've got some seed data that I would like to use with my tests. If I run:
php artisan migrate --seed
then it works for the first test, but it fails subsequent tests. This is because the trait rolls back the migration, and when it runs the migration again, it doesn't seed the database. How can I run the database seeds with the migration?
All you need to do is make an artisan call db:seed in the setUp function
<?php
use Illuminate\Foundation\Testing\DatabaseMigrations;
class ExampleTest extends TestCase
{
use DatabaseMigrations;
public function setUp(): void
{
parent::setUp();
// seed the database
$this->artisan('db:seed');
// alternatively you can call
// $this->seed();
}
/**
* A basic functional test example.
*
* #return void
*/
public function testBasicExample()
{
$response = $this->get('/');
// ...
}
}
ref: https://laravel.com/docs/5.6/testing#creating-and-running-tests
With Laravel 8, if you're using the RefreshDatabase trait you can invoke seeding from your test case using below:
use Illuminate\Foundation\Testing\RefreshDatabase;
class ExampleTest extends TestCase
{
use RefreshDatabase;
/**
* A basic functional test example.
*
* #return void
*/
public function testBasicExample()
{
// Run the DatabaseSeeder...
$this->seed();
// Run a specific seeder...
$this->seed(OrderStatusSeeder::class);
$response = $this->get('/');
// ...
}
}
see docs for more information/examples:
https://laravel.com/docs/8.x/database-testing#running-seeders
It took me some digging to figure this out, so I thought I'd share.
If you look at the source code for the DatabaseMigrations trait, then you'll see it has one function runDatabaseMigrations that's invoked by setUp which runs before every test and registers a callback to be run on teardown.
You can sort of "extend" the trait by aliasing that function, re-declare a new function with your logic in it (artisan db:seed) under the original name, and call the alias inside it.
use Illuminate\Foundation\Testing\DatabaseMigrations;
class ExampleTest extends TestCase
{
use DatabaseMigrations {
runDatabaseMigrations as baseRunDatabaseMigrations;
}
/**
* Define hooks to migrate the database before and after each test.
*
* #return void
*/
public function runDatabaseMigrations()
{
$this->baseRunDatabaseMigrations();
$this->artisan('db:seed');
}
/**
* A basic functional test example.
*
* #return void
*/
public function testBasicExample()
{
$response = $this->get('/');
// ...
}
}
I know this question has already been answered several times, but I didn't see this particular answer so I thought I'd throw it in.
For a while in laravel (at least since v5.5), there's been a method in the TestCase class specifically used for calling a database seeder:
https://laravel.com/api/5.7/Illuminate/Foundation/Testing/TestCase.html#method_seed
with this method, you just need to call $this->seed('MySeederName'); to fire the seeder.
So if you want this seeder to fire before every test, you can add the following setUp function to your test class:
public function setUp()
{
parent::setUp();
$this->seed('MySeederName');
}
The end result is the same as:
$this->artisan('db:seed',['--class' => 'MySeederName'])
or
Artisan::call('db:seed', ['--class' => 'MySeederName'])
But the syntax is a bit cleaner (in my opinion).
With Laravel 8, the RefreshDatabase is now looking for a boolean property called "seed".
/**
* Illuminate\Foundation\Testing\RefreshDatabase
* Determine if the seed task should be run when refreshing the database.
*
* #return bool
*/
protected function shouldSeed()
{
return property_exists($this, 'seed') ? $this->seed : false;
}
Simply give your test class the protected property $seed and set it to true if you wish to seed.
class ProjectControllerTest extends TestCase
{
protected $seed = true;
public function testCreateProject()
{
$project = Project::InRandomOrder()->first();
$this->assertInstanceOf($project,Project::class);
}
The nice part about this method is that individual tests won't seed everytime they are ran. Only seed necessary test will build the database.
If you're using the RefreshDatabase testing trait:
abstract class TestCase extends BaseTestCase
{
use CreatesApplication, RefreshDatabase {
refreshDatabase as baseRefreshDatabase;
}
public function refreshDatabase()
{
$this->baseRefreshDatabase();
// Seed the database on every database refresh.
$this->artisan('db:seed');
}
}
Here is an alternate solution, in case you prefer to bypass Artisan's native DatabaseMigrations and seeder/migration methods. You can create your own trait to seed your database:
namespace App\Traits;
use App\Models\User;
use App\Models\UserType;
trait DatabaseSetup
{
public function seedDatabase()
{
$user = $this->createUser();
}
public function createUser()
{
return factory(User::class)->create([
'user_type_id' => function () {
return factory(UserType::class)->create()->id;
}
]);
}
public function getVar() {
return 'My Data';
}
}
Then call it in your test like this:
use App\Traits\DatabaseSetup;
class MyAwesomeTest extends TestCase
{
use DatabaseSetup;
use DatabaseTransactions;
protected $reusableVar;
public function setUp()
{
parent::setUp();
$this->seedDatabase();
$this->reusableVar = $this->getVar();
}
/**
* #test
*/
public function test_if_it_is_working()
{
$anotherUser = $this->createUser();
$response = $this->get('/');
$this->seeStatusCode(200);
}
}

Run routing method from console

I want to be able to run a controller's method from URL, and from the console. How can I do that? I mean, having a method in some controller:
/**
* #Route("/fooBar", name="fooBar")
*/
public function actionFooBar() {
$this -> get('file') -> saveSomethingToSomeFile();
return 'a';
}
I want to be able to open it via http://domain.com/fooBar and php app/console fooBar, or something like this.
The console one doesn't work. How can I solve this?
What you want is (I think) technically doable, but not good practice.
You should move the code in your controller method to a service, then you can run that same code from both your command and your controller.
You need to build a Command:
<?php
namespace Application\CommandBundle\Command;
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Doctrine\ORM\EntityManager;
/**
* Class testCommand
* #package Application\CommandBundle\Command
*/
class TestCommand extends ContainerAwareCommand
{
/**
* Configuration of the command
*/
protected function configure()
{
$this
->setName('command:do:something')
->setDescription('This command does something');
}
protected function initialize(InputInterface $input, OutputInterface $output)
{
}
/**
* #param InputInterface $input An InputInterface instance
* #param OutputInterface $output An OutputInterface instance
*
* #return null|int null or 0 if everything went fine, or an error code
*/
protected function execute(InputInterface $inputInterface, OutputInterface $outputInterface)
{
$outputInterface->writeln(
'This command does something <info>' . $inputInterface->getOption('env') . '</info> environment'
);
$this->getContainer()->get('application_command.test')->doSomething();
$outputInterface->writeln('Done');
}
}
More info with details: https://extractcode.com

Categories