The exception is thrown correctly in the program, but the test does not detect it. Why??
public function null_user_send()
{
$message = 'Hi';
$response = $this->post(route('dialogSend'), ['to' => -1, 'message' => $message]);
$this->assertDatabaseMissing('messages', ['to' => -1, 'message' => $message]);
$this->expectException(Exception::class);
$response->assertStatus(500);
}
MailController:
/** #throws \Exception */
public function dialogSend(Request $request)
{
$handler = app(MailHandler::class);
if ($request->input('to') <= 0) {
throw new \Exception('Параметр "to" имел отрицательное значение', 500);
}
...
}
Test response: 'Failed asserting that exception of type "Exception" is thrown.'
And it doesn't matter what class of exceptions, nothing works. Pls help
Check the namespace of the exception you are testing
$this->expectException(Exception::class);
probably should be
$this->expectException(\Exception::class);
This is because your test itself sits in its own namespace (depend on the type of test). What your test is may actually be seeing is
$this->expectException(\Tests\Exception::class);
which will of course never be thrown since it doesn't exist.
One of the reasons you are having trouble diagnosing this is the nature of your tests. There are a few things there, while not strictly wrong, are going to make your testing that much harder to determine the cause of your problem.
You're trying to test too much at once. Separate each concern into its own test so that you can first determine if the database is empty, then if it throws an exception, then if it returns a 500 error. You are essentially testing three different aspects of your application here.
public function null_user_send()
{
// Arrange
$message = 'Hi';
// Act
$response = $this->post(route('dialogSend'), ['to' => -1, 'message' => $message]);
// Assert
$this->assertDatabaseMissing('messages', ['to' => -1, 'message' => $message]);
}
public function does_an_exception()
{
// Arrange, Act
...
// Assert
$this->expectException(Exception::class);
}
public function returns_server_erro()
{
// Arrange, Act
...
// Assert
$response->assertStatus(500);
}
Give your tests meaning in their names. This will help you immensely in the future when you can't remember what something is meant to do verus what it is actually doing.
public function it_should_not_have_a_message_if_the_input_is_wrong()
{
// database test
}
public function it_should_throw_an_exception_if_the_input_is_wrong()
{
// exception test
}
public function it_should_return_a_500_error_if_the_input_is_wrong()
{
// http code test
}
A good test should be able to answer the following question:
If I set up my application like this, and then I do that, then my application should now look like this.
This is where the Arrange (organise my application), Act (do something) and Assert (check my application) comes from in the code above.
There are couple of other things you might to look at, although this will be up you depending on your needs.
You look like you're doing validation on input here. You should probably throw a 400 or 422 error if your input is wrong
The 500 error code on your exception probably won't really do anything, it won't be related to the 500 HTTP status code, unless you have something in your error handling that does that.
Related
i have code.
try {
$this->entityManager->beginTransaction();
$this->repo->remove($something);
$this->repoTwo->delete($something);
$this->entityManager->commit();
} catch (Exception $e) {
$this->entityManager->rollback();
throw new Exception($e->getMessage(), 0, $e);
}
And now, i want to test, if there is still record in database, after exception, how i can do that, if test wont work after exception is expected?
$this->expectException(Exception::class);
$this->expectExceptionMessage('xxxx');
app(Command::class)->handle();
$this->seeInDatabase($table, [
'id' => $media->id(),
]);
How i can do that? Thanks.
Normally you might create two tests. One that tests an exception was thrown and one that depends on the first test and tests the record still exists, but in this case the database would be reset before each test, including test dependencies so it won't work as you might expect.
But you can still do two tests and have one depend on the other, however you need to re-run the same code in both test (because the database would be reset in between the tests). The "depends" in this case is merely documenting that one test is associated with the other.
public function testOne()
{
$this->expectException(Exception::class);
$this->expectExceptionMessage('xxxx');
app(Command::class)->handle();
}
/**
* #depends testOne
*/
public function testTwo($arg)
{
app(Command::class)->handle();
$this->seeInDatabase($table, [
'id' => $media->id(),
]);
}
If you really want to end-to-end test it, and do the assertions in the same test, then you can use a try ... catch block and test it procedurally.
public function testException()
{
try {
app(Command::class)->handle();
} catch (\Exception $e) {
// Make sure you catch the specific exception that you expect to be
// thrown, (e.g. the exception you would normally specify in the
// expectException method: $this->expectException(Exception::class);
// Assert the exception message.
$this->assertEquals('xxxx', $e->getMessage());
// Assert database still contains record.
$this->seeInDatabase($table, [
'id' => $media->id(),
]);
return;
}
// If the expected exception above was not caught then fail the test.
$this->fail('optional failure message');
}
I'm on a project where I don't want to throw errors directly at the user. Instead I want customized messages for the error that accur.
For later I also need to keep an error number in order to customize the error messages from outside the class, like an array of error messages.
So I made my own thing where I set $error = null and then later set error to a number that later becomes a message.
Question
Is there any disadvantages with this approach? Am I better of with try/catch or something else? I would like to keep the code short and tidy if possible.
In this short code example the error handling seems to be a big part of the class. In my real code which is a few hundred lines, it's not a big part of the whole code
http://sandbox.onlinephpfunctions.com/code/623b388b70603bf7f020468aa9e310f7340cd108
<?php
class Project {
private $error = null;
public function callMeFirst($num) {
$this->nestedLevelOne($num);
$this->nestedLevelTwo($num);
$this->setResults();
}
public function callMeSecond($num) {
$this->nestedLevelTwo($num);
$this->setResults();
}
private function nestedLevelOne($num) {
// Do stuff
if($num !== 1) {
$this->error = ['id' => 1, 'value' => $num];
}
}
private function nestedLevelTwo($num) {
// Do stuff
if($num !== 20) {
$this->error = ['id' => 2, 'value' => $num];
}
}
private function message($args) {
extract($args);
$message = [
1 => "Nested level one error: $value",
2 => "Another error at level two: $value",
];
return ['id' => $id, 'message' => $message[$id]];
}
private function setResults() {
$results['success'] = ($this->error === null) ? true : false;
if($this->error !== null) {
$results['error'] = $this->message($this->error);
}
$this->results = $results;
}
}
$project = new Project();
$project->callMeFirst(1);
$project->callMeFirst(2);
print_r($project->results);
It will output
Array
(
[success] =>
[error] => Array
(
[id] => 2
[message] => Another error at level two: 2
)
)
The reason I'm asking is that I have a feeling of that I may reinvent the wheel in this case. Am I?
If there is a better solution, I would be thankful to see how that code looks like.
I would probably separate the business logic from the error handling to simplify each part more. By using exceptions, you keep your business logic simpler; you simply throw an exception whenever you encounter a case that is not permitted, thereby preventing getting into any sort of inconsistent state at all. The business logic class doesn't have to care about how this error will be processed further, it just needs to raise the error. You should then create a separate wrapper around that business logic class which simply cares about handling any errors and formatting them into an array or other sort of response which will be handled elsewhere. Something along these lines:
class ProjectException extends Exception {
public function __construct($num) {
parent::__construct(get_called_class() . ": $num");
}
}
class NestedLevelOneException extends ProjectException {
// customise __construct here if desired
}
class NestedLevelTwoException extends ProjectException {}
class Project {
public function callMeFirst($num) {
$this->nestedLevelOne($num);
$this->nestedLevelTwo($num);
}
public function callMeSecond($num) {
$this->nestedLevelTwo($num);
}
protected function nestedLevelOne($num) {
if ($num !== 1) {
throw new NestedLevelOneException($num);
}
// do stuff
}
protected function nestedLevelTwo($num) {
if ($num !== 20) {
throw new NestedLevelTwoException($num);
}
// do stuff
}
}
class ProjectService {
protected $project;
public function __construct(Project $project = null) {
$this->project = $project ?: new Project;
}
public function process($a, $b) {
try {
$this->project->callMeFirst($a);
$this->project->callMeSecond($b);
return ['success' => true];
} catch (ProjectException $e) {
return ['success' => false, 'error' => $e->getMessage()];
}
}
}
$api = new ProjectService;
print_r($api->process(1, 2));
By defining three separate exceptions, you get a lot of flexibility in how and where you want to handle errors. You can specifically catch NestedLevel*Exception, or you catch either of them with ProjectException.
By having your methods throw exceptions, you gain flexible error handling possibilities. You are free to not catch the exception and have the program terminate, as would be entirely reasonable if one of your business requirements isn't met. Alternatively, you can catch the exception at a level up that is prepared to deal with that error and turn it into something meaningful that can be acted upon.
By moving the generation of the error message into the exceptions, you keep the error type and its message self-contained. There's exactly one place where you define what kind of error may happen and what its error message will look like; instead of spreading that out over your entire codebase. And you're still free to choose some other error message in the UI, e.g. for localising different kinds of errors into multiple languages; just check the type of the exception object.
By using a separate ProjectService which cares about handling those exceptions and turning it into an array response, you narrow each class' responsibilities and make each class more flexible and simpler.
What is the best way to return errors from a PHP function, when the function has executed normally?
Example
public function login($user, $pw){
if(!$this->verifyUser($user))
// return error about invalid user
}
else if (!$this->verifyPw($pw)){
// return error about invalid pw
}
else {
// return OK
}
}
Caller - Return response as JSON for web UI
public function doLogin($user,$pw){
$res = $this->login($user, $pw);
return json_encode($res);
}
On one hand I could understand returning results as an array, but I feel like this does not make sense for such low level functions. Perhaps they should return error codes and then caller must lookup the error code string?
Assuming you are in an object, you basically have three major options:
store errors in something like $this->errors array and return false
have some kind of error-collector as a dependency for object, where you
call $this->collector->addError('blah blah'); and return false
throw an exception
For the first two approaches, you will have to check the return value, and based on that, pull the list of errors. But both of those options have the benefit of being able to collect multiple errors.
The exception approach is a bit lighter on coupling, but you can only get one error.
As for what to actually return, I would recommend going with error code + description string. But that string would not be returned by your class. Instead your error should be registered using some "placeholder", that later is translated:
$this->errors[] = [
'code' => 52,
'msg' => 'authentication.login.invalid-password',
];
When you pull the errors from your object, it would be basically a list of entries like this, And then you just run them through your translation service.
In a case of exception, that same information would reside in $e->getCode() and $e->getMessage(), when your object throws InvalidPassword exception.
For an API response the answer from tereško would be along the correct lines.
For a DOM response you can do the following:
I have used a response code only in the past for something so simple:
http://php.net/manual/en/function.http-response-code.php with code 401
public function login($user, $pw) {
header_remove(); # Clear all previous headers.
if( !$this->verifyUser($user) || !$this->verifyPw($pw) ){
http_response_code(401);
exit;
}
http_response_code(200);
exit;
}
jQuery:
$.ajax({
.......
statusCode: {
200: function() {
window.location.href = '/';
},
401: function() {
alert( "Login Failed" );
}
}
});
I've got the following function in my controller that handles preparing and loading my home page.
public function index()
{
// GetBalance
$current_balance = $this->PayPal->getBalance();
if(Session::has('errors'))
{
return Redirect::to('error');
}
// TransactionSearch
$params = array(
'number_of_days' => 1
);
$recent_history = $this->PayPal->transactionSearch($params);
if(Session::has('errors'))
{
return Redirect::to('error');
}
// Make View
$data = array('current_balance' => $current_balance, 'recent_history' => $recent_history);
return View::make('index')->with('data', $data);
}
As you can see, I'm making 2 different calls to the PayPal API through my model, and after each one I'm checking for an error. When errors do occur I flash the error messages and redirect to an error page accordingly.
I'd like to improve upon that so I don't have to keep using this same snippet of code over and over again when I'm making a bunch of calls prior to loading a view.
if(Session::has('errors'))
{
return Redirect::to('error');
}
I tried moving this to its own function...
public function errorCheck()
{
if(Session::has('errors'))
{
return Redirect::to('error');
}
}
Then, I thought I could just do this within my index function...
// GetBalance
$current_balance = $this->PayPal->getBalance();
$this->errorCheck();
That doesn't work, though, I guess because errorCheck() is simply returning a value and not actually triggering the redirect, so I just end up at my home page with an error because none of the data it expects exists (since the API calls failed).
Any info on what I need to do here so that my errorCheck() function simply triggers the redirect when it should would be greatly appreciated.
Thanks!
The only way I can think of to avoid this is via the use of exceptions.
With your errorCheck() method you could try this:
// GetBalance
$current_balance = $this->PayPal->getBalance();
return $this->errorCheck();
but that's not what you want... it's going to exit your method after the first call. What you really need is a way to catch an error wherever it occurs and handle it - and that's what exceptions do.
I'm going to assume your PayPal class is a third-party package and you can't rewrite it to throw exceptions. Given that assumption, what you can do is this:
Rewrite your errorCheck() method like so:
public function errorCheck()
{
if(Session::has('errors'))
{
throw new Exception("Problem with Paypal!");
}
}
Then wrap all your Paypal access code in a try/catch block:
public function index()
{
try {
// GetBalance
$current_balance = $this->PayPal->getBalance();
$this->errorCheck();
// TransactionSearch
$params = array(
'number_of_days' => 1
);
$recent_history = $this->PayPal->transactionSearch($params);
$this->errorCheck();
// Make View
$data = array('current_balance' => $current_balance, 'recent_history' => $recent_history);
return View::make('index')->with('data', $data);
} catch(Exception $e) {
return Redirect::to('error');
}
}
Each time you call errorCheck() it will check for an error and throw an exception. In that case, execution will immediately jump to the catch block and redirect to the error page.
A better solution would be to throw the exceptions closer to the source of the error, ie. somewhere in the Paypal class when the error occurs. The idea here is that the exception includes a lot of useful information telling you what happened, like a stack trace. In the code I've given, the stack trace is going to show that the exception was thrown in the errorCheck() method which, while true, is not really helpful. If the exception could be thrown somewhere in the Paypal class, it would give you a better indication of what really went wrong.
While throwing an error is definitely the way to go, I'd say you go a step further and generalize the redirect. Instead of doing that try catch block every time the PayPal API is called, you can use App::error to do the redirect globally.
Create an exception class somewhere appropriate:
class PayPalApiException extends Exception {}
Then in your start/global.php add this (before the other App::error call):
App::error(function(PayPalApiException $exception)
{
return Redirect::to('error');
});
Then your code in the controller can become much simpler:
public function index()
{
$current_balance = $this->PayPal->getBalance();
$this->errorCheck();
$recent_history = $this->PayPal->transactionSearch([
'number_of_days' => 1
]);
$this->errorCheck();
$data = compact('current_balance', 'recent_history');
return View::make('index')->with('data', $data);
}
protected function errorCheck()
{
if (Session::has('errors'))
{
throw new PayPalApiException("Problem with Paypal!");
}
}
Why do you even need to check for the error twice? It doesnt seem to be related to each call? i.e. if doesnt seem to matter if the balance call fails, because you dont use the result in the transaction search.
I'd just do this
public function index()
{
// GetBalance
$current_balance = $this->PayPal->getBalance();
// TransactionSearch
$recent_history = $this->PayPal->transactionSearch(array('number_of_days' => 1));
if(Session::has('errors'))
{
return Redirect::to('error');
}
else
{
return View::make('index')->with('current_balance', $current_balance)
->with('recent_history', $recent_history);
}
}
For our PHPUnit testing, we sometimes write custom assertions. Today I found a custom assertion that wasn't asserting quite what it ought to have been. It seems that this problem could have been avoided if I had written a unit test for the assertion itself.
The only problem I see is that I'm not quite sure how to handle writing tests for an assertion that it ought to fail, without having that lead to the test itself failing. In other words, for a test that expects a string, 'foo', I want to do something like:
public function testAssertFoo()
{
$var = 'bar';
$callable = array( $this, "assertFoo" );
$this->assertTestFails( $callable, $var );
}
Of course, there is no assertTestFails assertion. But is there a clean way to do something like that?
Assuming that assertFoo uses PHPUnit's built-in assertions such as assertEquals, you can simply catch the PHPUnit_Framework_ExpectationFailedException that is thrown when the assertion fails.
function testAssertFoo() {
try {
$this->assertFoo('bar');
self::fail("assertFoo should fail for 'bar'");
}
catch (PHPUnit_Framework_ExpectationFailedException $e) { /* test passed */ }
}
function assertFoo($value) {
self::assertEquals('foo', $value);
}