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() {
...
}
Related
I´m trying to create $schedule job in Laravel to read Email with PHP IMAP package. If I go to route, package read email and does all correctly, but need to do this every 5 minutes.
I create a new command class and add this
use Illuminate\Http\Request;
class ReadMail extends Command implements SelfHandling {
protected $name = 'read:mail';
/**
* Create a new command instance.
*
* #return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the command.
*
* #return void
*/
public function fire()
{
$request = Request::create($this->option('App\Http\Controllers\MailController#index'), 'GET');
$this->info(app()['Illuminate\Contracts\Http\Kernel']->handle($request));
}
In kernel
protected $commands = [
'App\Console\Commands\ReadMail',
];
protected function schedule(Schedule $schedule)
{
$schedule->call('read:mail')
->everyFiveMinutes();
}
I'm not sure if this code it´is correct, but does not work properly. Any idea about it?
Thank in advance for your help.
UPDATE
I launch this
php artisan read:mail and return
Argument 1 passed to Illuminate\Console\Application::add() must be an instance of Symfony\Component\Console\Command\Command, instance of App\Commands\ReadMail given
The code of ReadMail class
<?php namespace App\Commands;
use App\Commands\Command;
use Illuminate\Contracts\Bus\SelfHandling;
use Illuminate\Http\Request;
class ReadMail extends Command implements SelfHandling {
protected $signature = 'read:mail';
/**
* Create a new command instance.
*
* #return void
*/
public function __construct()
{
//
}
/**
* Execute the command.
*
* #return void
*/
public function fire()
{
$request = Request::create($this->option('App\Http\Controllers\MailController#index'), 'GET');
$this->info(app()['Illuminate\Contracts\Http\Kernel']->handle($request));
}
}
UPDATE 2: SOLVED - ALL CODE
Kernel
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
class Kernel extends ConsoleKernel {
/**
* The Artisan commands provided by your application.
*
* #var array
*/
protected $commands = [
'App\Commands\ReadMail',
];
/**
* Define the application's command schedule.
*
* #param \Illuminate\Console\Scheduling\Schedule $schedule
* #return void
*/
protected function schedule(Schedule $schedule)
{
$schedule->command('read:mail')
->everyFiveMinutes();
}
}
ReadMail
<?php namespace App\Commands;
use Illuminate\Contracts\Bus\SelfHandling;
use Illuminate\Http\Request;
use Illuminate\Console\Command;
use App\Http\Controllers\MailController;
class ReadMail extends Command implements SelfHandling {
protected $name = 'read:mail';
/**
* Create a new command instance.
*
* #return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the command.
*
* #return void
*/
public function handle()
{
MailController::index();
}
}
MailController
<?php namespace App\Http\Controllers;
use App\Http\Requests;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use PhpImap\Mailbox as ImapMailbox;
use App\Models\Escalas;
class MailController extends Controller {
public static function index()
{
$mailbox = new ImapMailbox('{imap.gmail.com:993/imap/ssl}INBOX', '', '','');
$mailsIds = $mailbox->searchMailbox('UNSEEN');
if(!$mailsIds) {
die('Mailbox is empty');
}
$mail=[];
foreach ($mailsIds as $index=>$data){
$mail[]=$mailbox->getMail($mailsIds[$index]);
Escalas::insert([
['' => $mail[$index]->textPlain,
'' => $mail[$index]->date,
''=>$mail[$index]->subject,
''=>$mail[$index]->fromName,
''=>$mail[$index]->fromAddress,
''=>$mail[$index]->toString],
]);
}
}
}
Try changing
protected $name = 'read:mail';
with protected $signature= 'read:mail';
in your ReadMail class and then run in Kernel like this
$schedule->command('read:mail')->everyFiveMinutes();
Check whether your scheduled tasks are set up properly (see docs for Laravel 5.0) Make sure you've added a cronjob to trigger Laravel's scheduled commands. To check you cronjobs are running, look for a file like /logs/crond.log. When opening this file, you should see lines showing at what times this ran. This is the command that triggers Laravel's scheduled jobs.
If all that is correct, then try running your command via your terminal on localhost to check the command is all set. This should reveal any problems with the command setup. Your functionality itself seems allright, since you mentioned everything works when triggered via a route.
I am using Laravel 5.1 and I have the following test class where I am using some model factories in the setUp() method to temporarily seed the database.
class PendingUserWithManualStudentDetailsTest extends TestCase
{
use DatabaseMigrations, WithoutMiddleware;
public function setUp()
{
parent::setUp();
$this->studentDetail = factory(StudentDetail::class)->create();
// ... more factories
}
// ... some tests
}
When running the test I get the following error:
General error: 1 no such table: student_details
When I move the factory calls into a test method they execute just fine. So I am guessing that the order in which the setUp() and my test methods are called are causing the issue. Is there any way I can fix this?
This is my testcase class:
class TestCase extends Illuminate\Foundation\Testing\TestCase
{
/**
* The base URL to use while testing the application.
*
* #var string
*/
protected $baseUrl = 'http://myapp.dev';
/**
* Creates the application.
*
* #return \Illuminate\Foundation\Application
*/
public function createApplication()
{
$app = require __DIR__.'/../bootstrap/app.php';
$app->make(Illuminate\Contracts\Console\Kernel::class)->bootstrap();
$app->setLocale('en');
return $app;
}
}
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);
}
}
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');
}
I am using Laravel 5.0 to create phpunit test alongside the actual model.
I get errors in phpunit tests but no errors when controller calls the model and it returned the desired data.
sample.php
<?php namespace App;
use Illuminate\Database\Eloquent\Model;
class sample extends Model {
/**
* The database table used by the model.
*
* #var string
*/
protected $table = 'sample';
/**
* The attributes that are mass assignable.
*
* #var array
*/
protected $fillable = ['id','username','details','image'];
/**
* The attributes excluded from the model's JSON form.
*
* #var array
*/
public static function test()
{
return "Returned Text.";
}
public static function gettest()
{
return self::test();
}
public static function getItem()
{
return self::orderBy('username','asc')->get();
}
public static function findItem($id)
{
return self::find($id);
}
}
SampleTest.php
<?php namespace App;
use Mockery as m;
class SampleTest extends \PHPUnit_Framework_TestCase {
protected function setUp()
{
$this->mock = m::mock('App\sample')->makePartial();
}
protected function tearDown()
{
m::close();
}
/** #test */
public function should_return_string()
{
$response = $this->mock->test();
var_dump("test() returns :".$response);
}
/** #test */
public function should_return_string_from_test_function()
{
$response = $this->mock->gettest();
var_dump("gettest() returns :".$response);
}
/** #test */
public function should_return_mocked_data()
{
$this->mock->shouldReceive('test')->andReturn('Return Mocked Data');
$response = $this->mock->gettest();
var_dump("gettest() returns :".$response);
}
/** #test */
public function should_return_some_data_using_this_mock()
{
$this->mock->shouldReceive('get')->andReturn('hello');
$response = $this->mock->getItem();
}
}
Problem
When I use controller to call the model, it returned the desired data.
When I run phpunit on command prompt:-
test function is not mocked properly as it still returns the original string
getItem and findItem functions return an error saying
1) App\SampleTest::should_return_some_data_using_this_mock
BadMethodCallException: Static method Mockery_0_App_sample::getItem()
does not exist on this mock object
Question
How can I mock the function properly? Why it is saying the error code as shown above? Where was I doing it wrong?
Any help will be much appreciated.
Note: Test assertions is removed and replaced with var_dump to see the output on the command prompt.