diff --git a/core/modules/image/tests/src/Functional/ImageEffectsTest.php b/core/modules/image/tests/src/Functional/ImageEffectsTest.php
index e9cb157abe..3207442ab2 100644
--- a/core/modules/image/tests/src/Functional/ImageEffectsTest.php
+++ b/core/modules/image/tests/src/Functional/ImageEffectsTest.php
@@ -3,7 +3,7 @@
 namespace Drupal\Tests\image\Functional;
 
 use Drupal\image\Entity\ImageStyle;
-use Drupal\system\Tests\Image\ToolkitTestBase;
+use Drupal\FunctionalTests\Image\ToolkitTestBase;
 
 /**
  * Tests that the image effects pass parameters to the toolkit correctly.
diff --git a/core/modules/system/src/Tests/Image/ToolkitTestBase.php b/core/modules/system/src/Tests/Image/ToolkitTestBase.php
index 0051e94117..9c3b661861 100644
--- a/core/modules/system/src/Tests/Image/ToolkitTestBase.php
+++ b/core/modules/system/src/Tests/Image/ToolkitTestBase.php
@@ -2,11 +2,19 @@
 
 namespace Drupal\system\Tests\Image;
 
+@trigger_error(__FILE__ . ' is deprecated in Drupal 8.4.0 and will be removed before Drupal 9.0.0. Use Drupal\FunctionalTests\Image\ToolkitTestBase instead. See https://www.drupal.org/node/2862641.', E_USER_DEPRECATED);
+
 use Drupal\simpletest\WebTestBase;
 use Drupal\Component\Utility\SafeMarkup;
 
 /**
  * Base class for image manipulation testing.
+ *
+ * @deprecated in Drupal 8.4.0 and will be removed before Drupal 9.0.0.
+ *   Use Drupal\FunctionalTests\Image\ToolkitTestBase instead.
+ *
+ * @see https://www.drupal.org/node/2862641
+ */
  */
 abstract class ToolkitTestBase extends WebTestBase {
 
diff --git a/core/modules/system/tests/modules/image_test/src/Plugin/ImageToolkit/TestToolkit.php b/core/modules/system/tests/modules/image_test/src/Plugin/ImageToolkit/TestToolkit.php
index a28c786904..a0f508041b 100644
--- a/core/modules/system/tests/modules/image_test/src/Plugin/ImageToolkit/TestToolkit.php
+++ b/core/modules/system/tests/modules/image_test/src/Plugin/ImageToolkit/TestToolkit.php
@@ -161,8 +161,8 @@ public function save($destination) {
    * @param array $args
    *   Values passed to hook.
    *
-   * @see \Drupal\system\Tests\Image\ToolkitTestBase::imageTestReset()
-   * @see \Drupal\system\Tests\Image\ToolkitTestBase::imageTestGetAllCalls()
+   * @see \Drupal\Tests\system\Functional\Image\ToolkitTestBase::imageTestReset()
+   * @see \Drupal\Tests\system\Functional\Image\ToolkitTestBase::imageTestGetAllCalls()
    */
   protected function logCall($op, $args) {
     $results = $this->state->get('image_test.results') ?: [];
diff --git a/core/modules/system/src/Tests/Image/ToolkitSetupFormTest.php b/core/tests/Drupal/FunctionalTests/Image/ToolkitSetupFormTest.php
similarity index 94%
rename from core/modules/system/src/Tests/Image/ToolkitSetupFormTest.php
rename to core/tests/Drupal/FunctionalTests/Image/ToolkitSetupFormTest.php
index 7dc9d33874..d501f1d98c 100644
--- a/core/modules/system/src/Tests/Image/ToolkitSetupFormTest.php
+++ b/core/tests/Drupal/FunctionalTests/Image/ToolkitSetupFormTest.php
@@ -1,15 +1,15 @@
 <?php
 
-namespace Drupal\system\Tests\Image;
+namespace Drupal\FunctionalTests\Image;
 
-use Drupal\simpletest\WebTestBase;
+use Drupal\Tests\BrowserTestBase;
 
 /**
  * Tests image toolkit setup form.
  *
  * @group Image
  */
-class ToolkitSetupFormTest extends WebTestBase {
+class ToolkitSetupFormTest extends BrowserTestBase {
 
   /**
    * Admin user account.
diff --git a/core/modules/system/src/Tests/Image/ToolkitTest.php b/core/tests/Drupal/FunctionalTests/Image/ToolkitTest.php
similarity index 98%
rename from core/modules/system/src/Tests/Image/ToolkitTest.php
rename to core/tests/Drupal/FunctionalTests/Image/ToolkitTest.php
index f70c261efb..789369ceeb 100644
--- a/core/modules/system/src/Tests/Image/ToolkitTest.php
+++ b/core/tests/Drupal/FunctionalTests/Image/ToolkitTest.php
@@ -1,6 +1,6 @@
 <?php
 
-namespace Drupal\system\Tests\Image;
+namespace Drupal\FunctionalTests\Image;
 
 /**
  * Tests image toolkit functions.
diff --git a/core/tests/Drupal/FunctionalTests/Image/ToolkitTestBase.php b/core/tests/Drupal/FunctionalTests/Image/ToolkitTestBase.php
new file mode 100644
index 0000000000..16cd4aa550
--- /dev/null
+++ b/core/tests/Drupal/FunctionalTests/Image/ToolkitTestBase.php
@@ -0,0 +1,159 @@
+<?php
+
+namespace Drupal\FunctionalTests\Image;
+
+use Drupal\Component\Utility\SafeMarkup;
+use Drupal\Tests\BrowserTestBase;
+use Drupal\Tests\TestFileCreationTrait;
+
+
+/**
+ * Base class for image manipulation testing.
+ */
+abstract class ToolkitTestBase extends BrowserTestBase {
+
+  use TestFileCreationTrait {
+    getTestFiles as drupalGetTestFiles;
+  }
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = ['image_test'];
+
+  /**
+   * The URI for the file.
+   *
+   * @var string
+   */
+  protected $file;
+
+  /**
+   * The image factory service.
+   *
+   * @var \Drupal\Core\Image\ImageFactory
+   */
+  protected $imageFactory;
+
+  /**
+   * The image object for the test file.
+   *
+   * @var \Drupal\Core\Image\ImageInterface
+   */
+  protected $image;
+
+  protected function setUp() {
+    parent::setUp();
+
+    // Set the image factory service.
+    $this->imageFactory = $this->container->get('image.factory');
+
+    // Pick a file for testing.
+    $file = current($this->drupalGetTestFiles('image'));
+    $this->file = $file->uri;
+
+    // Setup a dummy image to work with.
+    $this->image = $this->getImage();
+
+    // Clear out any hook calls.
+    $this->imageTestReset();
+  }
+
+  /**
+   * Sets up an image with the custom toolkit.
+   *
+   * @return \Drupal\Core\Image\ImageInterface
+   *   The image object.
+   */
+  protected function getImage() {
+    $image = $this->imageFactory->get($this->file, 'test');
+    $this->assertTrue($image->isValid(), 'Image file was parsed.');
+    return $image;
+  }
+
+  /**
+   * Assert that all of the specified image toolkit operations were called
+   * exactly once once, other values result in failure.
+   *
+   * @param $expected
+   *   Array with string containing with the operation name, e.g. 'load',
+   *   'save', 'crop', etc.
+   */
+  public function assertToolkitOperationsCalled(array $expected) {
+    // If one of the image operations is expected, apply should be expected as
+    // well.
+    $operations = [
+      'resize',
+      'rotate',
+      'crop',
+      'desaturate',
+      'create_new',
+      'scale',
+      'scale_and_crop',
+      'my_operation',
+      'convert',
+    ];
+    if (count(array_intersect($expected, $operations)) > 0 && !in_array('apply', $expected)) {
+      $expected[] = 'apply';
+    }
+
+    // Determine which operations were called.
+    $actual = array_keys(array_filter($this->imageTestGetAllCalls()));
+
+    // Determine if there were any expected that were not called.
+    $uncalled = array_diff($expected, $actual);
+    if (count($uncalled)) {
+      $this->assertTrue(FALSE, SafeMarkup::format('Expected operations %expected to be called but %uncalled was not called.', ['%expected' => implode(', ', $expected), '%uncalled' => implode(', ', $uncalled)]));
+    }
+    else {
+      $this->assertTrue(TRUE, SafeMarkup::format('All the expected operations were called: %expected', ['%expected' => implode(', ', $expected)]));
+    }
+
+    // Determine if there were any unexpected calls.
+    // If all unexpected calls are operations and apply was expected, we do not
+    // count it as an error.
+    $unexpected = array_diff($actual, $expected);
+    if (count($unexpected) && (!in_array('apply', $expected) || count(array_intersect($unexpected, $operations)) !== count($unexpected))) {
+      $this->assertTrue(FALSE, SafeMarkup::format('Unexpected operations were called: %unexpected.', ['%unexpected' => implode(', ', $unexpected)]));
+    }
+    else {
+      $this->assertTrue(TRUE, 'No unexpected operations were called.');
+    }
+  }
+
+  /**
+   * Resets/initializes the history of calls to the test toolkit functions.
+   */
+  public function imageTestReset() {
+    // Keep track of calls to these operations
+    $results = [
+      'parseFile' => [],
+      'save' => [],
+      'settings' => [],
+      'apply' => [],
+      'resize' => [],
+      'rotate' => [],
+      'crop' => [],
+      'desaturate' => [],
+      'create_new' => [],
+      'scale' => [],
+      'scale_and_crop' => [],
+      'convert' => [],
+    ];
+    \Drupal::state()->set('image_test.results', $results);
+  }
+
+  /**
+   * Gets an array of calls to the test toolkit.
+   *
+   * @return array
+   *   An array keyed by operation name ('parseFile', 'save', 'settings',
+   *   'resize', 'rotate', 'crop', 'desaturate') with values being arrays of
+   *   parameters passed to each call.
+   */
+  public function imageTestGetAllCalls() {
+    return \Drupal::state()->get('image_test.results') ?: [];
+  }
+
+}
