In my Lumen app, when I execute
php artisan migrate --seed
it works well.
But when I try to run my tests with phpunit, it doesn't run migration from a Laravel package that I coded, so all tests fail
I run my migrations in my test with :
Artisan::call('migrate');
I use in memory testing for faster running.
Here is my Lumen app Testcase.php
abstract class TestCase extends Laravel\Lumen\Testing\TestCase
{
/** #var array */
protected $dispatchedNotifications = [];
protected static $applicationRefreshed = false;
/**
* Creates the application.
*
* #return \Laravel\Lumen\Application
*/
/**
* Creates the application.
*
*/
public function createApplication()
{
return self::initialize();
}
private static $configurationApp = null;
public static function initialize()
{
$app = require __DIR__ . '/../bootstrap/app.php';
if (is_null(self::$configurationApp)) {
$app->environment('testing');
if (config('database.default') == 'sqlite') {
$db = app()->make('db');
$db->connection()->getPdo()->exec("pragma foreign_keys=1");
}
Artisan::call('migrate');
Artisan::call('db:seed');
self::$configurationApp = $app;
}
return $app;
}
/**
* Refresh the application instance.
*
* #return void
*/
protected function forceRefreshApplication()
{
if (!is_null($this->app)) {
$this->app->flush();
}
$this->app = null;
self::$configurationApp = null;
self::$applicationRefreshed = true;
parent::refreshApplication();
}
...
In my package, I use in the boot method of service provider:
$this->loadMigrationsFrom(__DIR__.'/../database/migrations');
and then a test example:
class TournamentsTest extends TestCase
{
use DatabaseTransactions, AttachJwtToken;
protected $initialTournamentNum = 6;
protected $defaultPagintation = 25;
protected $user;
/** #test */
public function user_can_see_tournament_list()
{
$response = $this
->call('GET', '/tournaments');
$this->assertEquals(HttpResponse::HTTP_OK, $response->status());
}
...
All my test fail with:
PDOException: SQLSTATE[HY000]: General error: 1 no such table: ken_venue
ken_venue is a table that come from the laravel package
In fact, I have this same package working well in a Laravel 5.7 application. but I am migrating this app to a Lumen app.
Any idea why is it happening ?
A few remarks first:
Your test function does not start with test, for me in Laravel such tests would not execute.
Second try this to run the seeds instead of calling migrate and seed by hand
namespace Tests\Unit;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Tests\TestCase;
class ShippingCostTest extends TestCase
{
use DatabaseMigrations; // call migrations this way
public function testDoesApply() // start your function with the word 'test'
$this->assertTrue(true); // call assert functions
}
protected function setUp() // use this function for setup
{
parent::setUp();
$this->seed(); // call this for seeding
}
}
Third: do you use the same databast type (e.g. MySQL in both cases), because of you use sqlite for testing the syntax might break all of the sudden because of differences between systems.
Related
Followed every step from laravel 9 manual, and my service will not register consistently. Don't quite know why but it suddenly breaks and I receive a Target class [thumbnail] does not existerror out of nowhere.
Setup
Service:
namespace App\Services;
use Illuminate\Support\Facades\Storage;
use Intervention\Image\Facades\Image;
class ThumbnailService
{
public function storeThumbnail($userId, $file, $scale) {
$thumbnail_image = Image::make($file);
$orig_width = $thumbnail_image->width();
$orig_height = $thumbnail_image->height();
$thumbnail_image->resize($orig_width * $scale, $orig_height * $scale);
$thumb_stream = $thumbnail_image->stream();
$thumb_path = env('MIX_AWS_IMAGES_DIRECTORY') . '/'. $userId . '/thumbnails';
$thumb_file = $thumb_path . '/' . 'thumb_1_4_' . $file->getClientOriginalName();
Storage::disk('s3')->put($thumb_file, $thumb_stream->__toString());
return $thumb_file;
}
}
Provider:
namespace App\Providers;
use App\Services\ThumbnailService;
use Illuminate\Contracts\Support\DeferrableProvider;
use Illuminate\Support\ServiceProvider;
class ThumbnailServiceProvider extends ServiceProvider implements DeferrableProvider
{
/**
* Register services.
*
* #return void
*/
public function register()
{
$this->app->singleton('thumbnail', function() {
return new ThumbnailService();
});
}
/**
* Bootstrap services.
*
* #return void
*/
public function boot()
{
}
public function provides()
{
return [ThumbnailService::class];
}
}
Facade:
namespace App\Facades;
use Illuminate\Support\Facades\Facade;
class Thumbnail extends Facade
{
protected static function getFacadeAccessor() {
return 'thumbnail';
}
}
obviously, I did register in app.php in the providers array. But if I inspect the bindings, there is no service... Any help? This is the second time this happened and earlier, I got away with dumping autoload and config:clear and it is currently under my sail dev environment. I don't want this to happen in prod. So help would be appreciated.
I have a very simple example to show the problem:
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
class VendorCounts extends Command
{
/**
* The name and signature of the console command.
*
* #var string
*/
protected $signature = 'vendor:counts
{year : The year of vendor counts}';
/**
* The console command description.
*
* #var string
*/
protected $description = 'Runs vendor counts';
/**
* Create a new command instance.
*
* #return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* #return mixed
*/
public function handle()
{
$this->info('Starting Vendor Counts');
}
}
<?php
namespace Tests\Feature\Console\Vendor;
use Tests\TestCase;
class VendorCountsTest extends TestCase {
public function testVendorCounts()
{
$this->artisan('vendor:counts', ['year' => 2019])
->expectsOutput('Starting Vendor Counts')
->assertExitCode(0);
}
}
I get the following error:
1) Tests\Feature\Console\Vendor\VendorCountsTest::testVendorCounts
Error: Call to a member function expectsOutput() on integer
/Users/albertski/Sites/vrs/tests/Feature/Console/Vendor/VendorCountsTest.php:12
I know the command definitely runs because if I put a dump statement in it shows the debug output.
I am using Laravel 6.3. Is there a different way to test this?
The problem I was using was that TestCase was using Laravel\BrowserKitTesting\TestCase as BaseTestCase. I ended up creating another Base just for console commands.
<?php
namespace Tests;
use Illuminate\Foundation\Testing\TestCase as BaseTestCase;
abstract class ConsoleTestCase extends BaseTestCase
{
use CreatesApplication;
}
Can you add this to your VendorCountsTest class:
public $mockConsoleOutput = true;
This is set by a trait but just making sure something hasn't changed the value. When $mockConsoleOutput is false it will directly run the artisan commmand. When it is true it will wrap it in a PendingCommand object that has those methods you are trying to call.
I had an issue where the use of expectedOutput() on my Artisan class would fail all the time, which turned out to be because I had used exit() and/or die() in a method, which really did not work well with phpunit test methods.
So if you want to stop processing the "script" at some point, just use an empty return and not exit() or die() if you want to utilize the built-in ->artisan() testing in Laravel.
Working example:
<?php
// app/Console/Commands/FooCommand.php
public function handle()
{
$file = $this->argument('file');
if (! file_exists($file)) {
$this->line('Error! File does not exist!');
return;
}
}
// tests/Feature/FooCommandTest.php
public function testFoo() {
$this->artisan('foo', ['file' => 'foo.txt'])->expectsOutput('Something');
}
Non-working example:
<?php
// app/Console/Commands/FooCommand.php
public function handle()
{
$file = $this->argument('file');
if (! file_exists($file)) {
$this->line('Error! File does not exist!');
exit;
}
}
// tests/Feature/FooCommandTest.php
public function testFoo() {
$this->artisan('foo', ['file' => 'foo.txt'])->expectsOutput('Something');
}
I am finding a way to somewhat fire an event after the Eloquent has finished creating.
Here's my code in Branch model:
class Branch extends Model
{
//some code here
public static function boot() {
parent::boot();
self::created(function (HistoryLog $model) {
$model->tag = 'Created';
$model->description = 'This branch was created by '. ucwords(auth()->user()->name());
$model->save();
});
}
}
What I'm trying to do is, I want to create a history_log after branch was created.
But this code returns an error:
Symfony\Component\Debug\Exception\FatalThrowableError : Argument 1 passed to
App\Vehicle::App{closure}() must be an instance of App\HistoryLog, instance of
App\Vehicle given, called in D:\document\My Documents\optodph\vendor\laravel\fr
amework\src\Illuminate\Events\Dispatcher.php on line 347
Can someone point out to me what's wrong with that code? And what's the right way to achieve this?
Laravel way to do this.
Create an Observer:
php artisan make:observer BranchObserver --model=Branch
Add your logic to the Observer:
<?php
namespace App\Observers;
use App\Branch;
class BranchObserver
{
/**
* Handle the Branch "created" event.
*
* #param \App\Branch $branch
* #return void
*/
public function created(Branch $branch)
{
// Add your logic here
}
}
Register it in AppServiceProvider:
<?php
namespace App\Providers;
use App\Branch;
use App\Observers\BranchObserver;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*
* #return void
*/
public function register()
{
//
}
/**
* Bootstrap any application services.
*
* #return void
*/
public function boot()
{
Branch::observe(BranchObserver::class);
}
}
$model is a new record created. Is an instance of App\Vehicle not a App\HistoryLog.
Working code might look like this:
class Vehicle extends Model
{
//some code here
public static function boot() {
parent::boot();
self::created(function ($model) {
App\HistoryLog::create([...]);
});
}
}
You can also achieve this with Eloquent Observers https://laravel.com/docs/5.8/eloquent#observers
Im working on a Laravel 5 app and im trying to set up a ComposerServiceProvider to pass data to a couple of views (im trying now to add it to the layout/app.blade.php).
I did this following the documentation but the data im trying to add is still undefined..
In my config/app.php I added to the providers:
App\Providers\ComposerServiceProvider::class,
On ComposerServiceProvider.php
boot method:
View::composer(['layouts.app'], 'App\ViewComposers\LayoutAppComposer');
On the new created LayoutAppComposer.php
compose(View $view) method:
$metaTitle = 'MetaTitle';
$view->with('metaTitle', $metaTitle)
But When i access the url I still get:
Undefined variable: metaTitle (View: .../resources/views/layouts/app.blade.php)
Am I missing something here??
ServiceProvider
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\View;
class ComposerServiceProvider extends ServiceProvider
{
/**
* Bootstrap the application services.
*
* #return void
*/
public function boot()
{
View::composer(['layouts.app'], 'App\ViewComposers\LayoutAppComposer');
}
/**
* Register the application services.
*
* #return void
*/
public function register()
{
}
}
LayoutAppComposer
<?php
namespace App\ViewComposers;
use Illuminate\Support\Facades\Session;
use Illuminate\View\View;
class LayoutAppComposer {
protected $metaTitle;
public function __construct($metaTitle)
{
$this->metaTitle = $metaTitle;
}
/**
* Bind data to the view.
*
* #param View $view
* #return void
*/
public function compose(View $view) {
$this->metaTitle = 'MetaTitle';
$view->with('metaTitle', $this->metaTitle);
}
}
Try changing:
$metaTitle = 'MetaTitle';
$view->with('metaTitle', $metaTitle)
to
$this->metaTitle = 'metaTitle';
$view->with('metaTitle', $this->metaTitle)
setup $this->metaTitle as a protected class member and assign it in the composer constructor. it may be that $metaTitle is getting garbage collected before you use it since this being resolved at the service provider level.
Since you're registering the composer with your app layout, instead you may need to use the wildcard character in place of app.layout like such:
View::composer('*', function ($view) {
//
});
To resolve $metaTile for the View Composer, try binding in your AppServiceProvider:
$this->app->bind('metaTitle', 'the string i want displayed across all views');
I have a small websockets chat written, the php part is just 2 files, server.php and Chat.php, they are both inside a bin folder and depend on ratchet and some other libraries which I downloaded to the laravel installation via composer.
server.php
require __DIR__.'/../vendor/autoload.php';
require 'Chat.php';
use Ratchet\Server\IoServer;
use Ratchet\http\HttpServer;
use Ratchet\WebSocket\WsServer;
$server = IoServer::factory(new HttpServer(new WsServer(new Chat)), 8080);
$server->run();
Chat.php
use Ratchet\MessageComponentInterface;
use Ratchet\ConnectionInterface;
class Chat implements MessageComponentInterface {
protected $clients;
function __construct() {
$this->clients = new \SplObjectStorage;
}
public function onOpen(ConnectionInterface $conn)
{
$this->clients->attach($conn);
}
public function onMessage(ConnectionInterface $conn, $msg)
{
foreach ($this->clients as $client)
{
if ($client !== $conn ) {
$client->send($msg);
}
}
}
public function onClose(ConnectionInterface $conn)
{
$this->clients->detach($conn);
}
public function onError(ConnectionInterface $conn, \Exception $e)
{
echo 'the following error occured: ' . $e->getMessage();
$conn->close();
}
}
Now, I have that bin folder inside the laravel root, and so I am able to start the server since the server.php is looking for dependencies in vendor one level up, but what I wanna do is use all the laravel goodies within these files, especially within Chat.php.
So now for example if I write use DB in Chat.php it gives an error (which I understand, it has no way of knowing laravel), so my question is how do I include this bin folder and its files so that I can use all the laravel goodies within them?
You do not need to manually load vendor/autoload.php because laravel does that for you.
First you have to create folder inside your YourLaravelRoot/app dir(Let's name that as Services). Then move chat.php into that, rename it to ChatService.php(Change class name also to ChatService) or any appropriate name(reccomanded to ends with xxxxService so it's easier to identify) and namespace it as namespace App\Services;(Assumming that your app name is App).Namespacing correctly is important otherwise you have to manually loads it throught composer.json .Then create a artisan command and move content of server.php into handle method inside command(Let's name it ServerCommand.php). Add use App\Services\ChatService as Chat;. Register the command in Kernal.php on app/console That's it. Now you should be able to access any laravel facade inside ChatService
Summary:
YourLaravelProject
-app
--Console
Kernal.php
<?php
namespace App\Console;
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 = [
Commands\ServerCommand::class,
];
/**
* Define the application's command schedule.
*
* #param \Illuminate\Console\Scheduling\Schedule $schedule
* #return void
*/
protected function schedule(Schedule $schedule)
{
// $schedule->command('inspire')
// ->hourly();
}
}
---Commands
----ServerCommand.php
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Ratchet\Server\IoServer;
use Ratchet\Http\HttpServer;
use Ratchet\WebSocket\WsServer;
use App\Services\ChatService as Chat;
class ServerCommand extends Command
{
/**
* The name and signature of the console command.
*
* #var string
*/
protected $signature = 'server:run';
/**
* The console command description.
*
* #var string
*/
protected $description = 'Command description';
/**
* Create a new command instance.
*
* #return void
*/
public function __construct()
{
parent::__construct();
}
/**
* Execute the console command.
*
* #return mixed
*/
public function handle()
{
$server = IoServer::factory(new HttpServer(new WsServer(new Chat)), 8080);
$server->run();
}
}
--Services
---ChatService.php
<?php
namespace App\Services;
use Ratchet\MessageComponentInterface;
use Ratchet\ConnectionInterface;
/**
*
*/
class ChatService implements MessageComponentInterface {
{
protected $clients;
function __construct() {
$this->clients = new \SplObjectStorage;
}
public function onOpen(ConnectionInterface $conn)
{
$this->clients->attach($conn);
}
public function onMessage(ConnectionInterface $conn, $msg)
{
foreach ($this->clients as $client)
{
if ($client !== $conn ) {
$client->send($msg);
}
}
}
public function onClose(ConnectionInterface $conn)
{
$this->clients->detach($conn);
}
public function onError(ConnectionInterface $conn, \Exception $e)
{
echo 'the following error occured: ' . $e->getMessage();
$conn->close();
}
}
Execute command php artisan server:run