Laravel 5.8 testing settings record from database - php

I am new to writing tests and I was wondering if what I am doing here is correct.
Basically I wanna write a test to check if the settings are correct but I use a record straight from my database.
Everything works great but I was wondering what if my database is empty? This test will never work
So what is best practice here?
my test:
/** #test */
public function settings_for_ftp_flysytem_is_correct_test()
{
$clientSettings = Client::where('category', 'ftp')->first()->settings()->get();
$this->assertArrayHasKey('host', $clientSettings);
$this->assertArrayHasKey('username', $clientSettings);
$this->assertArrayHasKey('password', $clientSettings);
$this->assertArrayHasKey('port', $clientSettings);
$this->assertArrayHasKey('root', $clientSettings);
}

I reckon the easiest solution is to use DatabaseTransactions trait and model factories. It is always a good idea to generate test data for every test case.
<?php
// ClientFactory.php
use Faker\Generator;
$factory::define(App\Setting::class, function (Generator $faker) {
return [...];
});
<?php
// SettingFactory.php
use Faker\Generator;
$factory::define(App\Setting::class, function (Generator $faker) {
return [...];
});
use Tests\TestCase;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use App\Client;
use App\Setting;
class SettingsTest extends TestCase
{
use DatabaseTransactions;
public function test_client_has_settings()
{
// 1. Arrange.
$client = factory(Client::class)->create();
$settings = factory(Setting::class, 2)->create();
$client->settings()->saveMany($settings);
// 2. Act.
$response = $this->getJson("/clients/{$client->id}/settings");
// 3. Assert.
$response->assertJson([...]);
}
}
The above code is an example. For more info, pls check the following resources:Laravel Docs, Build a Forum with Laravel, Test-Driven Laravel.

Related

Laravel Botman issue - Can't call functions from the same class after nesting inside botman class

I am trying to use the native buttons and questions feature in Botman inside laravel, however i am struggling to understand how to chain functions without using static functions. I have it working where everything is a static function, however i want to use all the information gathered to send an email.
// initialization function
public function handle()
{
$botman->hears("{message}", function($botman, $message) {
$this->selectHelpQuery($botman);
});
}
// ask question function
public function selectHelpQuery($botman)
{
$question = Question::create("How can i help you, would you like to know about the following:")
->fallback("Unable to help at this time, please try again later")
->callbackId("choose_query")
->addButtons([
Button::create("button1")->value("val1"),
Button::create("button2")->value("val2"),
]);
$botman->ask($question, function (Answer $answer, $botman) {
// Detect if button was clicked:
if ($answer->isInteractiveMessageReply()) {
if($answer->getValue() == "val1")
{
$this->contactFollowUp($botman); //** not working
} else {
$this->contactNoFollowUp($botman); //** not working
}
}
});
}
// other functions.....
However without declaring the contactFollowUp() function as static and accessing it by using the classname BotManController::contactFollowUp($botman) However if i do this i have issues with accessing and setting data for use in other functions. Specifically i get a Method contactFollowUp does not exist error.
So, after finding some github code examples i have managed to work out the issue. It is to do with the way the botman framework is structured. To achive linked conversations you have to use a function from the botman framework called startConversation() to envoke this you need to reference bot which is from the extended base class Conversation. So you will need an entry point and then the conversation you want to link to like so:
*note you will need the default entry point of run() for each conversation.
//BotManController.php
<?php
namespace App\Http\Controllers\Chatbot;
use App\Http\Controllers\Controller;
use BotMan\BotMan\BotMan;
use Illuminate\Http\Request;
use BotMan\BotMan\Messages\Incoming\Answer;
use BotMan\BotMan\Messages\Outgoing\Actions\Button;
use BotMan\BotMan\Messages\Outgoing\Question;
class BotManController extends Controller
{
/**
* start the conversation on intitlization
*/
public function handle()
{
$botman = app("botman");
$botman->hears("{message}", function($botman, $message) {
$botman->startConversation(new BotManStart);
});
$botman->listen();
}
}
Then
// BotManStart.php
<?php
namespace App\Http\Controllers\Chatbot;
use BotMan\BotMan\BotMan;
use Illuminate\Http\Request;
use BotMan\BotMan\Messages\Incoming\Answer;
use BotMan\BotMan\Messages\Outgoing\Actions\Button;
use BotMan\BotMan\Messages\Outgoing\Question;
use BotMan\BotMan\Messages\Conversations\Conversation;
class BotManStart extends Conversation
{
public function run()
{
$this->selectHelpQuery();
}
public function selectHelpQuery()
{
$question = Question::create("How can i help you, would you like to know about the following: ")
->fallback("Unable to help at this time, please try again later")
->callbackId("choose_query")
->addButtons([
Button::create("Button 1")->value("button1"),
Button::create("Button 2")->value("button2"),
]);
$this->ask($question, function (Answer $answer) {
if ($answer->isInteractiveMessageReply()) {
switch ($answer->getValue()) {
case "button1":
$this->bot->startConversation(new BotManConversation1());
break;
case "button2":
$this->bot->startConversation(new BotManConversation2());
break;
}
}
});
}
}

How to share user data in laravel dusk tests

I'm very new to Laravel Dusk (like less than 24 hours) and I'm experimenting with creating some tests but I can't wrap my head around getting past the initial test.
So I have UserCanRegisterTest.php and UserCanSeeDashboardTest.php, In UserCanRegisterTest.php I register a user, how can I access that user info in UserCanSeeDashboardTest.php without having to recreate another user? I have tried researching but I've fallen down a rabbit hole, I've looked at memory, cookies, DatabaseTransactions but nothing seems to make sense or show an example.
Is it possible for me to use the $faker->safeEmail and $password from UserCanRegisterTest.php in UserCanSeeDashboardTest.php and all other tests I make?
UserCanRegisterTest.php:
<?php
namespace Tests\Browser;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Laravel\Dusk\Browser;
use Tests\DuskTestCase;
class UserCanRegisterTest extends DuskTestCase
{
use DatabaseMigrations;
/*public function setUp()
{
parent::setUp();
$this->artisan('db:seed');
}*/
/** #test */
public function user_passes_registration_form()
{
$faker = \Faker\Factory::create();
/*$roleSeeder = new RoleTableSeeder();
$roleSeeder->run();
$permissionSeeder = new PermissionTableSeeder();
$permissionSeeder->run();*/
$this->browse(function($browser) use ($faker) {
$password = $faker->password(9);
$browser->visit('/register')
//->assertSee('Welcome Back!')
->type('company_name', $faker->company)
->type('name', $faker->name)
->type('email', $faker->safeEmail)
->type('password', $password)
->type('password_confirmation', $password)
->press('REGISTER')
->assertPathIs('/register');
});
}
}
Here is UserCanSeeDashboardTest.php (note how I'd like to use $faker->safeEmail and $password from the above test so I don't need to create new user every time).
<?php
namespace Tests\Browser;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Laravel\Dusk\Browser;
use Tests\DuskTestCase;
use App\User;
class UserCanSeeDashboardTest extends DuskTestCase
{
use DatabaseMigrations;
/*public function setUp()
{
parent::setUp();
//$this->artisan('db:seed');
}*/
/** #test */
public function test_I_can_login_successfully()
{
$this->browse(function ($browser) {
//$user->roles()->attach(1); //Attach user admin role
$browser->visit('/login')
->type('email', $faker->safeEmail)
->type('password', $password)
->press('SIGN IN')
->assertSee('Dashboard');
});
}
}
Ideally, I have a test that registers a user, then I have other tests that use that registered user's data to log in and test other parts of my app.
PHPUnit doesn't have great support for tests that depend on each other. Tests in PHPUnit should mostly be considered independent. The framework does provide the #depends annotation that you might have been able to use for you tests that depend on the registration method, but it only works for tests that are in the same class.
Also, you don't need to worry about creating multiple users because you're using the DatabaseMigrations trait that refreshes your test database for you after every test.
The way I see it, you have two options. Either you:
Move your registration code (the part starting from $browser->visit('/register')) to a new method and then call that method in both your user_passes_registration_form test and in your other tests where you want to have a registered user, or
Write a new method that you can call from your other tests that registers a user directly in your database (e.g. using User::create).
The benefit of the second option is that you'll have less HTTP calls which will result in a faster test run and only your registration test would fail (instead of all your tests) when your registration endpoint is broken.
So what I'd suggest is that you keep your registration test as is and use either a trait or inheritance to add a few methods that you can reuse to register or login a test user from other test methods.
You could create a class MyDuskTestCase that inherits from DuskTestCase and that contains a method to register a test user:
<?php
namespace Tests;
use Tests\DuskTestCase;
use App\User;
use Hash;
abstract class MyDuskTestCase extends DuskTestCase
{
private $email = 'test#example.com';
private $password = 'password';
public function setup(): void
{
parent::setUp();
// If you want to run registerTestUser for every test:
// registerTestUser();
}
public function registerTestUser()
{
User::create([
'email' => $this->email,
'name' => 'My name',
'password' => Hash::make($this->password)
]);
// assign a role, etc.
}
public function getTestUser()
{
return User::where('email', $this->email)->first();
}
}
Then you can either run the registerTestUser method in the setup method to create the test user for every test, or you can call the method from only the tests where you'll need the user. For example:
<?php
namespace Tests\Browser;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Laravel\Dusk\Browser;
use Tests\MyDuskTestCase;
class UserCanRegisterTest extends MyDuskTestCase
{
use DatabaseMigrations;
public function test_I_can_login_successfully()
{
$this->registerTestUser();
$this->browse(function ($browser) use ($user) {
$browser->visit('/login')
->type('email', $this->email)
->type('password', $this->password)
->press('SIGN IN')
->assertSee('Dashboard');
});
}
}
For logins, you can either add another method to your base test class to log the test user in, or you could use the loginAs method that Dusk provides:
$user = this->getTestUser();
$this->browse(function ($browser) {
$browser->loginAs($user)
->visit('/home');
});

How to mock Auth in a Laravel 5.2 Model

I'm trying to write some unit tests for a brand new mini app. I usually write functional tests so this is me branching out to try and do it properly with mocking and stubs and all those things that make it just about the code.
The model looks like this :
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use Auth;
class myModel extends Model
{
public static function getUser()
{
$user = \Auth::user();
return $user->adldapUser->cn[0];
}
}
And the test :
class MyModelTest extends TestCase
{
public function testGetUser()
{
$mockResult = new StdClass();
$mockResult->adldapUser = new stdClass();
$mockResult->adldapUser->cn=array("test");
$auth = $this->getMock('Auth');
$auth
->method('user')
->will($this->returnValue($mockResult));
$this->assertEquals('test',\App\MyModel::getUser());
}
}
But when I run the unit test I get the following error message :
There was 1 error:
1) MyModelTest::testGetUser ErrorException: Trying to get property of
non-object
/home/aidan/web/vagrant-web-dev/src/apps/orcid/app/MyModel.php:61
/home/aidan/web/vagrant-web-dev/src/apps/orcid/tests/MyModelTest.php:18
and if I post out $user it's NULL.
What am I doing wrong here?
Here I go again answering my own question half an hour after asking it.
For prosperity's sake I'll leave it here.
The answer was in this post here : https://stackoverflow.com/a/17602763/808124
So I replaced the $this->getMock with
Auth::shouldReceive('user')->once()->andreturn($mockResult);
Working
I use it this way:
$user -factory(User:class)->create();
\Auth::shouldReceive('guard')->andReturnSelf()
->shouldReceive('user')->andReturn($user)
->shouldReceive('check')->andReturn(true);

How to create Laravel 5.1 Custom Authentication driver?

I am working in Laravel authentication login using socialite. Now I can able to save data of user from socialite. But now I am facing problem how to authenticate user from gmail, github.
After some research I understood that I need to create custom authentication. I googled but all are Laravel 4.1 topics. If any one work on this please provide your answers.
I already read following topics but I didn't got how to do it?
http://laravel.com/docs/5.1/authentication#social-authentication
http://laravel.com/docs/5.1/providers
http://laravel-recipes.com/recipes/115/using-your-own-authentication-driver
http://laravel.io/forum/11-04-2014-laravel-5-how-do-i-create-a-custom-auth-in-laravel-5
Update
public function handleProviderCallback() {
$user = Socialite::with('github')->user();
$email=$user->email;
$user_id=$user->id;
//$authUser = User::where('user_id',$user_id)->where('email', $email)->first();
$authUser = $this->findOrCreateUser($user);
if(Auth::login($authUser, true)) {
return Redirect::to('user/UserDashboard');
}
}
private function findOrCreateUser($user) {
if ($authUser = User::where('user_id',$user->id)->first()) {
return $authUser;
}
return User::create([
'user_id' => $user->id,
'name' => $user->nickname,
'email' => $user->email,
'avatar' => $user->avatar
]);
}
This answer is most suited for Laravel 5.1. Please take care if you
are in some other version. Also keep in mind that IMHO this is a rather advanced level in Laravel, and hence if you don't fully understand what you are doing, you may end up crashing your application. The solution is not end to end correct. This is just a general guideline of what you need to do in order for this to work.
Adding Custom Authentication Drivers In Laravel 5.1
Hint: Laravel documentation for this topic is here.
Hint2: The last link you mentioned is quite useful in my opinion. I learned all of this after reading that link.
http://laravel.io/forum/11-04-2014-laravel-5-how-do-i-create-a-custom-auth-in-laravel-5
Before we start, I would first like to describe the login flow which will help you understand the process. Laravel uses a driver to connect to the database to fetch your records. Two drivers come pre-bundled with laravel - eloquent & database. We want to create a third so that we can customize it to our needs.
Illuminate\Auth\Guard inside your vendor folder is the main file which has code for the user to log in and log out. And this file mainly uses two Contracts (or interfaces) that we need to override in order for our driver to work. From Laravel's own documentation read this:
The Illuminate\Contracts\Auth\UserProvider implementations are only
responsible for fetching a Illuminate\Contracts\Auth\Authenticatable
implementation out of a persistent storage system, such as MySQL,
Riak, etc. These two interfaces allow the Laravel authentication
mechanisms to continue functioning regardless of how the user data is
stored or what type of class is used to represent it.
So the idea is that for our driver to work we need to implement Illuminate\Contracts\Auth\UserProvider and Illuminate\Contracts\Auth\Authenticatable and tell Laravel to use these implementations instead of the defaults.
So let's begin.
Step 1:
Choose a name for your driver. I name mine socialite. Then in your config/auth.php, change the driver name to socialite. By doing this we just told laravel to use this driver for authentication instead of eloquent which is default.
Step 2:
In your app/Provider/AuthServiceProvider in the boot() method add the following lines:
Auth::extend('socialite', function($app) {
$provider = new SocialiteUserProvider();
return new AuthService($provider, App::make('session.store'));
});
What we did here is:
We first used Auth facade to define the socialite driver.
SocialiteUserProvider is an implementation of UserProvider.
AuthService is my extension of Guard class. The second parameter this class's constructor takes is the session which laravel uses to get and set sessions.
So we basically told Laravel to use our own implementation of Guard class instead of the default one.
Step 3:
Create SocialiteUserProvider. If you read the Laravel's documentation, you will understand what each of these methods should return. I have created the first method as a sample. As you can see, I use my UserService class to fetch results. You can fetch your own results however you want to fetch them. Then I created an User object out of it. This User class implements the Illuminate\Contracts\Auth\Authenticatable contract.
<?php
namespace App\Extensions;
use App\User;
use App\Services\UserService;
use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Contracts\Auth\UserProvider;
class SocialiteUserProvider implements UserProvider
{
private $userService;
public function __construct(UserService $userService)
{
$this->userService = $userService;
}
public function retrieveById($identifier)
{
$result = $this->userService->getUserByEmail($identifier);
if(count($result) === 0)
{
$user = null;
}
else
{
$user = new User($result[0]);
}
return $user;
}
public function retrieveByToken($identifier, $token)
{
// Implement your own.
}
public function updateRememberToken(Authenticatable $user, $token)
{
// Implement your own.
}
public function retrieveByCredentials(array $credentials)
{
// Implement your own.
}
public function validateCredentials(Authenticatable $user, array $credentials)
{
// Implement your own.
}
}
Step 4:
Create User class which implements the Authenticatable. This class has to implement this interface because the Guard class will use this class to get values.
<?php
namespace App;
use Illuminate\Contracts\Auth\Authenticatable;
class User implements Authenticatable
{
protected $primaryKey = 'userEmail';
protected $attributes = [];
public function __construct(array $attributes)
{
$this->attributes = $attributes;
}
public function getUserAttributes()
{
return $this->attributes;
}
public function getAuthIdentifier()
{
return $this->attributes[$this->primaryKey];
}
public function getAuthPassword()
{
// Implement your own.
}
public function getRememberToken()
{
// Implement your own.
}
public function setRememberToken($value)
{
// Implement your own.
}
public function getRememberTokenName()
{
// Implement your own.
}
}
Step 5:
Finally create the AuthService class that will call the Guard methods. This is my own implementation. You can write your own as per your needs. What we have done here is extended the Guard class to implement two new functions which are self explanatory.
<?php
namespace App\Services;
use Illuminate\Auth\Guard;
class AuthService extends Guard
{
public function signin($email)
{
$credentials = array('email' => $email);
$this->fireAttemptEvent($credentials, false, true);
$this->lastAttempted = $user = $this->provider->retrieveById($email);
if($user !== null)
{
$this->login($user, false);
return true;
}
else
{
return false;
}
}
public function signout()
{
$this->clearUserDataFromStorage();
if(isset($this->events))
{
$this->events->fire('auth.logout', [$this->user()]);
}
$this->user = null;
$this->loggedOut = true;
}
}
Step 6: Bonus Step
Just to complete my answer, I will also explain the structure that UserService class expects. First lets understand what this class does. In our above steps we created everything to let laravel know how to use our authentication driver, instead of theirs. But we still haven't told laravel that how should it get the data. All we told laravel that if you call the userService->getUserByEmail($email) method, you will get your data. So now we simply have to implement this function.
E.g.1 You are using Eloquent.
public function getUserByEmail($email)
{
return UserModel::where('email', $email)->get();
}
E.g.2 You are using Fluent.
public function getUserByEmail($email)
{
return DB::table('myusertable')->where('email', '=', $email)->get();
}
Update: 19 Jun 2016
Thank you #skittles for pointing out that I have not clearly shown where the files should be placed. All the files are to be placed as per the namespace given. E.g. if the namespace is App\Extensions and the class name is SocialiteUserProvider then location of file is App\Extensions\SocialiteUserProvider.php. The App directory in laravel is the app folder.
Good tutorial for setting up laravel socialite here: https://mattstauffer.co/blog/using-github-authentication-for-login-with-laravel-socialite
Auth::login doesn't return a boolean value you can use attempt to do a Auth::attempt
if(Auth::login($authUser, true)) {
return Redirect::to('user/UserDashboard');
}
Follow the tutorial and do this, and just have middleware configured on the home route
$authUser = $this->findOrCreateUser($user);
Auth::login($authUser, true);
return Redirect::to('home');

Having trouble with using ORM inside of route::resource

I'm just following the book of Martin Bean to learn about Laravel 5. He start to teach about laravel with routers and after using some basic Route::get and Route::delete methods he gives a really short example of how to use Route::resource and he says that I let you to do this yourself :)
Structually there is no problem but I'm having trouble when I'm trying to pass ORM inside of the method.
Here is my CatsController.php
namespace firstApp\Http\Controllers;
use Illuminate\Http\Request;
use firstApp\Http\Requests;
use firstApp\Http\Controllers\Controller;
public function show(\firstApp\Cat $cat)
{
return $cat;
//return view('cats.show')->with('cat', $cat);
}
Here is how i use router
Route::resource('cats', 'CatsController');
And this is my Cat.php
-
namespace firstApp;
use Illuminate\Database\Eloquent\Model;
class Cat extends Model {
public $timestamps = false;
protected $fillable = ['name', 'date_of_birth', 'breed_id'];
public function breed(){
return $this->belongsTo('firstApp\Breed');
}
}
When I call http://localhost/firstApp/public/cats/2 an empty object is what I got..
What is the problem?
Thanks.
You're injecting a model into the show method but not doing any queries to get the result.
To fix the problem, change your code to something like this:
public function show(\firstApp\Cat $cat, $id)
{
return $cat->find($id);
}
Note that in the code above I also inject an $id into the show method, so when you hit the http://localhost/firstApp/public/cats/2 URL, 2 will be stored in this variable.
In most cases people from Laravel community do the same thing as follows:
public function show($id)
{
return \firstApp\Cat::find($id);
}
Good luck.

Categories