Home » Php » Set Expected Exception in Codeception Functional Cept

Set Expected Exception in Codeception Functional Cept

Posted by: admin November 29, 2017 Leave a comment

Questions:

I want to do something like:

$I->setExpectedException('Laracasts\Validation\FormValidationException');

In a functional cept. Any chance to do so?

\PHPUnit_Framework_TestCase::setExpectedException('Laracasts\Validation\FormValidationException');

Above code will work in isolation but if I run codecept run, the tests get stuck once the test with the expected exception is complete.

Here’s my setup:

YML:

class_name: FunctionalTester
modules:
    enabled: [Filesystem, Db, FunctionalHelper, Laravel4, Asserts]
Answers:

I think this is a known problem with the Laravel 4 module for codeception, not sure if it is going to be fixed soon, but in the meantime I created a helper function to test Exceptions:

In the file tests/_support/FunctionalHelper.php add the following method:

public function seeExceptionThrown($exception, $function)
{
    try
    {
        $function();
        return false;
    } catch (Exception $e) {
        if( get_class($e) == $exception ){
            return true;
        }
        return false;
    }
}

You use it in your Cepts like this:

$I = new FunctionalTester($scenario);
$I->assertTrue(
    $I->seeExceptionThrown('Laracasts\Validation\FormValidationException', function() use ($I){
    //All actions that you expect to generate the Exception
    $I->amOnPage('/users/edit/1');
    $I->fillField('name', '');
    $I->click('Update');
}));

Questions:
Answers:

I extended Minds solution a bit:

public function seeException($callback, $exception = 'Exception', $message = '')
  {
    $function = function () use ($callback, $exception) {
      try {
        $callback();
        return false;
      }
      catch (Exception $e) {
        if (get_class($e) == $exception or get_parent_class($e) == $exception) {
          return true;
        }
        return false;
      }
    };
    $this->assertTrue($function(), $message);
  }

In your cepts you can

$I->seeException('Laracasts\Validation\FormValidationException', function() use ($I){
    //All actions that you expect to generate the Exception
    $I->amOnPage('/users/edit/1');
    $I->fillField('name', '');
    $I->click('Update');
})

This way there is no need for assertTrue outside of the exception test method. You can also leave out the second argument if it is fine for you too know that an exception is thrown. Last you can put an message string as third argument. That way you can make the error more expressive if it fails.

EDIT
Pardon, I missed one thing. In FunctionalHelper.php you have to extends the parents _beforeSuite method:

public function _beforeSuite()
  {
    parent::_beforeSuite();
    $this->assert = $this->getModule('Asserts');

  }

EDIT2
Just found out that the test method doesn’t work if you implicitly assert that an exception is thrown and php’s native exception isn’t the appearing exceptions parent because the method only checks first and second exception level. To make the method inspect the whole expection stack you have to use this method

public function assertThrows($callback, $exception = 'Exception', $message = '')
  {
    $function = function () use ($callback, $exception) {
      $getAncestors = function($e) {

        for ($classes[] = $e; $e = get_parent_class ($e); $classes[] = $e);
        return $classes;

      }

      try {
        $callback();
        return false;
      }
      catch (Exception $e) {
        if (get_class($e) == $exception or in_array($e, $getAncestors($e))) {
          return true;
        }
        return false;
      }
    };
    $this->assertTrue($function(), $message);
  }