Hope someone can help me out here. I bootstrapped a Laravel 5.3 application and I'm writing JUnitTest in order to verifiy my API.
However, as long as I create only one function in the Test Class everything works fine:
class MobileAppTest extends TestCase {
function __construct()
{
parent::setUp();
}
public function testUserLogin()
{
$this->json('POST', '/mobile/auth', ['username' => 'empty', 'password' => 'demo'])
->seeJson([
'success' => true
]);
$this->json('POST', '/mobile/auth', ['username' => 'wrongUser', 'password' => 'demo'])
->seeJson([
'success' => false,
'state' => 2
]);
}}
When I start to add another function like this:
class MobileAppTest extends TestCase{
function __construct()
{
parent::setUp();
}
public function testUserLogin()
{
$this->json('POST', '/mobile/auth', ['username' => 'empty', 'password' => 'demo'])
->seeJson([
'success' => true
]);
$this->json('POST', '/mobile/auth', ['username' => 'wrongUser', 'password' => 'demo'])
->seeJson([
'success' => false,
'state' => 2
]);
}
public function testSecond()
{
}}
Suddenly the parameters of the HTTP POST call become empty. I have no idea why this is happening. On the server-log there is the following message poping up:
[2017-05-10 11:09:15] testing.INFO: array (
)
[2017-05-10 11:09:15] testing.ERROR: ErrorException: Undefined index: username
in app/Http/Controllers/Controller.php:36
But on the second call the parameters are present:
[2017-05-10 11:09:15] testing.INFO: array (
'username' => 'wrongUser',
'password' => 'demo',
)
Thanks for helping me out here.
Related
I'm implementing the password reset functionality described in the Laravel docs at https://laravel.com/docs/8.x/passwords. My method for resetting passwords is as follows:
public function doPasswordReset(Request $request)
{
$request->validate([
'token' => 'required',
'email' => 'required|email',
'password' => 'required|min:8|confirmed',
]);
$status = Password::reset(
$request->only('email', 'password', 'password_confirmation', 'token'),
function ($user, $password) {
$user->forceFill([
'password' => Hash::make($password)
]);
//remember token not needed
//->setRememberToken(Str::random(60));
$user->save();
event(new PasswordReset($user));
}
);
return $status === Password::PASSWORD_RESET
? redirect()->route('login')->with('status', __($status))
: back()->withErrors(['email' => [__($status)]]);
}
My test for this method is:
/** #test */
public function the_user_can_update_their_password()
{
ParentUser::factory()->create([
'email' => 'user#domain.com',
'password' => Hash::make('oldpassword')
]);
$token = Password::createToken(ParentUser::first());
Password::shouldReceive('reset')
->once()
->withSomeofArgs([
'email' => 'user#domain.com',
'password' => 'newpassword',
'password_confirmation' => 'newpassword',
'token' => $token
])
->andReturn(Password::PASSWORD_RESET);
$response = $this->post(route('password.update'), [
'email' => 'user#domain.com',
'password' => 'newpassword',
'password_confirmation' => 'newpassword',
'token' => $token
]);
$response->assertRedirect(route('login'));
//failures from here
$this->assertEquals(Hash::make('newpassword'), Hash::make(ParentUser::first()->password));
$this->assertNotEquals(Hash::make('oldpassword'), Hash::make(ParentUser::first()->password));
Event::fake();
Event::assertDispatched(PasswordReset::class, ParentUser::first());
}
My issue is that the last three assertations fail. I realize that this is happening because my mock is not calling the closure to effect the password change and raise the event. So my question is whether it is possible to mock some arguments of a function call.
One solution I am thinking is to factor out the closure into its own method and test that separately. In the absence of anything else, I think this may be the only way.
Props to #tim-lewis and #apokryfos who both provided answers to help. The following is the amended test which now passes.
#apokryfos stated that mocking the call shouldn't happen as we need to actuallly effect the password change.
#tim-lewis in his answer showed that a hash needs to be checked with the Hash::check() method
/** #test */
public function the_user_can_update_their_password()
{
ParentUser::factory()->create([
'email' => 'user#domain.com',
'password' => Hash::make('oldpassword')
]);
$token = Password::createToken(ParentUser::first());
Event::fake();
$response = $this->post(route('password.update'), [
'email' => 'user#domain.com',
'password' => 'newpassword',
'password_confirmation' => 'newpassword',
'token' => $token
]);
dump(ParentUser::first()->password);
$response->assertRedirect(route('login'));
$this->assertTrue(Hash::check('newpassword', ParentUser::first()->password));
Event::assertDispatched(PasswordReset::class);
}
Hash::make() doesn't generate the same Hash for any given value. Take a look:
Hash::make('newpassword') == Hash::make('newpassword')
// false
Since this is false, I would expect assetEquals() to fail here, since they values do not equal each other. Instead, take a look at the Hash::check() method:
Hash::check('newpassword', Hash::make('newpassword'))
// true
You can use assertTrue() against it instead, something like:
$this->assertTrue(Hash::check('newpassword', ParentUser::first()->password));
$this->assertFalse(Hash::check('oldpassword', ParentUser::first()->password));
Sidenote, ->password should already be Hashed, so there's no need to wrap that in another Hash::make(), as you'd then have a "Hash-of-a-hash", which would fail the Hash::check().
How can I to test user authentication? My test:
public function successLogin()
{
$user = User::factory()->create(['login' => '123']);
$user->makeVisible(['password']);
$this->post(route('login'), $user->toArray());
$this->assertAuthenticated(); // not asserts
}
I saw this decision
$this->User->makeVisible(['password']);
But what is this proterty? Model? It is undefined.
The decision was change post request of auth to attempt method:
public function successLogin()
{
$user = User::factory()->create(['password' => \Hash::make('pass')]);
\Auth::attempt(['login' => $user->login, 'password' => 'pass']);
$this->assertAuthenticated();
}
public function failedLogin()
{
User::factory()->create(['login' => 'username1', 'password' => \Hash::make('pass')]);
\Auth::attempt(['login' => 'username2', 'password' => 'pass']);
$this->assertGuest();
}
showing Constant expression contains invalid operations
i am new in laravel livewire when i was trying to pass function value it showing
Constant expression contains invalid operations.
im trying this function
'suppliers_master_id' => $this->generateRegistrationId(),
my code
<?php
namespace App\Http\Livewire\Purchase;
use App\Supplier;
use Livewire\Component;
class AddSuppliers extends Component
{
public $form = [
'supplier' => '',
'email' => '',
'phone' => '',
'address' => '',
'city' => '',
'state' => '',
'pincode' => '',
'GSTIN' => '',
'suppliers_master_id' => $this->generateRegistrationId(),
];
public function submit()
{
$this->validate([
'form.supplier' => 'required|string|max:255',
'form.email' => 'required|email',
'form.phone' => 'required|string|max:10',
'form.address' => 'required|string|max:255',
'form.city' => 'required|string|max:255',
'form.state' => 'required|string|max:255',
'form.pincode' => 'required|string|max:6',
'form.GSTIN' => 'required|string|max:255',
]);
Supplier::create($this->form);
session()->flash('message', 'Supplier Added successfully .');
return redirect()->to('/addsuppliers');
}
function generateRegistrationId() {
$id = 'SIIT_' . mt_rand(1000000000, 9999999999); // better than rand()
// call the same function if the id exists already
if ($this->registrationIdExists($id)) {
return $this->generateRegistrationId();
}
// otherwise, it's valid and can be used
return $id;
}
function registrationIdExists($id) {
// query the database and return a boolean
// for instance, it might look like this in Laravel
return Supplier::where('suppliers_master_id', $id)->exists();
}
public function render()
{
return view('livewire.purchase.add-suppliers');
}
}
That's not a livewire problem, you are not allowed in PHP to call a function to initialize a property.
In regular PHP you would assign this inside your __construct() method.
Since Livewire works a bit differently you have to use mount() instead.
public function mount()
{
$this->form['suppliers_master_id'] = $this->generateRegistrationId();
}
I'm using pre-built authentication controllers suggested larvel 5.4.
There is a validator function to validate incoming registration request like this :
protected function validator (array $data)
{
return Validator::make($data, [
'name' => 'required|string|max:255',
'family' => 'required|string|max:255',
'email' => 'required|string|email|max:255|unique:users',
'username' => 'required|string|max:255|unique:users',
'password' => 'required|string|min:6|confirmed',
'captcha' => 'required|captcha'
]);
}
Problem is When send an invalid data to registerController, laravel return all occurred errors as pre formatted json while I want to return only first error and in this format :
{
"success" : false ,
"msg" : "error Message comes here"
}
How can I do that ?
You may use the bail rule to stop running validation rules on an attribute after the first validation failure as mentioned in laravel documentation:
In you case you may try with the following:
protected function validator (array $data)
{
return Validator::make($data, [
'name' => 'bail|required|string|max:255',
'family' => 'bail|required|string|max:255',
'email' => 'bail|required|string|email|max:255|unique:users',
'username' => 'bail|required|string|max:255|unique:users',
'password' => 'bail|required|string|min:6|confirmed',
'captcha' => 'bail|required|captcha'
]);
}
simple example
App\Exceptions\Handler
public function render($request, Exception $e)
{
if ($e instanceof \Illuminate\Validation\ValidationException) {
return response()->json(['success'=>false,'msg'=>current($e->errors())]);
}
return parent::render($request, $e);
}
I am trying to seed some files, yet I get:
[Error Exception] Closure object cannot have properties
Not sure what is wrong, since I do very basic seeding.
Here are my files:
all.php in tests/factories/all.php
$factory('App\User', [
'name' => $faker->name,
'email' => $faker->email,
'password' => password_hash('000400', PASSWORD_DEFAULT)
]);
This is the command I am using:
php artisan db:seed --class="UserTableSeeder"
This is my UserTableSeeder:
public function run()
{
// User::create([
// 'name' => 'Rainbow Warrior',
// 'email' => 'email#exmaple.org',
// 'password' => password_hash('123456', PASSWORD_DEFAULT)
// ]);
TestDummy::times(20)->create('App\User');
}
I had a typo inside my all.php that looked like this:
$factory('App\Comment', [
'user_id' => 'factory:App\User',
'question' => $factory->sentence,
'answer' => $factory->text
]);
If you look closely, you will notice, that instead of $faker, I wrote $factory. Well... fair enough.