diff --git a/core/lib/Drupal/Core/Form/FormBuilder.php b/core/lib/Drupal/Core/Form/FormBuilder.php
index d204050..d8cdb7a 100644
--- a/core/lib/Drupal/Core/Form/FormBuilder.php
+++ b/core/lib/Drupal/Core/Form/FormBuilder.php
@@ -405,16 +405,8 @@ public function retrieveForm($form_id, FormStateInterface &$form_state) {
     $args = array_merge(array($form, &$form_state), $args);
 
     $form = call_user_func_array($callback, $args);
-    // If the form returns a response, skip subsequent page construction by
-    // throwing an exception.
-    // @see Drupal\Core\EventSubscriber\EnforcedFormResponseSubscriber
-    //
-    // @todo Exceptions should not be used for code flow control. However, the
-    //   Form API currently allows any form builder functions to return a
-    //   response.
-    //   @see https://www.drupal.org/node/2363189
-    if ($form instanceof Response) {
-      throw new EnforcedResponseException($form);
+    if (!is_array($form)) {
+      throw new \UnexpectedValueException('Form builder functions must return arrays');
     }
     $form['#form_id'] = $form_id;
 
diff --git a/core/modules/language/src/Form/LanguageDeleteForm.php b/core/modules/language/src/Form/LanguageDeleteForm.php
index 32fb889..703f980 100644
--- a/core/modules/language/src/Form/LanguageDeleteForm.php
+++ b/core/modules/language/src/Form/LanguageDeleteForm.php
@@ -88,13 +88,6 @@ public function getFormId() {
   public function buildForm(array $form, FormStateInterface $form_state) {
     $langcode = $this->entity->id();
 
-    // Warn and redirect user when attempting to delete the default language.
-    if (language_default()->getId() == $langcode) {
-      drupal_set_message($this->t('The default language cannot be deleted.'));
-      $url = $this->urlGenerator->generateFromPath('admin/config/regional/language', array('absolute' => TRUE));
-      return new RedirectResponse($url);
-    }
-
     // Throw a 404 when attempting to delete a non-existing language.
     $languages = language_list();
     if (!isset($languages[$langcode])) {
diff --git a/core/modules/language/src/LanguageAccessControlHandler.php b/core/modules/language/src/LanguageAccessControlHandler.php
index 0490e91..db388f8 100644
--- a/core/modules/language/src/LanguageAccessControlHandler.php
+++ b/core/modules/language/src/LanguageAccessControlHandler.php
@@ -15,7 +15,7 @@
 /**
  * Defines the access control handler for the language entity type.
  *
- * @see \Drupal\language\Entity\Language
+ * @see \Drupal\language\Entity\ConfigurableLanguage
  */
 class LanguageAccessControlHandler extends EntityAccessControlHandler {
 
@@ -23,17 +23,24 @@ class LanguageAccessControlHandler extends EntityAccessControlHandler {
    * {@inheritdoc}
    */
   public function checkAccess(EntityInterface $entity, $operation, $langcode, AccountInterface $account) {
-    switch ($operation) {
-      case 'update':
-      case 'delete':
-        /* @var \Drupal\Core\Language\LanguageInterface $entity */
-        return AccessResult::allowedIf(!$entity->isLocked())->cacheUntilEntityChanges($entity)
-          ->andIf(parent::checkAccess($entity, $operation, $langcode, $account));
-
-      default:
-        // No opinion.
-        return AccessResult::neutral();
+    /* @var \Drupal\language\ConfigurableLanguageInterface $entity */
+    // Locked languages may not be updated.
+    if ($operation == 'update') {
+      $access_condition = !$entity->isLocked();
+    }
+    // The default language or locked languages may not be deleted.
+    elseif ($operation == 'delete') {
+      $access_condition = !$entity->isLocked() && !$entity->isDefault();
     }
+    // Otherwise no opinion.
+    else {
+      return AccessResult::neutral();
+    }
+
+    // Return the proper access result for the determined access condition.
+    return AccessResult::allowedIf($access_condition)
+      ->cacheUntilEntityChanges($entity)
+      ->andIf(parent::checkAccess($entity, $operation, $langcode, $account));
   }
 
 }
diff --git a/core/modules/language/src/Tests/LanguageListTest.php b/core/modules/language/src/Tests/LanguageListTest.php
index c27f981..64ad74f 100644
--- a/core/modules/language/src/Tests/LanguageListTest.php
+++ b/core/modules/language/src/Tests/LanguageListTest.php
@@ -75,10 +75,10 @@ function testLanguageList() {
 
     // Ensure we can't delete the default language.
     $this->drupalGet('admin/config/regional/language/delete/' . $langcode);
-    $this->assertUrl(\Drupal::url('language.admin_overview', [], ['absolute' => TRUE, 'language' => $language]));
-    $this->assertText(t('The default language cannot be deleted.'), 'Failed to delete the default language.');
+    $this->assertResponse(403, 'Failed to delete the default language.');
 
     // Ensure 'Edit' link works.
+    $this->drupalGet('admin/config/regional/language');
     $this->clickLink(t('Edit'));
     $this->assertTitle(t('Edit language | Drupal'), 'Page title is "Edit language".');
     // Edit a language.
diff --git a/core/modules/node/src/Form/DeleteMultiple.php b/core/modules/node/src/Form/DeleteMultiple.php
index 7b2aced..68923ac 100644
--- a/core/modules/node/src/Form/DeleteMultiple.php
+++ b/core/modules/node/src/Form/DeleteMultiple.php
@@ -13,8 +13,8 @@
 use Drupal\Component\Utility\String;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\user\TempStoreFactory;
-use Symfony\Component\HttpFoundation\RedirectResponse;
 use Symfony\Component\DependencyInjection\ContainerInterface;
+use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
 
 /**
  * Provides a node deletion confirmation form.
@@ -97,9 +97,12 @@ public function getConfirmText() {
    * {@inheritdoc}
    */
   public function buildForm(array $form, FormStateInterface $form_state) {
-    $this->nodes = $this->tempStoreFactory->get('node_multiple_delete_confirm')->get(\Drupal::currentUser()->id());
+    $current_user_id = $this->currentUser()->id();
+    $node_temp_store = $this->tempStoreFactory->get('node_multiple_delete_confirm');
+    $this->nodes = $node_temp_store->get($current_user_id);
     if (empty($this->nodes)) {
-      return new RedirectResponse($this->getCancelUrl()->setAbsolute()->toString());
+      $node_temp_store->delete($current_user_id);
+      throw new AccessDeniedHttpException();
     }
 
     $form['nodes'] = array(
diff --git a/core/modules/node/src/Tests/NodeAdminTest.php b/core/modules/node/src/Tests/NodeAdminTest.php
index d88d7db..37ce473 100644
--- a/core/modules/node/src/Tests/NodeAdminTest.php
+++ b/core/modules/node/src/Tests/NodeAdminTest.php
@@ -164,5 +164,11 @@ function testContentAdminPages() {
       $this->assertLinkByHref('node/' . $node->id() . '/edit');
       $this->assertLinkByHref('node/' . $node->id() . '/delete');
     }
+
+    $this->drupalPostForm(NULL, [], 'Apply');
+    $this->assertRaw('No content selected');
+    $this->drupalGet('admin/content/node/delete');
+    $this->assertResponse(403);
   }
+
 }
diff --git a/core/modules/simpletest/simpletest.routing.yml b/core/modules/simpletest/simpletest.routing.yml
index ae67d46..9872850 100644
--- a/core/modules/simpletest/simpletest.routing.yml
+++ b/core/modules/simpletest/simpletest.routing.yml
@@ -17,7 +17,7 @@ simpletest.test_form:
 simpletest.result_form:
   path: '/admin/config/development/testing/results/{test_id}'
   defaults:
-    _form: 'Drupal\simpletest\Form\SimpletestResultsForm'
+    _content: 'Drupal\simpletest\Form\SimpletestResultsForm::getForm'
     _title: 'Test result'
   requirements:
     _permission: 'administer unit tests'
diff --git a/core/modules/simpletest/src/Form/SimpletestResultsForm.php b/core/modules/simpletest/src/Form/SimpletestResultsForm.php
index a127683..77642a8 100644
--- a/core/modules/simpletest/src/Form/SimpletestResultsForm.php
+++ b/core/modules/simpletest/src/Form/SimpletestResultsForm.php
@@ -8,19 +8,19 @@
 namespace Drupal\simpletest\Form;
 
 use Drupal\Component\Utility\SafeMarkup;
+use Drupal\Core\Controller\ControllerBase;
 use Drupal\Core\Database\Connection;
-use Drupal\Core\Form\FormBase;
+use Drupal\Core\Form\FormInterface;
 use Drupal\Core\Form\FormState;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Url;
 use Drupal\simpletest\TestDiscovery;
 use Symfony\Component\DependencyInjection\ContainerInterface;
-use Symfony\Component\HttpFoundation\RedirectResponse;
 
 /**
  * Test results form for $test_id.
  */
-class SimpletestResultsForm extends FormBase {
+class SimpletestResultsForm extends ControllerBase implements FormInterface {
 
   /**
    * Associative array of themed result images keyed by status.
@@ -106,16 +106,22 @@ public function getFormId() {
   /**
    * {@inheritdoc}
    */
+  public function getForm($test_id = NULL) {
+    if (is_numeric($test_id) && !$results = $this->getResults($test_id)) {
+      drupal_set_message($this->t('No test results to display.'), 'error');
+      return $this->redirect('simpletest.test_form');
+    }
+    return $this->formBuilder()->getForm($this, $test_id);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
   public function buildForm(array $form, FormStateInterface $form_state, $test_id = NULL) {
     $this->buildStatusImageMap();
     // Make sure there are test results to display and a re-run is not being
     // performed.
-    $results = array();
-
-    if (is_numeric($test_id) && !$results = $this->getResults($test_id)) {
-      drupal_set_message($this->t('No test results to display.'), 'error');
-      return new RedirectResponse($this->url('simpletest.test_form', array(), array('absolute' => TRUE)));
-    }
+    $results = is_numeric($test_id) ? $this->getResults($test_id) : [];
 
     // Load all classes and include CSS.
     $form['#attached']['library'][] = 'simpletest/drupal.simpletest';
@@ -252,6 +258,12 @@ public function buildForm(array $form, FormStateInterface $form_state, $test_id
   /**
    * {@inheritdoc}
    */
+  public function validateForm(array &$form, FormStateInterface $form_state) {
+  }
+
+  /**
+   * {@inheritdoc}
+   */
   public function submitForm(array &$form, FormStateInterface $form_state) {
     $pass = $form_state->getValue('filter_pass') ? explode(',', $form_state->getValue('filter_pass')) : array();
     $fail = $form_state->getValue('filter_fail') ? explode(',', $form_state->getValue('filter_fail')) : array();
diff --git a/core/modules/simpletest/src/Tests/SimpleTestTest.php b/core/modules/simpletest/src/Tests/SimpleTestTest.php
index d2949a6..278e2f1 100644
--- a/core/modules/simpletest/src/Tests/SimpleTestTest.php
+++ b/core/modules/simpletest/src/Tests/SimpleTestTest.php
@@ -129,6 +129,10 @@ function testWebTestRunner() {
       // Regression test for #290316.
       // Check that test_id is incrementing.
       $this->assertTrue($this->test_ids[0] != $this->test_ids[1], 'Test ID is incrementing.');
+
+      // Attempt to retrieve results for a non-existent test run ID.
+      $this->drupalGet('admin/config/development/testing/results/' . ($this->test_ids[1] + 1));
+      $this->assertRaw('No test results to display');
     }
   }
 
diff --git a/core/modules/system/src/Form/CronForm.php b/core/modules/system/src/Form/CronForm.php
index cfb82fd..5956c34 100644
--- a/core/modules/system/src/Form/CronForm.php
+++ b/core/modules/system/src/Form/CronForm.php
@@ -14,7 +14,6 @@
 use Drupal\Core\State\StateInterface;
 use Drupal\Core\Form\ConfigFormBase;
 use Symfony\Component\DependencyInjection\ContainerInterface;
-use Symfony\Component\HttpFoundation\RedirectResponse;
 
 /**
  * Configure cron settings for this site.
@@ -144,7 +143,7 @@ public function submitCron(array &$form, FormStateInterface $form_state) {
       drupal_set_message(t('Cron run failed.'), 'error');
     }
 
-    return new RedirectResponse($this->url('system.cron_settings', array(), array('absolute' => TRUE)));
+    return $this->redirect('system.cron_settings');
   }
 
 }
diff --git a/core/modules/system/src/Form/ModulesListConfirmForm.php b/core/modules/system/src/Form/ModulesListConfirmForm.php
index 2a31dbe..dfa5bbb 100644
--- a/core/modules/system/src/Form/ModulesListConfirmForm.php
+++ b/core/modules/system/src/Form/ModulesListConfirmForm.php
@@ -14,6 +14,7 @@
 use Drupal\Core\KeyValueStore\KeyValueStoreExpirableInterface;
 use Drupal\Core\Url;
 use Symfony\Component\DependencyInjection\ContainerInterface;
+use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
 
 /**
  * Builds a confirmation form for enabling modules with dependencies.
@@ -119,7 +120,7 @@ public function buildForm(array $form, FormStateInterface $form_state) {
 
     // Redirect to the modules list page if the key value store is empty.
     if (!$this->modules) {
-      return $this->redirect('system.modules_list');
+      throw new AccessDeniedHttpException();
     }
 
     $items = array();
diff --git a/core/modules/system/src/Form/ModulesUninstallConfirmForm.php b/core/modules/system/src/Form/ModulesUninstallConfirmForm.php
index d2974b3..1c16116 100644
--- a/core/modules/system/src/Form/ModulesUninstallConfirmForm.php
+++ b/core/modules/system/src/Form/ModulesUninstallConfirmForm.php
@@ -13,9 +13,9 @@
 use Drupal\Core\Form\ConfirmFormBase;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Url;
-use Symfony\Component\HttpFoundation\RedirectResponse;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 use Drupal\Core\KeyValueStore\KeyValueStoreExpirableInterface;
+use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
 
 /**
  * Builds a confirmation form to uninstall selected modules.
@@ -133,7 +133,7 @@ public function buildForm(array $form, FormStateInterface $form_state) {
 
     // Prevent this page from showing when the module list is empty.
     if (empty($this->modules)) {
-      return new RedirectResponse('/admin/modules/uninstall');
+      throw new AccessDeniedHttpException();
     }
 
     $data = system_rebuild_module_data();
diff --git a/core/modules/system/src/Form/ModulesUninstallForm.php b/core/modules/system/src/Form/ModulesUninstallForm.php
index 4285956..208b352 100644
--- a/core/modules/system/src/Form/ModulesUninstallForm.php
+++ b/core/modules/system/src/Form/ModulesUninstallForm.php
@@ -152,8 +152,7 @@ public function buildForm(array $form, FormStateInterface $form_state) {
   public function validateForm(array &$form, FormStateInterface $form_state) {
     // Form submitted, but no modules selected.
     if (!array_filter($form_state->getValue('uninstall'))) {
-      drupal_set_message($this->t('No modules selected.'), 'error');
-      $form_state->setRedirect('system.modules_uninstall');
+      $form_state->setErrorByName('', $this->t('No modules selected.'));
     }
   }
 
diff --git a/core/modules/system/src/Tests/Module/UninstallTest.php b/core/modules/system/src/Tests/Module/UninstallTest.php
index 1b1cd70..880fc6a 100644
--- a/core/modules/system/src/Tests/Module/UninstallTest.php
+++ b/core/modules/system/src/Tests/Module/UninstallTest.php
@@ -45,6 +45,12 @@ function testUninstallPage() {
     $this->drupalGet('admin/modules/uninstall');
     $this->assertTitle(t('Uninstall') . ' | Drupal');
 
+    // Attempt to uninstall no modules.
+    $this->drupalPostForm(NULL, [], 'Uninstall');
+    $this->assertResponse(200);
+    $this->drupalGet('admin/modules/uninstall/confirm');
+    $this->assertResponse(403);
+
     // Uninstall module_test.
     $edit = array();
     $edit['uninstall[module_test]'] = TRUE;
diff --git a/core/modules/user/src/Form/UserMultipleCancelConfirm.php b/core/modules/user/src/Form/UserMultipleCancelConfirm.php
index 45f340f..f92900a 100644
--- a/core/modules/user/src/Form/UserMultipleCancelConfirm.php
+++ b/core/modules/user/src/Form/UserMultipleCancelConfirm.php
@@ -10,6 +10,7 @@
 use Drupal\Component\Utility\String;
 use Drupal\Core\Entity\EntityManagerInterface;
 use Drupal\Core\Form\ConfirmFormBase;
+use Drupal\Core\Form\FormBuilderInterface;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Url;
 use Drupal\user\TempStoreFactory;
@@ -43,6 +44,13 @@ class UserMultipleCancelConfirm extends ConfirmFormBase {
   protected $entityManager;
 
   /**
+   * The form builder.
+   *
+   * @var \Drupal\Core\Form\FormBuilderInterface
+   */
+  protected $formBuilder;
+
+  /**
    * Constructs a new UserMultipleCancelConfirm.
    *
    * @param \Drupal\user\TempStoreFactory $temp_store_factory
@@ -52,10 +60,11 @@ class UserMultipleCancelConfirm extends ConfirmFormBase {
    * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
    *   The entity manager.
    */
-  public function __construct(TempStoreFactory $temp_store_factory, UserStorageInterface $user_storage, EntityManagerInterface $entity_manager) {
+  public function __construct(TempStoreFactory $temp_store_factory, UserStorageInterface $user_storage, EntityManagerInterface $entity_manager, FormBuilderInterface $form_builder) {
     $this->tempStoreFactory = $temp_store_factory;
     $this->userStorage = $user_storage;
     $this->entityManager = $entity_manager;
+    $this->formBuilder = $form_builder;
   }
 
   /**
@@ -65,7 +74,8 @@ public static function create(ContainerInterface $container) {
     return new static(
       $container->get('user.tempstore'),
       $container->get('entity.manager')->getStorage('user'),
-      $container->get('entity.manager')
+      $container->get('entity.manager'),
+      $container->get('form_builder')
     );
   }
 
@@ -100,15 +110,30 @@ public function getConfirmText() {
   /**
    * {@inheritdoc}
    */
-  public function buildForm(array $form, FormStateInterface $form_state) {
-    // Retrieve the accounts to be canceled from the temp store.
-    $accounts = $this->tempStoreFactory
-      ->get('user_user_operations_cancel')
-      ->get($this->currentUser()->id());
-    if (!$accounts) {
+  public function getForm() {
+    $current_user_id = $this->currentUser()->id();
+    $user_temp_store = $this->tempStoreFactory->get('user_user_operations_cancel');
+    $accounts = $user_temp_store->get($current_user_id);
+    if (!$accounts || (isset($accounts[1]) && count($accounts) == 1)) {
+      if ($accounts) {
+        drupal_set_message($this->t('The user account %name cannot be canceled.', array('%name' => $accounts[1]->label())), 'error');
+      }
+      $user_temp_store->delete($current_user_id);
       return $this->redirect('user.admin_account');
     }
 
+    return $this->formBuilder->getForm($this);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildForm(array $form, FormStateInterface $form_state) {
+    $current_user_id = $this->currentUser()->id();
+    $user_temp_store = $this->tempStoreFactory->get('user_user_operations_cancel');
+    // Retrieve the accounts to be canceled from the temp store.
+    $accounts = $user_temp_store->get($current_user_id);
+
     $form['accounts'] = array('#prefix' => '<ul>', '#suffix' => '</ul>', '#tree' => TRUE);
     foreach ($accounts as $uid => $account) {
       // Prevent user 1 from being canceled.
@@ -125,13 +150,7 @@ public function buildForm(array $form, FormStateInterface $form_state) {
 
     // Output a notice that user 1 cannot be canceled.
     if (isset($accounts[1])) {
-      $redirect = (count($accounts) == 1);
-      $message = $this->t('The user account %name cannot be canceled.', array('%name' => $accounts[1]->label()));
-      drupal_set_message($message, $redirect ? 'error' : 'warning');
-      // If only user 1 was selected, redirect to the overview.
-      if ($redirect) {
-        return $this->redirect('user.admin_account');
-      }
+      drupal_set_message($this->t('The user account %name cannot be canceled.', array('%name' => $accounts[1]->label())), 'warning');
     }
 
     $form['operation'] = array('#type' => 'hidden', '#value' => 'cancel');
diff --git a/core/modules/user/src/Tests/UserCancelTest.php b/core/modules/user/src/Tests/UserCancelTest.php
index 9841ebe..7c39729 100644
--- a/core/modules/user/src/Tests/UserCancelTest.php
+++ b/core/modules/user/src/Tests/UserCancelTest.php
@@ -94,9 +94,10 @@ function testUserCancelUid1() {
     $this->drupalLogin($this->admin_user);
     $edit = array(
       'action' => 'user_cancel_user_action',
-      'user_bulk_form[0]' => TRUE,
+      'user_bulk_form[1]' => TRUE,
     );
     $this->drupalPostForm('admin/people', $edit, t('Apply'));
+    $this->assertResponse(200);
 
     // Verify that uid 1's account was not cancelled.
     $user1 = user_load(1, TRUE);
@@ -145,6 +146,10 @@ function testUserCancelInvalid() {
     $node_storage->resetCache(array($node->id()));
     $test_node = $node_storage->load($node->id());
     $this->assertTrue(($test_node->getOwnerId() == $account->id() && $test_node->isPublished()), 'Node of the user has not been altered.');
+
+    // Attempt to visit the bulk user cancel page without selecting users.
+    $this->drupalGet('admin/people/cancel');
+    $this->assertResponse(403);
   }
 
   /**
diff --git a/core/modules/user/user.routing.yml b/core/modules/user/user.routing.yml
index 4e1b99d..3e54f73 100644
--- a/core/modules/user/user.routing.yml
+++ b/core/modules/user/user.routing.yml
@@ -78,7 +78,7 @@ entity.user_role.edit_permissions_form:
 user.multiple_cancel_confirm:
   path: '/admin/people/cancel'
   defaults:
-    _form: '\Drupal\user\Form\UserMultipleCancelConfirm'
+    _content: '\Drupal\user\Form\UserMultipleCancelConfirm::getForm'
     _title: 'Cancel user'
   requirements:
     _permission: 'administer users'
diff --git a/core/modules/views_ui/src/Form/Ajax/ViewsFormBase.php b/core/modules/views_ui/src/Form/Ajax/ViewsFormBase.php
index e265ce7..34c3207 100644
--- a/core/modules/views_ui/src/Form/Ajax/ViewsFormBase.php
+++ b/core/modules/views_ui/src/Form/Ajax/ViewsFormBase.php
@@ -157,7 +157,7 @@ public function getForm(ViewStorageInterface $view, $display_id, $js) {
     elseif (!$form_state->get('ajax')) {
       // if nothing on the stack, non-js forms just go back to the main view editor.
       $display_id = $form_state->get('display_id');
-      return new RedirectResponse($this->url('entity.view.edit_display_form', ['view' => $view->id(), 'display_id' => $display_id], ['absolute' => TRUE]));
+      return $this->redirect('entity.view.edit_display_form', ['view' => $view->id(), 'display_id' => $display_id]);
     }
     else {
       $response = new AjaxResponse();
diff --git a/core/tests/Drupal/Tests/Core/Form/FormBuilderTest.php b/core/tests/Drupal/Tests/Core/Form/FormBuilderTest.php
index e677fdc..2c56ce9 100644
--- a/core/tests/Drupal/Tests/Core/Form/FormBuilderTest.php
+++ b/core/tests/Drupal/Tests/Core/Form/FormBuilderTest.php
@@ -356,19 +356,34 @@ public function testGetCache() {
   /**
    * Tests the sendResponse() method.
    *
+   * @covers ::sendResponse
+   *
    * @expectedException \Exception
    */
   public function testSendResponse() {
     $form_id = 'test_form_id';
-    $expected_form = $this->getMockBuilder('Symfony\Component\HttpFoundation\Response')
+    $expected_form = $form_id();
+    $response = $this->getMockBuilder('Symfony\Component\HttpFoundation\Response')
       ->disableOriginalConstructor()
       ->getMock();
 
     $form_arg = $this->getMockForm($form_id, $expected_form);
+    $form_arg->expects($this->any())
+      ->method('submitForm')
+      ->will($this->returnCallback(function ($form, FormStateInterface $form_state) use ($response) {
+        // Set the response.
+        $form_state->setResponse($response);
+      }));
+
+    $this->eventDispatcher->expects($this->once())
+      ->method('dispatch');
+    $this->kernel->expects($this->once())
+      ->method('terminate');
 
-    // Do an initial build of the form and track the build ID.
     $form_state = new FormState();
-    $this->formBuilder->buildForm($form_arg, $form_state);
+    $input['form_id'] = $form_id;
+    $form_state->setUserInput($input);
+    $this->simulateFormSubmission($form_id, $form_arg, $form_state, FALSE);
   }
 
   /**
@@ -401,6 +416,29 @@ public function testUniqueHtmlId() {
     $this->assertSame('test-form-id--2', $form['#id']);
   }
 
+  /**
+   * Tests FormInterface::buildForm() returning a Response object.
+   *
+   * @covers ::retrieveForm
+   *
+   * @expectedException \UnexpectedValueException
+   * @expectedExceptionMessage Form builder functions must return arrays
+   */
+  public function testFormBuildForm() {
+    $form_id = 'test_form_id';
+    $expected_form = $this->getMockBuilder('Symfony\Component\HttpFoundation\Response')
+      ->disableOriginalConstructor()
+      ->getMock();
+    $expected_form->expects($this->never())
+      ->method('prepare');
+
+    $form_arg = $this->getMockForm($form_id, $expected_form);
+
+    // Do an initial build of the form and track the build ID.
+    $form_state = new FormState();
+    $this->formBuilder->buildForm($form_arg, $form_state);
+  }
+
 }
 
 class TestForm implements FormInterface {
