diff --git a/core/modules/update/tests/modules/update_test/lib/Drupal/update_test/Controller/UpdateTestController.php b/core/modules/update/tests/modules/update_test/lib/Drupal/update_test/Controller/UpdateTestController.php
new file mode 100644
index 0000000..172292a
--- /dev/null
+++ b/core/modules/update/tests/modules/update_test/lib/Drupal/update_test/Controller/UpdateTestController.php
@@ -0,0 +1,87 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\update_test\Controller\UpdateTestController.
+ */
+
+namespace Drupal\update_test\Controller;
+
+use Symfony\Component\HttpFoundation\Response;
+use Symfony\Component\HttpFoundation\BinaryFileResponse;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+use Drupal\Core\Config\ConfigFactory;
+use Drupal\Core\Controller\ControllerInterface;
+
+/**
+ * Provides different routes of the update_test module.
+ */
+class UpdateTestController implements ControllerInterface {
+  protected $config;
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static($container->get('config.factory'));
+  }
+
+  /**
+   * Constructs a UpdateTestController object.
+   *
+   * @param \Drupal\Core\Config\ConfigFactory $config_factory
+   *   The factory for configuration objects.
+   */
+  public function __construct(ConfigFactory $config_factory) {
+    $this->config = $config_factory->get('update_test.settings');
+  }
+
+
+  /**
+   * Prints mock XML for the Update Manager module.
+   *
+   * The specific XML file to print depends on two things: the project we're
+   * trying to fetch data for, and the desired "availability scenario" for that
+   * project which we're trying to test. Before attempting to fetch this data (by
+   * checking for updates on the available updates report), callers need to define
+   * the 'update_test_xml_map' variable as an array, keyed by project name,
+   * indicating which availability scenario to use for that project.
+   *
+   * @param string $project_name
+   *   The project short name the update manager is trying to fetch data for (the
+   *   fetch URLs are of the form: [base_url]/[project_name]/[core_version]).
+   *
+   * @return BinaryFileResponse|Response
+   *   A BinaryFileResponse object containing the content of the XML release file
+   *   for the specified project if one is available; a Response object with no
+   *   content otherwise.
+   */
+  public function mockPage($project_name) {
+    $xml_map = $this->config->get('xml_map');
+    if (isset($xml_map[$project_name])) {
+      $availability_scenario = $xml_map[$project_name];
+    }
+    elseif (isset($xml_map['#all'])) {
+      $availability_scenario = $xml_map['#all'];
+    }
+    else {
+      // The test didn't specify (for example, the webroot has other modules and
+      // themes installed but they're disabled by the version of the site
+      // running the test. So, we default to a file we know won't exist, so at
+      // least we'll get an empty xml response instead of a bunch of Drupal page
+      // output.
+      $availability_scenario = '#broken#';
+    }
+
+    $path = drupal_get_path('module', 'update_test');
+    $file = "$path/$project_name.$availability_scenario.xml";
+    $headers = array('Content-Type' => 'text/xml; charset=utf-8');
+    if (!is_file($file)) {
+      // Return an empty response.
+      return new Response('', 200, $headers);
+    }
+    return new BinaryFileResponse($file, 200, $headers);
+  }
+
+}
diff --git a/core/modules/update/tests/modules/update_test/update_test.module b/core/modules/update/tests/modules/update_test/update_test.module
index a8c9e22..a965916 100644
--- a/core/modules/update/tests/modules/update_test/update_test.module
+++ b/core/modules/update/tests/modules/update_test/update_test.module
@@ -23,12 +23,6 @@ function update_test_system_theme_info() {
 function update_test_menu() {
   $items = array();
 
-  $items['update-test'] = array(
-    'title' => t('Update test'),
-    'page callback' => 'update_test_mock_page',
-    'access callback' => TRUE,
-    'type' => MENU_CALLBACK,
-  );
   $items['503-error'] = array(
     'title' => t('503 Service unavailable'),
     'page callback' => 'update_callback_service_unavailable',
@@ -88,54 +82,6 @@ function update_test_update_status_alter(&$projects) {
 }
 
 /**
- * Page callback: Prints mock XML for the Update Manager module.
- *
- * The specific XML file to print depends on two things: the project we're
- * trying to fetch data for, and the desired "availability scenario" for that
- * project which we're trying to test. Before attempting to fetch this data (by
- * checking for updates on the available updates report), callers need to define
- * the 'update_test_xml_map' variable as an array, keyed by project name,
- * indicating which availability scenario to use for that project.
- *
- * @param $project_name
- *   The project short name the update manager is trying to fetch data for (the
- *   fetch URLs are of the form: [base_url]/[project_name]/[core_version]).
- *
- * @return BinaryFileResponse|Response
- *   A BinaryFileResponse object containing the content of the XML release file
- *   for the specified project if one is available; a Response object with no
- *   content otherwise.
- *
- * @see update_test_menu()
- */
-function update_test_mock_page($project_name) {
-  $xml_map = Drupal::config('update_test.settings')->get('xml_map');
-  if (isset($xml_map[$project_name])) {
-    $availability_scenario = $xml_map[$project_name];
-  }
-  elseif (isset($xml_map['#all'])) {
-    $availability_scenario = $xml_map['#all'];
-  }
-  else {
-    // The test didn't specify (for example, the webroot has other modules and
-    // themes installed but they're disabled by the version of the site
-    // running the test. So, we default to a file we know won't exist, so at
-    // least we'll get an empty xml response instead of a bunch of Drupal page
-    // output.
-    $availability_scenario = '#broken#';
-  }
-
-  $path = drupal_get_path('module', 'update_test');
-  $file = "$path/$project_name.$availability_scenario.xml";
-  $headers = array('Content-Type' => 'text/xml; charset=utf-8');
-  if (!is_file($file)) {
-    // Return an empty response.
-    return new Response('', 200, $headers);
-  }
-  return new BinaryFileResponse($file, 200, $headers);
-}
-
-/**
  * Implements hook_archiver_info_alter().
  */
 function update_test_archiver_info_alter(&$definitions) {
diff --git a/core/modules/update/tests/modules/update_test/update_test.routing.yml b/core/modules/update/tests/modules/update_test/update_test.routing.yml
new file mode 100644
index 0000000..42865ed
--- /dev/null
+++ b/core/modules/update/tests/modules/update_test/update_test.routing.yml
@@ -0,0 +1,6 @@
+update_test:
+  pattern: 'update-test/{project_name}'
+  defaults:
+    _controller: 'Drupal\update_test\Controller\UpdateTestController::mockPage'
+  requirements:
+    _access: 'TRUE'
