I want to test a missing record in the database using Laravel and PHPUnit.
I have setup my tests and set that the method throws an exception. The test gives an error, because the exception has been thrown.
This is my test:
/** #test */
public function myTest()
{
$this->withoutExceptionHandling();
$this->_createMemberRequest(['email_address' => $this->email_address]);
}
The testresults:
There was 1 error:
1) myTest
Exceptions\InvalidEmailException: Email already exists
What am I doing wrong?
Thanks for help!
The whole point of $this->withoutExceptionHandling(); is when you get a funky error and you dont know what to do or the error is not clear enough but if you are trying to test your form validations ('name' => 'required') or something like that when storing a request then you have to remove $this->withoutExceptionHandling(); to get your test passed.
Related
I have an error, like the error below:
Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException
The GET method is not supported for this route. Supported methods: PUT.
I know that's a error from routes, but I can't see anything in laravel.log.
Here I saw that Laravel doesn't report some things, but my Exceptions/Handler.php file is like this:
protected $dontReport = [
//
];
How do I report everything in laravel.log?
Laravel 6
All exceptions are handled by the App\Exceptions\Handler class. Use the report method to log exceptions.
public function report(Throwable $exception)
{
Log::error($exception); // add this line here
parent::report($exception);
}
See more from docs here
Edited:
I have a custom exception with render method which is being called when I throw it e.g. from controller, but not being called when I throw it in View composer.
So when I do something like that
public function compose(View $view)
{
throw new CustomException();
}
and put dd() to exception render method
public function render()
{
dd('render is called');
}
I get no result.
If I log my exception directly, finds out that first the CustomException being thrown, then as the result I see ErrorException.
I found a place where it being thrown.
\Illuminate\View\Engines\CompilerEngine::handleViewException
protected function handleViewException(Exception $e, $obLevel)
{
$e = new ErrorException($this->getMessage($e), 0, 1, $e->getFile(), $e->getLine(), $e);
parent::handleViewException($e, $obLevel);
}
I didn't found any mentions in Laravel docs about that case.
I found a tread on github with the same issue: https://github.com/laravel/framework/issues/24658
So the question is, is this expected? Is there any adequate way to avoid this behaviour?
Edit
So, as you know, any exception during view compilation is intercepted and rethrown as ErrorException or as FatalThrowableError.
What you can do is intercept ErrorException and check if ($e->getPrevious() instanceof \CustomException) if so, you do your code, else, let the handler continue.
So I've found working solution for myself.
I've extended CompilerEngine and added additional processing in order to not throw ErrorException when I don't want to.
The important thing is - your resulting Exception must be inherited from ErrorException. Otherwise you will face multiple calls to \App\View\Engines\CompilerEngine::handleViewException which can break your logic and write multiple log entities to your log file.
I have index method in controller like this
public function index()
{
$this->authorize('index', Contact::class);
....
}
and index method in ContactPolicy
public function index()
{
return Auth::user()->can('view_Contact');
}
and test method like this
/** #test */
public function a_user_without_permission_can_not_see_contacts()
{
$this->login(['no_permission']);
$this->get('/contacts')
->assertStatus(403);
}
When I run my test show me this error
1) Tests\Feature\AccountTest::a_user_without_permission_can_not_see_contacts
Illuminate\Auth\Access\AuthorizationException: This action is unauthorized.
Note1:
When I change My controller to this it work correctly and show me green
public function index()
{
if(Auth::user()->can('view_Contact')){
........
}else
return response()->view('403',['msg' => 'you_not_have_not_permission'])->setStatusCode(403) ;
Note2:
In login method in test class I send user permissions parameters and it work correctly.
the authorize method throws an exception if the user is not authorized. you need to use #expectesException or similar methods to signal your test to expect an exception.
Or, you can add a condition to your Handler class to catch this kind of exception and return a 403 response
edit: more explanation
when you call authorize method and the user does not have permission, the program throws an exception.
when a user is calling the api, the exception is translated to a response with 403 status and shown to the user, but when you are calling the api from within the program itself, the exception is thrown and the program halts, hence there would be no JSON response whatsoever.
To handle this situation, you have two options:
1- if you decide not to change your program, then instead of expecting a response with a specific status, you should tell your test to expect an AuthorizationException thrown. you can read how to do this hear
2- you can change your code so that instead of delegating the task of handling the exception to Laravel, you do it yourself in the Handler class and prepare a JSON response with 403 status and then your test should be run correctly.
I have turned throw Exception in handler.php so that I can catch exceptions and see the errors but what happened is when I try to do the validation checks it throws me an exception which is correct but in my test case instead of catching the exception i'm asserting that the session has errors.
/** #test*/
public function a_thread_requires_a_title(){
$thread = make('App\Thread', ['title'=> null]);
$this->post('threads', $thread->toArray())
->assertSessionHasErrors('title');
}
Since, validation error is an exception so it throws me an exception because I've modified the handler.php file as
if(app()->environment() === "testing") throw $exception;
so, what I'm trying to do is change the env for this one test so that it wont throw me an 'Exception'
There are 2 helper methods which you can write at the top of your test method:
$this->withoutExceptionHandling(); and $this->withExceptionHandling();
They are included in Laravel's 'Illuminate\Foundation\Testing\Concerns\InteractsWithExceptionHandling' trait which is used by the abstract TestCase that you should be extending from your test. (as mentioned here)
/** #test*/
public function a_thread_requires_a_title() {
$this->withExceptionHandling();
$thread = make('App\Thread', ['title'=> null]);
$this->post('threads', $thread->toArray())
->assertSessionHasErrors('title');
}
I am writing unit test for a code that sends email with Mail::queue function, like the one in the documentation: https://laravel.com/docs/5.4/mocking#mail-fake
My Test:
/** #test */
public function send_reminder()
{
Mail::fake();
$response = $this->actingAs($this->existing_account)->json('POST', '/timeline-send-reminder', []);
$response->assertStatus(302);
Mail::assertSent(ClientEmail::class, function ($mail) use ($approver) {
return $mail->approver->id === $approver->id;
});
}
Code being Tested:
Mail::to($email, $name)->queue(new ClientEmail(Auth::user()));
Error Message:
The expected [App\Mail\ClientEmail] mailable was not sent.
Failed asserting that false is true.
The email is sent when I manually test it, but not from Unit Test. I'm thinking it might be because I am using Mail::queue instead of Mail::send function.
In .env file, I have
QUEUE_DRIVER=sync and MAIL_DRIVER=log
How can I test Mail::queue for Laravel?
Found the solution:
The problem was that the class was not imported at the top of PHPUnit test file.
Importing the mail class solved the issue. Strange how it didn't error out the testcase itself.