How to cover exception in code coverage report Laravel 8? - php

How can I catch any exceptions to cover the 100% code coverage report? This only covers the try condition in the code.
Controller
public function getItem()
{
try {
// Some code
return $result;
} catch (Exception $e) {
Log::error($e->getMessage());
throw new Exception ("$e->getMessage ()", 500);
}
}
Test file
public function testGetItem()
{
$this->get('api/getitem')->assertStatus(200);
}

Testing exceptions is easy in PHPUnit, but doesn't work like you'd expect in Laravel thanks to how it handles exceptions.
To test exceptions in Laravel you first need to disable Laravel exception handling - which if you extend the provided TestCase, you can do with the withoutExceptionHandling() method.
From there you can use PHPUnit's expectException() methods. Here's a small example.
use Tests\TestCase;
class ExceptionTest extends TestCase
{
public function testExceptionIsThrownOnFailedRequest()
{
// Disable exception handling for the test.
$this->withoutExceptionHandling();
// Expect a specific exception class.
$this->expectException(\Exception::class);
// Expect a specific exception message.
$this->expectExceptionMessage('Simulate a throw.');
// Expect a specific exception code.
$this->expectExceptionCode(0);
// Code that triggers the exception.
$this->get('/stackoverflow');
}
}
Now when the test is run, it'll disable Laravel's exception handling for this test run, then we set some expectations about what should happen, lastly, we call the code that'll fulfill those expectations, in this case, that's a get() call to route.
Now how the expectations are fulfilled will be up to your application.

Related

Symfony 4 custom exception handler for certain controllers

From my understanding there can only be one exception handler which will catch all unhandled exceptions. However, is there an elegant way to have a specific exception handler for Controllers A and B, and controllers C, D, etc. would be handled by default exception handler?
I don't think this is possible by default, other than creating a kernel eventlistener. However, you can create something simple yourself.
In a Controller Method, you might could do something like
public function index()
{
try {
// Do normal logic here.
} catch (\Exception $e) {
// Do whatever you want with any exception.
// Or Call your exception handler: MyExceptionHandler::handle()
}
}
You can also create a custom AbstractController so it might be a bit simpler. In which you extend the render function, so you abstract that logic away

PHPUnit Mocked method causes an exception

I have this function in a class
public function doSomething()
{
try {
$result = functionCall();
if ($result === true) {
$this->doGoodResult('With Success Message');
} else {
$this->doBadResult('ERROR - unable to do something');
}
} catch (Exception $e) {
$this->doBadResult('Did you divide by zero?');
}
}
and I'm trying to test it with mocking out the extra functions
public function test_doSomthingWithBadResultGetsDoBadResultCalled()
{
$ajc = $this->getMockBuilder('MyClass')
->setMethods(array('doBadResult'))
->setConstructorArgs(array('doSomething', array('input_var' => 0)))
->getMock();
$ajc->expects($this->once())
->method('doBadResult')
->willReturn(null)
->with($this->contains('ERROR - unable to do something'));
$ajc->doSomething();
}
and the problem is that I'm getting the following error from PHPUnit
MyClass::doBadResult('Did you divide by zero?') was not expected to be called more than once.
I'm not sure why, but it looks like the catch statement is being executed only when in this mock scenario.
Could anyone help me figure out why this is happening and especially how to set this up so that my test doesn't fail?
After some debugging I found the Exception was Invalid argument supplied for foreach() and after some digging, it was in the $this->contains('ERROR...')
the answer is $this->contains should be $this->stringContains
To prevent your need to do more digging in the future. One of the problems that you have in your code is that you are catching the Base Exception type.
} catch (Exception $e) {
$this->doBadResult('Did you divide by zero?');
}
When an assertion fails in PHPUnit, a PHPUnit_Framework_AssertionFailedError is thrown and is caught by PHPUnit. This error extends the Base PHP Exception class. So inside your try block when the mock object gets called, it checks the parameters that were used. Since these are not correct, it throws the PHPUnit_Framework_AssertionFailedError which your catch statement grabs. Which then calls the method on your mock that wasn't expected to be called.
Because of this, it would be possible for your test to end up passing incorrectly. Because the failed assertion gets caught and handled by your code. You should have a specific exception that your catch is looking for and any other exceptions get passed on to the appropriate level.

Disable Laravel's built-in error handling methods

Is there anyway to disable the Laravel error handler all together?
I want to simply display standard PHP errors, not the Whoops, looks like something went wrong errors.
Not without majorly violating the principles of the framework (which I'll tell you how to do below, if you're still interested).
There's a few things that make this difficult to accomplish. It's easy enough to unset the default error and exception handlers
set_error_handler(null);
set_exception_handler(null);
but that leaves you with two major hurdles.
The first is Laravel registers a shutdown handler as part of its bootstrapping, and this shutdown function will look for the last error, and if it was a fatal error, manually call the exception handling code. There's no easy way to un-register a shutdown function.
The second is, the main Laravel Application handler looks like this
#File: vendor/laravel/framework/src/Illuminate/Foundation/Application.php
public function handle(SymfonyRequest $request, $type = HttpKernelInterface::MASTER_REQUEST, $catch = true)
{
try
{
$this->refreshRequest($request = Request::createFromBase($request));
$this->boot();
return $this->dispatch($request);
}
catch (\Exception $e)
{
if ($this->runningUnitTests()) throw $e;
return $this['exception']->handleException($e);
}
}
That is -- if you application code throws an exception, Laravel catches it here and manually calls the exception's handleException method (which triggers the standard Laravel exception handling). There's no way to let PHP handle a fatal exception that happens in your application, Laravel blocks that from ever happening.
The part where I tell you how to do what you want
All this means we need to replace the main Laravel application with our own. In bootstrap/start.php, there's the following line
#File: bootstrap/start.php
$app = new Illuminate\Foundation\Application;
Replace it with the following
ini_set('display_errors','1');
class MyApplication extends Illuminate\Foundation\Application
{
function startExceptionHandling()
{
//do nothing
}
public function handle(Symfony\Component\HttpFoundation\Request $request, $type = HttpKernelInterface::MASTER_REQUEST, $catch = true)
{
$this->refreshRequest($request = Request::createFromBase($request));
$this->boot();
return $this->dispatch($request);
}
}
$app = new MyApplication;
The first thing we're doing is setting PHP's display errors ini to 1. This makes sure errors are output to the browser.
Next, we're defining a new application class that extends the real application class.
Finally, we replace the real Laravel $app object with an object instantiated by our class.
In our application class itself we blank out startExceptionHandling. This prevents Laravel from setting up custom exception, error, and shutdown callbacks. We also define handle to remove the application boot/dispatch from a try/catch. This is the most fragile part of the process, and may look different depending on your Laravel version.
Final Warnings
If the handle method changes in future version of Laravel this will break.
If custom packages rely on adding custom exception handlers, they may break.
I'd recommend staying away from this as anything other than a temporary debugging technique.
Then set 'debug' => false, in \config\local\app.php file
<?php
return array(
'debug' => false,
);
In laravel 5 to disable debug, you just need to comment
//'debug' => env('APP_DEBUG'),
in \config\app.php file
Exception handling is hardcoded into the Application class. You can override the classes in your bootstrap/start.php file:
class ExceptionHandler {
public function handleException($exception) {
throw $exception;
}
public function handleConsole($exception) {
throw $exception;
}
}
class MyApplication extends Illuminate\Foundation\Application
{
public function registerExceptionProvider() {}
public function startExceptionHandling() {}
}
$app = new MyApplication;
It should go without saying that this is definitely not encouraged.
In file .env, just change:
APP_DEBUG=true
To:
APP_DEBUG=false
Related to the question: Laravel has a built in method for disabling exceptions in unit tests:
$this->withoutExceptionHandling()
Laracast on the subject: https://laracasts.com/series/whats-new-in-laravel-5-5/episodes/17
This will get you close. There might be a more proper way of doing this. It replaces Laravel's current exception handler with a single-method class. I haven't tested this, besides the basic route below, so you may need to add other methods to satisfy different situations.
class ExceptionHandler
{
public function handleException($e)
{
echo $e;
die;
}
}
App::bind('exception', App::share(function($app)
{
return new ExceptionHandler;
}));
Route::get('/', function()
{
throw new Exception('Testing');
});

in PHPUnit how to force tearDownAfterClass() to run in case of unexpected exceptions

So every time I encounter unexpected exceptions in PHPUnit (such as fails to insert into db because of an integrity check) my tests fail and it errors out without running tearDownAfterClass() function. This will leave my database in a messy state so I have to manually go and clean it up. Is there a way to ensure tearDownAfterClass() is always executed?
PHPUnit will call tearDownAfterClass even when there are errors and failures in test methods. It will not call it when setUpBeforeClass throws an exception. In order to ensure that your database is cleaned up, move the cleanup code into a new method that you call from tearDownAfterClass and the catch clause in setUpBeforeClass.
function FooTest extends PHPUnit_Framework_TestCase
{
static function setUpBeforeClass() {
try {
... setup code that might fail ...
}
catch (Exception $e) {
self::cleanupDatabase();
throw $e; // so the tests will be skipped
}
}
static function tearDownAfterClass() {
self::cleanupDatabase();
}
static function cleanupDatabase() {
... clean ...
}
... test methods ...
}
You can override the function
protected function onNotSuccessfulTest(Exception $e)
with for instance;
$this->tearDownAfterClass();
throw $e;

php symfony exception handling/error handling

Working on a symfony application that uses nusoap (is this the best method for integrating soap work with php/symfony?) for taking credit card payments.
I've simplified an example of my code below.
What I'm struggling with is the best way to handle exceptions. The example below only has 1 custom exception (where should my custom exceptions reside within the directory structure of symfony? (lib/exception?)) But what happens when there are several different types of exceptions that handle a specific error? It's not very elegant to have a try/catch block with 20 odd exceptions.
I'm also not sure of where I should be throwing and catching. I need to set some user flashes to alert the user of any problems, so I figure the catching should be done in the actions controller rather than within the class that handles the soap call.
Could anyone please advise where I might be going wrong?
I hate messy code/solutions and want to stick to the DRY principle as much as possible. I think I might also be missing some built in symfony functionality that might help with this but whenever I search I usually find examples that are for symfony 1.2, I'm using 1.4.
Some examples would be great, thanks.
lib/soap_payment.class.php
class SoapPayment
{
public function charge()
{
/*assume options are setup correctly for sake of example*/
try
{
$this->call();
}
catch (SoapPaymentClientFaultException $e)
{
/* should this be caught here? */
}
}
private function call()
{
$this->client->call($this->options);
if ($this->client->hasFault())
{
throw new SoapPaymentClientFaultException();
}
}
}
apps/frontend/payment/actions/actions.class.php
class paymentActions extends sfActions
{
public function executeCreate(sfWebRequest $request)
{
/* check form is valid etc */
$soap_payment = new SoapPayment();
try
{
$soap_payment->charge();
}
catch (SoapPaymentClientFaultException $e)
{
/* or throw/catch here? */
$this->getUser()->setFlash('error', ...);
$this->getLogger()->err(...);
}
/* save form regardless, will set a flag to check if successful or not in try/catch block */
}
}
One not very well known feature of Symfony is that exceptions can manage the content sent in a response. So you could do something like this:
class SoapException extends sfException
{
public function printStackTrace() //called by sfFrontWebController when an sfException is thrown
{
$response = sfContext::getInstance()->getResponse();
if (null === $response)
{
$response = new sfWebResponse(sfContext::getInstance()->getEventDispatcher());
sfContext::getInstance()->setResponse($response);
}
$response->setStatusCode(5xx);
$response->setContent('oh noes'); //probably you want a whole template here that prints the message that was a part of the SoapException
}
}
If you need a cleaner handling of SOAP exceptions, like setting flashes, etc. you'll probably have to catch each exception. One idea here might be to create a generic SoapException class that is extended by more specific SoapExceptions so you don't have to catch a bunch of different types. The above code may be a useful fallback mechanism as well.
Finally, yes, you should place custom exceptions in lib/exception.

Categories