Continue test after expect exception phpunit - php

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');
}

Related

throw new exception laravel doesnt work in expectException PHP unit

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.

testing exceptions with phpunit

i am trying to test a function when i know error i going to be thrown. the function looks like this:
function testSetAdsData_dataIsNull(){
$dataArr = null;
$fixture = new AdGroup();
try{
$fixture->setAdsData($dataArr);
} catch (Exception $e){
$this->assertEquals($e->getCode(), 2);
}
$this->assertEmpty($fixture->ads);
$this->assertEmpty($fixture->adIds);
}
Now i am trying to use the phpunit exceptions assertions methods to replace the try catch part but i can't figure out how to do that.
i did lots of reading including this post PHPUnit assert that an exception was thrown? but i couldnt really understand how it shuold be implemented.
i tried something like this:
/**
* #expectedException dataIsNull
*/
function testSetAdsData_dataIsNull(){
$dataArr = null;
$fixture = new AdGroup();
$this->setExpectedException('dataIsNull');
$fixture->setAdsData($dataArr);
$this->assertEmpty($fixture->ads);
$this->assertEmpty($fixture->adIds);
}
but obviously it didn't work and i got this error:
1) adGroupTest::testSetAdsData_dataIsNull
ReflectionException: Class dataIsNull does not exist
what am i doing wrong and how exactly can i assert if exception was thrown plz?
I generally use the #expectedException annotations for just such cases. See all exception-related annotations here:
/**
* #expectedException \Exception
* #expectedExceptionCode 2
*/
function testSetAdsData_dataIsNull()
{
$dataArr = null;
$fixture = new AdGroup();
$fixture->setAdsData($dataArr);
}
Checking that $fixture->ads is really null doesn't really add up here, you can add these asserts prior to the call that actually triggers an exception:
$this->assertNull($fixture->ads);
$fixture->setAdsData($dataArr);//throws exception
You're unit testing. This test serves a clear purpose: it makes sure an exception is thrown in a given situation. If it does, then that's where the test ends.
Still, if you want to keep those assertEmpty calls, you could do this:
try {
$fixture->setAdsData($dataArr);
$e = null;
} cathc (Exception $e) {}
$this->assertEmpty($fixture->ads);
$this->assertEmpty($fixture->adIds);
if (!$e instanceof \Exception) {
//if the exception is not thát important:
$this->markTestIncomplete('No Exception thrown');
//do other stuff here... possibly
$this->fail('The exception was not thrown');
}
throw $e;//throw exception a bit later
An alternative approach would be to call $this->setExpectedException manually as explained here. Since we don't seem to know/care what the exception message will look like, I'm going to use the setExpectedExceptionRegExp method:
$fixture = new AdGroup();
$this->setExpectedExceptionRegExp(
//exception class, message regex, exception code
'Exception', '/.*/'. 2
);
$fixture->setAdsData(null);//passing null seems to be what you're doing anyway

executing assertions after exception is thrown using phpunit

i started using phpunit and came across this issue im wondering what is the best way to deal with it. I am testing the following function:
/**
* #expectedException PHPUnit_Framework_Error
*/
function testSetAdsData_dataIsNull()
{
$dataArr = null;
$fixture = new AdGroup();
$fixture->setAdsData($dataArr);
$this->assertEmpty($fixture->ads);
$this->assertEmpty($fixture->adIds);
}
now the line $fixture->setAdsData($dataArr); throws an exception as i want and that's ok, but the problem is the following two assertions won't execute. so i read about it and understood that if i want to execute the two following assertions i need to use try/catch , so my question is,
what is the correct way to do that?
i tried doing this:
/**
* #expectedException PHPUnit_Framework_Error
*/
function testSetAdsData_dataIsNull()
{
$dataArr = null;
$fixture = new AdGroup();
try{
$fixture->setAdsData($dataArr);
} catch (Exception $e){
$this->assertEmpty($fixture->ads);
$this->assertEmpty($fixture->adIds);
}
}
but now the exception is not thrown. should i leave it this way and just remove the expectation part from the top or is there a better way to do that?? thx
Yes, you need to get rid of the expectedException annotation. You're catching the exception so it won't be thrown now. Indeed, there is another problem: now, if the Exception is not thrown, the test will pass.
The solution is use a return sentence after the assertions, and making the test fail manually when the Exception is not thrown:
function testSetAdsData_dataIsNull()
{
$dataArr = null;
$fixture = new AdGroup();
try {
$fixture->setAdsData($dataArr);
} catch (Exception $e){
$this->assertEmpty($fixture->ads);
$this->assertEmpty($fixture->adIds);
return;
}
$this->fail('Exception not thrown');
}
Another recommendation: use Exception subclasses. If an Exception is thrown but not for the reason you expect, the test will pass but maybe the behaviour is not the expected in the real code. If you catch only a certain type of exception, you will be sure that the Exception was raised for the right reason.

Laravel DB::transaction() return value

It's my first time to use DB::transaction() but how exactly does it work if a transaction fails or is successful? In the example below, do I have to manually assign a value to return true, or if it fails will the method either return false or totally exit the transaction (therefore skipping the rest of the code)? The docs aren't so helpful on this.
use Exception;
use DB;
try {
$success = DB::transaction(function() {
// Run some queries
});
print_r($success);
} catch(Exception $e) {
echo 'Uh oh.';
}
Solution
I wrote down this solution for others who might be wondering.
Since I was more concerned about returning a boolean value depending on the success of my query, with a few modifications it now returns true/false depending on its success:
use Exception;
use DB;
try {
$exception = DB::transaction(function() {
// Run queries here
});
return is_null($exception) ? true : $exception;
} catch(Exception $e) {
return false;
}
Take note that the variable $exception is never returned since if something goes wrong with your query, the catch is immediately triggered returning false. Thanks to #ilaijin for showing that an Exception object is thrown if something goes wrong.
By giving a look at function transaction it does its process inside a try/catch block
public function transaction(Closure $callback)
{
$this->beginTransaction();
// We'll simply execute the given callback within a try / catch block
// and if we catch any exception we can rollback the transaction
// so that none of the changes are persisted to the database.
try
{
$result = $callback($this);
$this->commit();
}
// If we catch an exception, we will roll back so nothing gets messed
// up in the database. Then we'll re-throw the exception so it can
// be handled how the developer sees fit for their applications.
catch (\Exception $e)
{
$this->rollBack();
throw $e;
}
So throws an Exception (after the rollback) if fails or returns $result, which is the result of your callback
There is a short version if you want to use the default transaction method that ships with Laravel without handling it manually.
$result = DB::transaction(function () {
// logic here
return $somethingYouWantToCheckLater;
});
You can also use the following
DB::rollback();

How can I run the same code for a custom exception and a standard exception?

I have a custom exception class that extends Exception and adds on the ability to pass back more data on what threw the exception. The problem now is if I want to catch my custom exception and a standard exception, but handle them with the same block of code, I don't know how to do that other than create a new function (which I don't want to do for every place I'd like to use this).
try {
} catch(QM\DebugInfoException $e) {
// I don't want to duplicate the Exception handling code up here
}catch(Exception $e){
$db->rollBack();
$return['error'] = 1;
$return['errInfo'] = array(
'code' => $e->getCode(),
'message' => $e->getMessage(),
'trace' => $e->getTraceAsString()
);
// I'd rather handle both here, and just add data on to $return['errInfo']
switch ($ExceptionType) {
case 'QM\DebugInfoException':
$return['errInfo']['extraInfo'] = $e->getExtraInfo();
break;
}
}
Does anyone have any good ideas on this?
You could do a get_class($e) and that will return the string representing the class name of the exception object then use that to compare in your switch.
Another option is to put a function that encapsulates the common functionality, and call it from each of the exception blocks. That way new, unexpected exceptions not in your switch can still percolate up. I'm a big fan of explicitly catching specific exceptions.

Categories