Browse Source

Merge pull request #12774 from CakeDC/feature/integration-unit-tests-verbose-asserts

Verbose assertion messages in integration tests when exceptions are detected
Mark Story 7 years ago
parent
commit
bb177a0107

+ 99 - 36
src/TestSuite/IntegrationTestTrait.php

@@ -610,7 +610,7 @@ trait IntegrationTestTrait
         ];
         $session = Session::create($sessionConfig);
         $session->write($this->_session);
-        list ($url, $query) = $this->_url($url);
+        list($url, $query) = $this->_url($url);
         $tokenUrl = $url;
 
         if ($query) {
@@ -777,7 +777,8 @@ trait IntegrationTestTrait
      */
     public function assertResponseOk($message = null)
     {
-        $this->assertThat(null, new StatusOk($this->_response), $message);
+        $verboseMessage = $this->extractVerboseMessage($message);
+        $this->assertThat(null, new StatusOk($this->_response), $verboseMessage);
     }
 
     /**
@@ -788,7 +789,8 @@ trait IntegrationTestTrait
      */
     public function assertResponseSuccess($message = null)
     {
-        $this->assertThat(null, new StatusSuccess($this->_response), $message);
+        $verboseMessage = $this->extractVerboseMessage($message);
+        $this->assertThat(null, new StatusSuccess($this->_response), $verboseMessage);
     }
 
     /**
@@ -836,10 +838,11 @@ trait IntegrationTestTrait
      */
     public function assertRedirect($url = null, $message = '')
     {
-        $this->assertThat(null, new HeaderSet($this->_response, 'Location'), $message);
+        $verboseMessage = $this->extractVerboseMessage($message);
+        $this->assertThat(null, new HeaderSet($this->_response, 'Location'), $verboseMessage);
 
         if ($url) {
-            $this->assertThat(Router::url($url, ['_full' => true]), new HeaderEquals($this->_response, 'Location'), $message);
+            $this->assertThat(Router::url($url, ['_full' => true]), new HeaderEquals($this->_response, 'Location'), $verboseMessage);
         }
     }
 
@@ -852,8 +855,9 @@ trait IntegrationTestTrait
      */
     public function assertRedirectContains($url, $message = '')
     {
-        $this->assertThat(null, new HeaderSet($this->_response, 'Location'), $message);
-        $this->assertThat($url, new HeaderContains($this->_response, 'Location'), $message);
+        $verboseMessage = $this->extractVerboseMessage($message);
+        $this->assertThat(null, new HeaderSet($this->_response, 'Location'), $verboseMessage);
+        $this->assertThat($url, new HeaderContains($this->_response, 'Location'), $verboseMessage);
     }
 
     /**
@@ -865,8 +869,9 @@ trait IntegrationTestTrait
      */
     public function assertRedirectNotContains($url, $message = '')
     {
-        $this->assertThat(null, new HeaderSet($this->_response, 'Location'), $message);
-        $this->assertThat($url, new HeaderNotContains($this->_response, 'Location'), $message);
+        $verboseMessage = $this->extractVerboseMessage($message);
+        $this->assertThat(null, new HeaderSet($this->_response, 'Location'), $verboseMessage);
+        $this->assertThat($url, new HeaderNotContains($this->_response, 'Location'), $verboseMessage);
     }
 
     /**
@@ -877,7 +882,8 @@ trait IntegrationTestTrait
      */
     public function assertNoRedirect($message = '')
     {
-        $this->assertThat(null, new HeaderNotSet($this->_response, 'Location'), $message);
+        $verboseMessage = $this->extractVerboseMessage($message);
+        $this->assertThat(null, new HeaderNotSet($this->_response, 'Location'), $verboseMessage);
     }
 
     /**
@@ -890,8 +896,9 @@ trait IntegrationTestTrait
      */
     public function assertHeader($header, $content, $message = '')
     {
-        $this->assertThat(null, new HeaderSet($this->_response, $header), $message);
-        $this->assertThat($content, new HeaderEquals($this->_response, $header), $message);
+        $verboseMessage = $this->extractVerboseMessage($message);
+        $this->assertThat(null, new HeaderSet($this->_response, $header), $verboseMessage);
+        $this->assertThat($content, new HeaderEquals($this->_response, $header), $verboseMessage);
     }
 
     /**
@@ -904,8 +911,9 @@ trait IntegrationTestTrait
      */
     public function assertHeaderContains($header, $content, $message = '')
     {
-        $this->assertThat(null, new HeaderSet($this->_response, $header), $message);
-        $this->assertThat($content, new HeaderContains($this->_response, $header), $message);
+        $verboseMessage = $this->extractVerboseMessage($message);
+        $this->assertThat(null, new HeaderSet($this->_response, $header), $verboseMessage);
+        $this->assertThat($content, new HeaderContains($this->_response, $header), $verboseMessage);
     }
 
     /**
@@ -918,8 +926,9 @@ trait IntegrationTestTrait
      */
     public function assertHeaderNotContains($header, $content, $message = '')
     {
-        $this->assertThat(null, new HeaderSet($this->_response, $header), $message);
-        $this->assertThat($content, new HeaderNotContains($this->_response, $header), $message);
+        $verboseMessage = $this->extractVerboseMessage($message);
+        $this->assertThat(null, new HeaderSet($this->_response, $header), $verboseMessage);
+        $this->assertThat($content, new HeaderNotContains($this->_response, $header), $verboseMessage);
     }
 
     /**
@@ -931,7 +940,8 @@ trait IntegrationTestTrait
      */
     public function assertContentType($type, $message = '')
     {
-        $this->assertThat($type, new ContentType($this->_response), $message);
+        $verboseMessage = $this->extractVerboseMessage($message);
+        $this->assertThat($type, new ContentType($this->_response), $verboseMessage);
     }
 
     /**
@@ -943,7 +953,8 @@ trait IntegrationTestTrait
      */
     public function assertResponseEquals($content, $message = '')
     {
-        $this->assertThat($content, new BodyEquals($this->_response), $message);
+        $verboseMessage = $this->extractVerboseMessage($message);
+        $this->assertThat($content, new BodyEquals($this->_response), $verboseMessage);
     }
 
     /**
@@ -955,7 +966,8 @@ trait IntegrationTestTrait
      */
     public function assertResponseNotEquals($content, $message = '')
     {
-        $this->assertThat($content, new BodyNotEquals($this->_response), $message);
+        $verboseMessage = $this->extractVerboseMessage($message);
+        $this->assertThat($content, new BodyNotEquals($this->_response), $verboseMessage);
     }
 
     /**
@@ -968,7 +980,8 @@ trait IntegrationTestTrait
      */
     public function assertResponseContains($content, $message = '', $ignoreCase = false)
     {
-        $this->assertThat($content, new BodyContains($this->_response, $ignoreCase), $message);
+        $verboseMessage = $this->extractVerboseMessage($message);
+        $this->assertThat($content, new BodyContains($this->_response, $ignoreCase), $verboseMessage);
     }
 
     /**
@@ -981,7 +994,8 @@ trait IntegrationTestTrait
      */
     public function assertResponseNotContains($content, $message = '', $ignoreCase = false)
     {
-        $this->assertThat($content, new BodyNotContains($this->_response, $ignoreCase), $message);
+        $verboseMessage = $this->extractVerboseMessage($message);
+        $this->assertThat($content, new BodyNotContains($this->_response, $ignoreCase), $verboseMessage);
     }
 
     /**
@@ -993,7 +1007,8 @@ trait IntegrationTestTrait
      */
     public function assertResponseRegExp($pattern, $message = '')
     {
-        $this->assertThat($pattern, new BodyRegExp($this->_response), $message);
+        $verboseMessage = $this->extractVerboseMessage($message);
+        $this->assertThat($pattern, new BodyRegExp($this->_response), $verboseMessage);
     }
 
     /**
@@ -1005,7 +1020,8 @@ trait IntegrationTestTrait
      */
     public function assertResponseNotRegExp($pattern, $message = '')
     {
-        $this->assertThat($pattern, new BodyNotRegExp($this->_response), $message);
+        $verboseMessage = $this->extractVerboseMessage($message);
+        $this->assertThat($pattern, new BodyNotRegExp($this->_response), $verboseMessage);
     }
 
     /**
@@ -1039,7 +1055,8 @@ trait IntegrationTestTrait
      */
     public function assertTemplate($content, $message = '')
     {
-        $this->assertThat($content, new TemplateFileEquals($this->_viewName), $message);
+        $verboseMessage = $this->extractVerboseMessage($message);
+        $this->assertThat($content, new TemplateFileEquals($this->_viewName), $verboseMessage);
     }
 
     /**
@@ -1051,7 +1068,8 @@ trait IntegrationTestTrait
      */
     public function assertLayout($content, $message = '')
     {
-        $this->assertThat($content, new LayoutFileEquals($this->_layoutName), $message);
+        $verboseMessage = $this->extractVerboseMessage($message);
+        $this->assertThat($content, new LayoutFileEquals($this->_layoutName), $verboseMessage);
     }
 
     /**
@@ -1064,7 +1082,8 @@ trait IntegrationTestTrait
      */
     public function assertSession($expected, $path, $message = '')
     {
-        $this->assertThat($expected, new SessionEquals($this->_requestSession, $path), $message);
+        $verboseMessage = $this->extractVerboseMessage($message);
+        $this->assertThat($expected, new SessionEquals($this->_requestSession, $path), $verboseMessage);
     }
 
     /**
@@ -1077,7 +1096,8 @@ trait IntegrationTestTrait
      */
     public function assertFlashMessage($expected, $key = 'flash', $message = '')
     {
-        $this->assertThat($expected, new FlashParamEquals($this->_requestSession, $key, 'message'), $message);
+        $verboseMessage = $this->extractVerboseMessage($message);
+        $this->assertThat($expected, new FlashParamEquals($this->_requestSession, $key, 'message'), $verboseMessage);
     }
 
     /**
@@ -1091,7 +1111,8 @@ trait IntegrationTestTrait
      */
     public function assertFlashMessageAt($at, $expected, $key = 'flash', $message = '')
     {
-        $this->assertThat($expected, new FlashParamEquals($this->_requestSession, $key, 'message', $at), $message);
+        $verboseMessage = $this->extractVerboseMessage($message);
+        $this->assertThat($expected, new FlashParamEquals($this->_requestSession, $key, 'message', $at), $verboseMessage);
     }
 
     /**
@@ -1104,7 +1125,8 @@ trait IntegrationTestTrait
      */
     public function assertFlashElement($expected, $key = 'flash', $message = '')
     {
-        $this->assertThat($expected, new FlashParamEquals($this->_requestSession, $key, 'element'), $message);
+        $verboseMessage = $this->extractVerboseMessage($message);
+        $this->assertThat($expected, new FlashParamEquals($this->_requestSession, $key, 'element'), $verboseMessage);
     }
 
     /**
@@ -1118,7 +1140,8 @@ trait IntegrationTestTrait
      */
     public function assertFlashElementAt($at, $expected, $key = 'flash', $message = '')
     {
-        $this->assertThat($expected, new FlashParamEquals($this->_requestSession, $key, 'element', $at), $message);
+        $verboseMessage = $this->extractVerboseMessage($message);
+        $this->assertThat($expected, new FlashParamEquals($this->_requestSession, $key, 'element', $at), $verboseMessage);
     }
 
     /**
@@ -1131,8 +1154,9 @@ trait IntegrationTestTrait
      */
     public function assertCookie($expected, $name, $message = '')
     {
-        $this->assertThat($name, new CookieSet($this->_response), $message);
-        $this->assertThat($expected, new CookieEquals($this->_response, $name), $message);
+        $verboseMessage = $this->extractVerboseMessage($message);
+        $this->assertThat($name, new CookieSet($this->_response), $verboseMessage);
+        $this->assertThat($expected, new CookieEquals($this->_response, $name), $verboseMessage);
     }
 
     /**
@@ -1144,7 +1168,8 @@ trait IntegrationTestTrait
      */
     public function assertCookieNotSet($cookie, $message = '')
     {
-        $this->assertThat($cookie, new CookieNotSet($this->_response), $message);
+        $verboseMessage = $this->extractVerboseMessage($message);
+        $this->assertThat($cookie, new CookieNotSet($this->_response), $verboseMessage);
     }
 
     /**
@@ -1179,7 +1204,8 @@ trait IntegrationTestTrait
      */
     public function assertCookieEncrypted($expected, $name, $encrypt = 'aes', $key = null, $message = '')
     {
-        $this->assertThat($name, new CookieSet($this->_response), $message);
+        $verboseMessage = $this->extractVerboseMessage($message);
+        $this->assertThat($name, new CookieSet($this->_response), $verboseMessage);
 
         $this->_cookieEncryptionKey = $key;
         $this->assertThat($expected, new CookieEncryptedEquals($this->_response, $name, $encrypt, $this->_getCookieEncryptionKey()));
@@ -1194,7 +1220,44 @@ trait IntegrationTestTrait
      */
     public function assertFileResponse($expected, $message = '')
     {
-        $this->assertThat(null, new FileSent($this->_response), $message);
-        $this->assertThat($expected, new FileSentAs($this->_response), $message);
+        $verboseMessage = $this->extractVerboseMessage($message);
+        $this->assertThat(null, new FileSent($this->_response), $verboseMessage);
+        $this->assertThat($expected, new FileSentAs($this->_response), $verboseMessage);
+    }
+
+    /**
+     * Inspect controller to extract possible causes of the failed assertion
+     *
+     * @param string $message Original message to use as a base
+     * @return null|string
+     */
+    protected function extractVerboseMessage($message = null)
+    {
+        if ($this->_exception instanceof \Exception) {
+            $message .= $this->extractExceptionMessage($this->_exception);
+        }
+        if ($this->_controller === null) {
+            return $message;
+        }
+        $error = Hash::get($this->_controller->viewVars, 'error');
+        if ($error instanceof \Exception) {
+            $message .= $this->extractExceptionMessage($this->viewVariable('error'));
+        }
+
+        return $message;
+    }
+
+    /**
+     * Extract verbose message for existing exception
+     *
+     * @param \Exception $exception Exception to extract
+     * @return string
+     */
+    protected function extractExceptionMessage(\Exception $exception)
+    {
+        return PHP_EOL .
+            sprintf('Possibly related to %s: "%s" ', get_class($exception), $exception->getMessage()) .
+            PHP_EOL .
+            $exception->getTraceAsString();
     }
 }

+ 140 - 0
tests/TestCase/TestSuite/IntegrationTestTraitTest.php

@@ -14,10 +14,13 @@
  */
 namespace Cake\Test\TestCase\TestSuite;
 
+use Cake\Controller\Controller;
 use Cake\Core\Configure;
 use Cake\Core\Plugin;
 use Cake\Event\EventManager;
+use Cake\Http\Cookie\Cookie;
 use Cake\Http\Response;
+use Cake\Http\Session;
 use Cake\Routing\DispatcherFactory;
 use Cake\Routing\RouteBuilder;
 use Cake\Routing\Router;
@@ -1368,30 +1371,44 @@ class IntegrationTestTraitTest extends IntegrationTestCase
 
         return [
             'assertContentType' => ['assertContentType', 'Failed asserting that \'test\' was set as the Content-Type.', '/posts/index', 'test'],
+            'assertContentTypeVerbose' => ['assertContentType', 'Possibly related to Cake\Routing\Exception\MissingRouteException: "A route matching "/notfound" could not be found."', '/notfound', 'test'],
             'assertCookie' => ['assertCookie', 'Failed asserting that \'test\' was in cookie \'remember_me\'.', '/posts/index', 'test', 'remember_me'],
+            'assertCookieVerbose' => ['assertCookie', 'Possibly related to Cake\Routing\Exception\MissingRouteException: "A route matching "/notfound" could not be found."', '/notfound', 'test', 'remember_me'],
             'assertCookieEncrypted' => ['assertCookieEncrypted', 'Failed asserting that \'test\' was encrypted in cookie \'NameOfCookie\'.', '/cookie_component_test/set_cookie', 'test', 'NameOfCookie'],
+            'assertCookieEncryptedVerbose' => ['assertCookieEncrypted', 'Possibly related to Cake\Routing\Exception\MissingRouteException: "A route matching "/notfound" could not be found."', '/notfound', 'test', 'NameOfCookie'],
             'assertCookieNotSet' => ['assertCookieNotSet', 'Failed asserting that \'remember_me\' cookie was not set.', '/posts/index', 'remember_me'],
             'assertFileResponse' => ['assertFileResponse', 'Failed asserting that \'test\' file was sent.', '/posts/file', 'test'],
+            'assertFileResponseVerbose' => ['assertFileResponse', 'Possibly related to Cake\Routing\Exception\MissingRouteException: "A route matching "/notfound" could not be found."', '/notfound', 'test'],
             'assertHeader' => ['assertHeader', 'Failed asserting that \'test\' equals content in header \'X-Cake\'.', '/posts/header', 'X-Cake', 'test'],
             'assertHeaderContains' => ['assertHeaderContains', 'Failed asserting that \'test\' is in header \'X-Cake\'', '/posts/header', 'X-Cake', 'test'],
+            'assertHeaderContainsVerbose' => ['assertHeaderContains', 'Possibly related to Cake\Routing\Exception\MissingRouteException: "A route matching "/notfound" could not be found."', '/notfound', 'X-Cake', 'test'],
+            'assertHeaderNotContainsVerbose' => ['assertHeaderNotContains', 'Possibly related to Cake\Routing\Exception\MissingRouteException: "A route matching "/notfound" could not be found."', '/notfound', 'X-Cake', 'test'],
             'assertLayout' => ['assertLayout', 'Failed asserting that \'custom_layout\' equals layout file ' . $templateDir . 'Layout' . DS . 'default.ctp.', '/posts/index', 'custom_layout'],
+            'assertLayoutVerbose' => ['assertLayout', 'Possibly related to Cake\Routing\Exception\MissingRouteException: "A route matching "/notfound" could not be found."', '/notfound', 'custom_layout'],
             'assertRedirect' => ['assertRedirect', 'Failed asserting that \'http://localhost/\' equals content in header \'Location\'.', '/posts/flashNoRender', '/'],
+            'assertRedirectVerbose' => ['assertRedirect', 'Possibly related to Cake\Routing\Exception\MissingRouteException: "A route matching "/notfound" could not be found."', '/notfound', '/'],
             'assertRedirectContains' => ['assertRedirectContains', 'Failed asserting that \'/posts/somewhere-else\' is in header \'Location\'.', '/posts/flashNoRender', '/posts/somewhere-else'],
+            'assertRedirectContainsVerbose' => ['assertRedirectContains', 'Possibly related to Cake\Routing\Exception\MissingRouteException: "A route matching "/notfound" could not be found."', '/notfound', '/posts/somewhere-else'],
+            'assertRedirectNotContainsVerbose' => ['assertRedirectNotContains', 'Possibly related to Cake\Routing\Exception\MissingRouteException: "A route matching "/notfound" could not be found."', '/notfound', '/posts/somewhere-else'],
             'assertResponseCode' => ['assertResponseCode', 'Failed asserting that 302 matches response status code 200.', '/posts/index', 302],
             'assertResponseContains' => ['assertResponseContains', 'Failed asserting that \'test\' is in response body.', '/posts/index', 'test'],
             'assertResponseEmpty' => ['assertResponseEmpty', 'Failed asserting that response body is empty.', '/posts/index'],
             'assertResponseEquals' => ['assertResponseEquals', 'Failed asserting that \'test\' matches response body.', '/posts/index', 'test'],
+            'assertResponseEqualsVerbose' => ['assertResponseEquals', 'Possibly related to Cake\Routing\Exception\MissingRouteException: "A route matching "/notfound" could not be found."', '/notfound', 'test'],
             'assertResponseError' => ['assertResponseError', 'Failed asserting that 200 is between 400 and 429.', '/posts/index'],
             'assertResponseFailure' => ['assertResponseFailure', 'Failed asserting that 200 is between 500 and 505.', '/posts/index'],
             'assertResponseNotContains' => ['assertResponseNotContains', 'Failed asserting that \'index\' is not in response body.', '/posts/index', 'index'],
             'assertResponseNotEmpty' => ['assertResponseNotEmpty', 'Failed asserting that response body is not empty.', '/posts/empty_response'],
             'assertResponseNotEquals' => ['assertResponseNotEquals', 'Failed asserting that \'posts index\' does not match response body.', '/posts/index/error', 'posts index'],
             'assertResponseNotRegExp' => ['assertResponseNotRegExp', 'Failed asserting that /index/ PCRE pattern not found in response body.', '/posts/index/error', '/index/'],
+            'assertResponseNotRegExpVerbose' => ['assertResponseNotRegExp', 'Possibly related to Cake\Routing\Exception\MissingRouteException: "A route matching "/notfound" could not be found."', '/notfound', '/index/'],
             'assertResponseOk' => ['assertResponseOk', 'Failed asserting that 404 is between 200 and 204.', '/posts/missing', '/index/'],
             'assertResponseRegExp' => ['assertResponseRegExp', 'Failed asserting that /test/ PCRE pattern found in response body.', '/posts/index/error', '/test/'],
             'assertResponseSuccess' => ['assertResponseSuccess', 'Failed asserting that 404 is between 200 and 308.', '/posts/missing'],
+            'assertResponseSuccessVerbose' => ['assertResponseSuccess', 'Possibly related to Cake\Controller\Exception\MissingActionException: "Action PostsController::missing() could not be found, or is not accessible."', '/posts/missing'],
             'assertSession' => ['assertSession', 'Failed asserting that \'test\' is in session path \'Missing.path\'.', '/posts/index', 'test', 'Missing.path'],
             'assertTemplate' => ['assertTemplate', 'Failed asserting that \'custom_template\' equals template file ' . $templateDir . 'Posts' . DS . 'index.ctp.', '/posts/index', 'custom_template'],
+            'assertTemplateVerbose' => ['assertTemplate', 'Possibly related to Cake\Routing\Exception\MissingRouteException: "A route matching "/notfound" could not be found."', '/notfound', 'custom_template'],
             'assertFlashMessage' => ['assertFlashMessage', 'Failed asserting that \'missing\' was in \'flash\' message.', '/posts/index', 'missing'],
             'assertFlashMessageWithKey' => ['assertFlashMessage', 'Failed asserting that \'missing\' was in \'auth\' message.', '/posts/index', 'missing', 'auth'],
             'assertFlashMessageAt' => ['assertFlashMessageAt', 'Failed asserting that \'missing\' was in \'flash\' message #0.', '/posts/index', 0, 'missing'],
@@ -1418,4 +1435,127 @@ class IntegrationTestTraitTest extends IntegrationTestCase
             'DELETE' => ['delete'],
         ];
     }
+
+    /**
+     * Test assertCookieNotSet is creating a verbose message
+     *
+     * @return void
+     */
+    public function testAssertCookieNotSetVerbose()
+    {
+        $this->expectException(AssertionFailedError::class);
+        $this->expectExceptionMessage('Possibly related to Cake\Routing\Exception\MissingRouteException: "A route matching "/notfound" could not be found."');
+        $this->get('/notfound');
+        $this->_response = $this->_response->withCookie(new Cookie('cookie', 'value'));
+        $this->assertCookieNotSet('cookie');
+    }
+
+    /**
+     * Test assertNoRedirect is creating a verbose message
+     *
+     * @return void
+     */
+    public function testAssertNoRedirectVerbose()
+    {
+        $this->expectException(AssertionFailedError::class);
+        $this->expectExceptionMessage('Possibly related to Cake\Routing\Exception\MissingRouteException: "A route matching "/notfound" could not be found."');
+        $this->get('/notfound');
+        $this->_response = $this->_response->withHeader('Location', '/redirect');
+        $this->assertNoRedirect();
+    }
+
+    /**
+     * Test the header assertion generating a verbose message.
+     *
+     * @return void
+     */
+    public function testAssertHeaderVerbose()
+    {
+        $this->expectException(AssertionFailedError::class);
+        $this->expectExceptionMessage('Possibly related to Cake\Routing\Exception\MissingRouteException: "A route matching "/notfound" could not be found."');
+        $this->get('/notfound');
+        $this->assertHeader('Etag', 'abc123');
+    }
+
+    /**
+     * Test the assertResponseNotEquals generates a verbose message.
+     *
+     * @return void
+     */
+    public function testAssertResponseNotEqualsVerbose()
+    {
+        $this->expectException(AssertionFailedError::class);
+        $this->expectExceptionMessage('Possibly related to Cake\Routing\Exception\MissingRouteException: "A route matching "/notfound" could not be found."');
+        $this->get('/notfound');
+        $this->_response = $this->_response->withStringBody('body');
+        $this->assertResponseNotEquals('body');
+    }
+
+    /**
+     * Test the assertResponseRegExp generates a verbose message.
+     *
+     * @return void
+     */
+    public function testAssertResponseRegExpVerbose()
+    {
+        $this->expectException(AssertionFailedError::class);
+        $this->expectExceptionMessage('Possibly related to Cake\Routing\Exception\MissingRouteException: "A route matching "/notfound" could not be found."');
+        $this->get('/notfound');
+        $this->_response = $this->_response->withStringBody('body');
+        $this->assertResponseRegExp('/patternNotFound/');
+    }
+
+    /**
+     * Test the assertion generates a verbose message for session related checks.
+     *
+     * @dataProvider assertionFailureSessionVerboseProvider
+     * @return void
+     */
+    public function testAssertSessionRelatedVerboseMessages($assertMethod, ...$rest)
+    {
+        $this->expectException(AssertionFailedError::class);
+        $this->expectExceptionMessage('Possibly related to OutOfBoundsException: "oh no!"');
+        $this->get('/posts/throw_exception');
+        $this->_requestSession = new Session();
+        call_user_func_array([$this, $assertMethod], $rest);
+    }
+
+    /**
+     * data provider for assertion verbose session related tests
+     *
+     * @return array
+     */
+    public function assertionFailureSessionVerboseProvider()
+    {
+        return [
+            'assertFlashMessageVerbose' => ['assertFlashMessage', 'notfound'],
+            'assertFlashMessageAtVerbose' => ['assertFlashMessageAt', 2, 'notfound'],
+            'assertFlashElementVerbose' => ['assertFlashElement', 'notfound'],
+            'assertSessionVerbose' => ['assertSession', 'notfound', 'notfound'],
+        ];
+    }
+
+    /**
+     * Test fail case for viewVariable
+     *
+     * @return void
+     */
+    public function testViewVariableShouldFailIfNoViewVars()
+    {
+        $this->expectException(AssertionFailedError::class);
+        $this->expectExceptionMessage('There are no view variables, perhaps you need to run a request?');
+        $this->viewVariable('shouldFail');
+    }
+
+    /**
+     * Test viewVariable not found
+     *
+     * @return void
+     */
+    public function testViewVariableNotFoundShouldReturnNull()
+    {
+        $this->_controller = new Controller();
+        $this->_controller->viewVars = ['key' => 'value'];
+        $this->assertNull($this->viewVariable('notFound'));
+    }
 }

+ 6 - 0
tests/test_app/TestApp/Controller/PostsController.php

@@ -130,4 +130,10 @@ class PostsController extends AppController
 
         return $this->getResponse()->withStringBody('');
     }
+
+    public function throw_exception()
+    {
+        $this->Flash->error('Error 1');
+        throw new \OutOfBoundsException('oh no!');
+    }
 }