Cannot use right environment database with Laravel Dusk - laravel

I have two environment files: .env and .env.dusk.testing (Because .env gets overwritten constantly if I'm not explicit with the environment handler).
I've got two development servers:
One in port 8080 that uses .env (using command php artisan serve --port=8080)
One in port 8000 that uses .env.dusk.testing (using command php artisan serve --env=dusk.testing)
I'm trying a very basic test. Logging in an user. It's not working because it's not using the correct database.
.env
APP_NAME=DEV
APP_ENV=local
APP_DEBUG=true
DB_CONNECTION=pgsql
DB_HOST=127.0.0.1
DB_PORT=5433
DB_DATABASE=db
DB_USERNAME=dbuser
DB_PASSWORD=****
.env.dusk.testing
APP_NAME=DUSK
APP_ENV=testing
APP_DEBUG=true
DB_CONNECTION=sqlite
phpunit.dusk.xml
...
<php>
<env name="APP_ENV" value="testing"/>
<env name="DB_CONNECTION" value="sqlite"/>
<env name="DB_DATABASE" value=":memory:"/>
</php>
...
I've also defined the sqlite connection in config/database.php as follows:
'sqlite' => [
'driver' => 'sqlite',
'database' => ':memory:',
'prefix' => '',
],
My test uses a setup method to wipe up an user and persist it in the database.
namespace Tests\Browser;
use App\User;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Laravel\Dusk\Browser;
use Tests\DuskTestCase;
class ExampleTest extends DuskTestCase
{
use DatabaseMigrations;
protected $user;
public function setUp(): void
{
parent::setUp();
$this->user = factory(User::class)->create([
'nombre' => 'John Doe',
'email' => 'john.doe#testing.com',
'password' => bcrypt('password')
]);
}
/** #test */
public function guest_user_can_authenticate_with_valid_credentials()
{
$this->browse(function (Browser $browser) {
$browser->assertGuest()
->visit('/login')
->type('#email', $this->user->email)
->type('#password', 'password')
->click('#login-button')
->assertAuthenticatedAs($this->user)
->assertUrlIs('/dashboard')
->logout();
});
}
}
Using the DatabaseMigrations trait wipes out my PostgreSQL database instead of using the SQLite one. I tried to use RefreshDatabase instead but it's the same result. My environment database gets wiped out.
And the test fails anyways in both cases. The user is persisted into the SQLite testing database but it's still looking in the other one in the tests and destroying it in the process.
I'm this close to just drop this package and browser tests altogether. There is nothing simple about it. The documentation is shallow and it just doesn't work as expected.

My issue was fixed by
Not using an in-memory SQLite database (I used a .sqlite file instead).
Using the DatabaseMigrations trait instead of RefreshDatabase.
Being explicit about the environment file when running dusk. php artisan dusk --env=dusk.testing.

Related

Laravel Forge database seeder

I'm trying to insert a user into my laravel forge site automatically, but the seeder is not working. I can create a user and log in, but I'm trying to have a default admin user once the site is live.
class UsersTableSeeder extends Seeder
{
public function run()
{
DB::table('users')->insert([
'name' => 'Jovani Calixte',
'is_admin' => 1,
'email' => 'user1#email.com',
'password' => bcrypt('password'),
]);
}
}
and this the database user php file
class DatabaseSeeder extends Seeder
{
public function run()
{
$this->call(UsersTableSeeder::class);
}
}
When I deploy the site to forge and try to log in I get the following error. What am I doing wrong?
Whoops! Something went wrong.
These credentials do not match our
records.
If you want to seed the database on the Forge provisioned server, you can log into Forge and navigate to your server/site, and click "Commands" in the navbar on the left, you can then run the command for your seeder,
php artisan db:seed
or
php artisan db:seed --class=UsersTableSeeder
To ensure that this data did indeed seed, you should check your users table to make sure that the record is in the table.

how to run unittests in laravel 5.5 using a sqlite in memory database

I setup a test database:
phpunit.xml:
<phpunit>
<!... other stuff ...>
<php>
<env name="APP_ENV" value="testing"/>
<env name="CACHE_DRIVER" value="array"/>
<env name="SESSION_DRIVER" value="array"/>
<env name="QUEUE_DRIVER" value="sync"/>
<env name="DB_CONNECTION" value="sqlite_testing"/>
</php>
</phpunit>
database.php:
'connections' => [
'sqlite_testing' => [
'driver' => 'sqlite',
'database' => ':memory:',
'prefix' => '',
],
DatabaseSeeder.php
<?php
use Illuminate\Database\Seeder;
class DatabaseSeeder extends Seeder
{
/**
* Run the database seeds.
*
* #return void
*/
public function run()
{
$this->call([
MyTableSeeder::class
]);
}
}
MyTableSeeder.php:
<?php
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;
use App\My\Model;
class MyTableSeeder extends Seeder
{
/**
* Run the database seeds.
*
* #return void
*/
public function run()
{
DB::table('model')->insert([
'id' => 1,
'created_at' => \Carbon\Carbon::now(),
'updated_at' => \Carbon\Carbon::now()
]);
/**
* create random data
*/
factory(Model::class, 50)->create();
}
}
ModelFactory.php:
<?php
use Faker\Generator as Faker;
$factory->define(\App\My\Model::class, function (Faker $faker) {
return [
'id' => $faker->unique()->randomNumber(),
'created_at' => $faker->dateTime('now'),
'updated_at' => $faker->dateTime('now')
];
});
MyTest.php:
<?php
namespace Tests\Unit;
use App\My\Model;
use Tests\TestCase;
use Illuminate\Foundation\Testing\RefreshDatabase;
class MyTest extends TestCase
{
use RefreshDatabase;
public function testGettingModel() {
var_dump(Model::all()); // <-- returns a collection without any items
$model = Model::where('id', 1)->first();
$this->assertEquals('1', $model->id); // <-- trying to get property of non-object
}
}
So at the test run it seems the database is migrated by the trait but not seeded and thus nothing is returned.
The documentation does not state however (or I couldn't find it) how to seed the database upon testing.
It states how to manually seed by running "php artisan db:seed" but that's not working in a test obviously as the database doesn't exist anymore after the test. And I can't run it manually as the database doesn't exist before the test. Also that would make testing impractical.
Some examples state running the seeding before testing in the setup method of the test like so:
public function setUp() {
Artisan::call('db:seed');
}
But including this statement leads to the error:
RuntimeException : A facade root has not been set. (in /vendor/laravel/framework/src/Illuminate/Support/Facades/Facade.php:218)
Alternatively running it like so:
public function setUp()
{
$this->artisan('db:seed');
}
Leads to:
Error : Call to a member function call() on null (in /vendor/laravel/framework/src/Illuminate/Foundation/Testing/Concerns/InteractsWithConsole.php:18)
How do I actually do this? Is there any fully working example anywhere? So far I couldn't find any :(
You have to call $this->seed(); in the setUp method. But you need to ensure to call the parents setUp method before or it will fail.
<?php
namespace Tests\Unit;
use App\My\Model;
use Tests\TestCase;
use Illuminate\Foundation\Testing\RefreshDatabase;
class MyTest extends TestCase
{
use RefreshDatabase;
public function setUp()
{
parent::setUp();
$this->seed();
}
public function testGettingModel() {
$model = Model::where('id', 1)->first();
$this->assertEquals('1', $model->id); // will assert to true if the id actually exists
}
}

laravel how to send email on new user registration only to admin email

I need to send a notification email only to admin when new user is registered.
When i submitting registration getting error -
Class 'app\Mail\NewUser' not found
in RegisterController.php (line 88)
Here is controller:
namespace App\Http\Controllers\Auth;
use App\User;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Validator;
use Illuminate\Foundation\Auth\RegistersUsers;
use app\Mail\NewUser;
use Illuminate\Support\Facades\Mail;
protected function create(array $data)
{
$user = User::create([
'companyname' => $data['companyname'],
'email' => $data['email'],
'password' => bcrypt($data['password']),
'VAT' => $data['VAT'],
'companyphone' => $data['companyphone'],
'companystreet' => $data['companystreet'],
'companycity' => $data['companycity'],
'companycountry' => $data['companycountry'],
'companypostcode' => $data['companypostcode']
]);
Mail::to('example#gmail.com')->send(new NewUser());
return $user;
}
would be great if someone help me.
Because you may not have created NewUser mailable class. You can create mailable classes by following artisan command.
php artisan make:mail NewUser
To know how to configure mali parameters like sender, from, to, subject, body etc., refer Writing Mailable here
Source
Update:
If you have created mailable and not able to find it, most probably it's composer issue. Fire following commands to clear cache and autoload files.
composer dump-autoload
php artisan config:cache

Laravel 5: Handle multiple connections and testing

I have a Laravel 5.4 app which has models pointing to different database connections.
For example, I have User pointing to a MySQL database and then Company pointing to a PostgreSQL database (using the $connection variable).
Now, when I run PHPUnit I'd like the $connection variable to be replaced by what's specified in the phpunit.xml file, which is a SQLite in memory type of database.
How is that achievable?
Most answers are changing production code, which I don't like.
Since \Illuminate\Contracts\Foundation\Application is available in your tests, let's use it!
<?php
declare(strict_types=1);
namespace Tests\Feature;
use Tests\TestCase;
use App\Models\Company;
class CompanyFeatureTest extends TestCase
{
/**
* #return void
*/
protected function setUp(): void
{
parent::setUp();
$this->app->bind(Company::class, function () {
return (new Company())->setConnection(config('database.default'));
});
}
}
Whenever your Company class is called, we give back a manipulated one.
In this one we have changed the $connection property.
If you have the following in your phpunit.xml:
<server name="DB_CONNECTION" value="sqlite"/>
The value of config('database.default') will be sqlite.
More info about binding can be found here: https://laravel.com/docs/5.8/container#binding
I'd rather not touch the production code and instead use the service container to create test-specific services.
In this case, if you want all your models to use the same default testing connection:
public function createApplication()
{
$app = require __DIR__.'/../bootstrap/app.php';
$app->make(Kernel::class)->bootstrap();
$fakeManager = new class ($app, $app['db.factory']) extends DatabaseManager {
public function connection($name = null) {
return parent::connection($this->getDefaultConnection());
}
};
$app->instance('db', $fakeManager);
Model::setConnectionResolver($fakeManager);
return $app;
}
(This overrides the CreatesApplication trait, you could instead place this code anywhere between when the app is bootstrapped and when the migration command is called).
(Also note this is using PHP 7 inline anonymous classes. You could also define a fake db manager as a separate class).
Off the top of my head you could move the connection names to the .env file
in your model:
public function __construct(array $attributes = [])
{
$this->connection = env('MY_CONNECTION');
parent::__construct($attributes);
}
in your .env file
MY_CONNECTION=mysql
in phpunit.xml
<env name="MY_CONNECTION" value="sqlite"/>
As mentioned before, you first need to set the connection in each Model.
So, you setup the connections in the database config file, set the values in the .env file and use these in the Model's constructors.
For testing, you could also do this.
Add the testing connection to the config/database.php file and then use an overriding env file.
Create an additional env file, name it something like .env.testing.
So, in your .env file you will have:
CONNECTION_MYSQL=mysql
CONNECTION_POSTGRESS=postgress
Then in the .env.testing file you can have:
CONNECTION_MYSQL=test_sqlite
CONNECTION_POSTGRESS=test_sqlite
Finally to load this env file when testing, go to CreatesApplication trait and update to the following:
public function createApplication()
{
$app = require __DIR__.'/../bootstrap/app.php';
$app->loadEnvironmentFrom('.env.testing');
$app->make(Kernel::class)->bootstrap();
return $app;
}
By using the loadEnvironemtFrom() method, all tests that use this trait will load the .env.testing file and use the connections defined there.
Like Arturo Rojas wrote in his answer you have to check in the constructor, if the connection variable has to be overwritten:
public function __construct(array $attributes = [])
{
if(App::environment() == 'testing') {
$this->connection = env('DB_CONNECTION');
}
parent::__construct($attributes);
}
In your phpunit.xml you need these variables (DB_DATABASE is optional):
<php>
<env name="APP_ENV" value="testing"/>
<env name="DB_CONNECTION" value="sqlite"/>
<env name="DB_DATABASE" value="testDatabase"/>
<php>
Laravel will then use the sqllite connection from /config/database.php

Laravel's Middleware not working on Controller

I have a middleware called 'AdminMiddleware', which is being used in the constructor of a class. For some reason the middleware is not called from the constructor, although the constructor function is being run. I tried doing a die dump on the adminMiddleware file but it seems like it just ignores this file.
namespace App\Http\Controllers\SuperAdmin;
use Illuminate\Http\Request;
use App\Http\Requests;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Auth;
class dashboard extends Controller
{
protected $user;
public function __construct()
{
$this->middleware('admin');
$this->user = Auth::User();
}
//Kernel.php
protected $routeMiddleware = [
'auth' => \App\Http\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
'superadmin' => \App\Http\Middleware\SuperAdminMiddleware::class,
'admin' => \App\Http\Middleware\AdminMiddleware::class,
];
For some project requirements i cant use the middleware directly on the routes. Any help is appreciated, i am using laravel 5.1.
I ran into the same issue, and apparently route caching also caches middlewares.
So php artisan route:clear solved it in my case.
You need to do 2 things to enable middleware in the controller:
Register middleware in $routeMiddleware in your App\Http\Kernel.php
protected $routeMiddleware = [
'admin' => 'App\Http\Middleware\AdminMiddleware',
];
Enable middleware in your controller, using middleware's key, not class name:
$this->middleware('admin');
I have the same problem, and solve it by composer dump-autoload.
My problem is change the file name and not regenerate the autoload.
I hope it works for you.
Using Laravel Framework 9.46.0 doesn't work for me also with the key 'admin', changing it to 'administrator' or 'admin.only' works. it's like 'admin' is a reserved word.

Resources