diff --git a/src/Tests/CacheabilityTest.php b/src/Tests/CacheabilityTest.php
index 9969576..f8390ae 100644
--- a/src/Tests/CacheabilityTest.php
+++ b/src/Tests/CacheabilityTest.php
@@ -25,7 +25,7 @@ class CacheabilityTest extends WebTestBase {
    */
   public static $modules = array(
     'search_api',
-    'search_api_test_backend',
+    'search_api_test',
     'search_api_test_views',
   );
 
diff --git a/src/Tests/HooksTest.php b/src/Tests/HooksTest.php
index 29edb6c..16c4487 100644
--- a/src/Tests/HooksTest.php
+++ b/src/Tests/HooksTest.php
@@ -15,7 +15,7 @@ class HooksTest extends WebTestBase {
   public static $modules = array(
     'node',
     'search_api',
-    'search_api_test_backend',
+    'search_api_test',
     'search_api_test_views',
     'search_api_test_hooks',
   );
diff --git a/src/Tests/IntegrationTest.php b/src/Tests/IntegrationTest.php
index 064ae87..b7b6718 100644
--- a/src/Tests/IntegrationTest.php
+++ b/src/Tests/IntegrationTest.php
@@ -6,7 +6,8 @@
 use Drupal\search_api\Entity\Server;
 use Drupal\search_api\SearchApiException;
 use Drupal\search_api\Utility;
-use Drupal\search_api_test_backend\Plugin\search_api\tracker\Test;
+use Drupal\search_api_test\Plugin\search_api\tracker\TestTracker;
+use Drupal\search_api_test\PluginTestTrait;
 
 /**
  * Tests the overall functionality of the Search API framework and admin UI.
@@ -15,6 +16,8 @@
  */
 class IntegrationTest extends WebTestBase {
 
+  use PluginTestTrait;
+
   /**
    * The ID of the search server used for this test.
    *
@@ -35,7 +38,7 @@ class IntegrationTest extends WebTestBase {
   public static $modules = array(
     'node',
     'search_api',
-    'search_api_test_backend',
+    'search_api_test',
     'field_ui',
     'link',
     'image',
@@ -159,7 +162,7 @@ protected function createServer($server_id = '_test_server') {
       'name' => '',
       'status' => 1,
       'description' => 'A server used for testing.',
-      'backend' => 'search_api_test_backend',
+      'backend' => 'search_api_test',
     );
 
     $this->drupalPostForm($settings_path, $edit, $this->t('Save'));
@@ -169,7 +172,7 @@ protected function createServer($server_id = '_test_server') {
       'name' => $server_name,
       'status' => 1,
       'description' => $server_description,
-      'backend' => 'search_api_test_backend',
+      'backend' => 'search_api_test',
     );
     $this->drupalPostForm($settings_path, $edit, $this->t('Save'));
     $this->assertText($this->t('@name field is required.', array('@name' => $this->t('Machine-readable name'))));
@@ -179,7 +182,7 @@ protected function createServer($server_id = '_test_server') {
       'id' => $this->serverId,
       'status' => 1,
       'description' => $server_description,
-      'backend' => 'search_api_test_backend',
+      'backend' => 'search_api_test',
     );
 
     $this->drupalPostForm(NULL, $edit, $this->t('Save'));
@@ -320,7 +323,7 @@ protected function editServer() {
     $remaining = $index->getTrackerInstance()->getRemainingItemsCount();
     $this->assertEqual(0, $remaining, 'Index was not scheduled for re-indexing when saving its server.');
 
-    \Drupal::state()->set('search_api_test_backend.return.postUpdate', TRUE);
+    $this->setReturnValue('backend', 'postUpdate', TRUE);
     $this->drupalPostForm($path, $edit, $this->t('Save'));
 
     $tracked_items = $this->countTrackedItems();
@@ -342,7 +345,7 @@ protected function editIndex() {
     $this->assertEqual(count($elements), 1, 'Machine name cannot be changed.');
 
     // Test the AJAX functionality for configuring the tracker.
-    $edit = array('tracker' => 'search_api_test_backend');
+    $edit = array('tracker' => 'search_api_test');
     $this->drupalPostAjaxForm(NULL, $edit, 'tracker_configure');
     $edit['tracker_config[foo]'] = 'foobar';
     $this->drupalPostForm(NULL, $edit, $this->t('Save'));
@@ -352,8 +355,12 @@ protected function editIndex() {
     // Verify that everything was changed correctly.
     $index = $this->getIndex(TRUE);
     $tracker = $index->getTrackerInstance();
-    $this->assertTrue($tracker instanceof Test, 'Tracker was successfully switched.');
-    $this->assertEqual($tracker->getConfiguration(), array('foo' => 'foobar'), 'Tracker config was successfully saved.');
+    $this->assertTrue($tracker instanceof TestTracker, 'Tracker was successfully switched.');
+    $configuration = array(
+      'foo' => 'foobar',
+      'dependencies' => array(),
+    );
+    $this->assertEqual($tracker->getConfiguration(), $configuration, 'Tracker config was successfully saved.');
     $this->assertEqual($this->countTrackedItems(), $tracked_items, 'Items are still correctly tracked.');
 
     // Revert back to the default tracker for the rest of the test.
@@ -389,12 +396,12 @@ protected function checkServerAvailability() {
     $this->assertResponse(200);
     $this->assertRaw($this->t('Enabled'));
 
-    \Drupal::state()->set('search_api_test_backend.return.isAvailable', FALSE);
+    $this->setReturnValue('backend', 'isAvailable', FALSE);
     $this->drupalGet('admin/config/search/search-api');
     $this->assertResponse(200);
     $this->assertRaw($this->t('Unavailable'));
 
-    \Drupal::state()->set('search_api_test_backend.return.isAvailable', TRUE);
+    $this->setReturnValue('backend', 'isAvailable', TRUE);
   }
 
   /**
@@ -656,7 +663,7 @@ protected function addFieldsToIndex() {
     $edit = array(
       'fields[title][type]' => 'text',
       'fields[title][boost]' => '21.0',
-      'fields[revision_log][type]' => 'search_api_test_data_type',
+      'fields[revision_log][type]' => 'search_api_test',
     );
     $this->drupalPostForm($this->getIndexPath('fields'), $edit, $this->t('Save changes'));
     $this->assertText($this->t('The changes were successfully saved.'));
@@ -669,7 +676,7 @@ protected function addFieldsToIndex() {
       $this->assertEqual($fields['title']->getBoost(), $edit['fields[title][boost]'], 'title field boost value is 21.');
     }
     if ($this->assertTrue(!empty($fields['revision_log']), 'revision_log field is indexed.')) {
-      $this->assertEqual($fields['revision_log']->getType(), $edit['fields[revision_log][type]'], 'revision_log field type is search_api_test_data_type.');
+      $this->assertEqual($fields['revision_log']->getType(), $edit['fields[revision_log][type]'], 'revision_log field type is search_api_test.');
     }
   }
 
@@ -848,7 +855,7 @@ protected function checkReferenceFieldsNonBaseFields() {
     $this->drupalGet('node/2/edit');
     $edit = array('field__reference_field_[0][target_id]' => 'Something (2)');
     $this->drupalPostForm('node/2/edit', $edit, $this->t('Save and keep published'));
-    $indexed_values = \Drupal::state()->get("search_api_test_backend.indexed.{$this->indexId}", array());
+    $indexed_values = \Drupal::state()->get("search_api_test.indexed.{$this->indexId}", array());
     $this->assertEqual(array(2), $indexed_values['entity:node/2:en']['field__reference_field_'], 'Correct value indexed for nested non-base field.');
   }
 
diff --git a/src/Tests/LanguageIntegrationTest.php b/src/Tests/LanguageIntegrationTest.php
index 6faaec0..0118001 100644
--- a/src/Tests/LanguageIntegrationTest.php
+++ b/src/Tests/LanguageIntegrationTest.php
@@ -18,7 +18,7 @@ class LanguageIntegrationTest extends WebTestBase {
   public static $modules = array(
     'node',
     'search_api',
-    'search_api_test_backend',
+    'search_api_test',
     'language',
   );
 
diff --git a/src/Tests/ProcessorIntegrationTest.php b/src/Tests/ProcessorIntegrationTest.php
index e1a58fa..57aed1c 100644
--- a/src/Tests/ProcessorIntegrationTest.php
+++ b/src/Tests/ProcessorIntegrationTest.php
@@ -4,6 +4,7 @@
 
 use Drupal\search_api\Entity\Index;
 use Drupal\search_api\Entity\Server;
+use Drupal\search_api_test\PluginTestTrait;
 
 /**
  * Tests the admin UI for processors.
@@ -14,6 +15,8 @@
 // @todo Add tests for the "Aggregated fields" and "Role filter" processors.
 class ProcessorIntegrationTest extends WebTestBase {
 
+  use PluginTestTrait;
+
   /**
    * {@inheritdoc}
    */
@@ -148,23 +151,22 @@ public function testLimitProcessors() {
     $this->assertText($this->t('Tokenizer'));
     $this->assertText($this->t('Stopwords'));
 
-    // Create a new server with the "search_api_test_backend" backend.
+    // Create a new server with the "search_api_test" backend.
     $server = Server::create(array(
       'id' => 'webtest_server',
       'name' => 'WebTest server',
       'description' => 'WebTest server',
-      'backend' => 'search_api_test_backend',
+      'backend' => 'search_api_test',
       'backend_config' => array(),
     ));
     $server->save();
-    $key = 'search_api_test_backend.return.getDiscouragedProcessors';
     $processors = array(
       'highlight',
       'ignore_character',
       'tokenizer',
       'stopwords',
     );
-    \Drupal::state()->set($key, $processors);
+    $this->setReturnValue('backend', 'getDiscouragedProcessors', $processors);
 
     // Use the newly created server.
     $settings_path = 'admin/config/search/search-api/index/' . $this->indexId . '/edit';
diff --git a/src/Tests/WebTestBase.php b/src/Tests/WebTestBase.php
index 59995eb..cc6d0a3 100644
--- a/src/Tests/WebTestBase.php
+++ b/src/Tests/WebTestBase.php
@@ -22,7 +22,7 @@
   public static $modules = array(
     'node',
     'search_api',
-    'search_api_test_backend',
+    'search_api_test',
   );
 
   /**
@@ -115,7 +115,7 @@ public function setUp() {
    * @return \Drupal\search_api\ServerInterface
    *   A search server.
    */
-  public function getTestServer($name = 'WebTest server', $id = 'webtest_server', $backend_id = 'search_api_test_backend', $backend_config = array(), $reset = FALSE) {
+  public function getTestServer($name = 'WebTest server', $id = 'webtest_server', $backend_id = 'search_api_test', $backend_config = array(), $reset = FALSE) {
     if ($reset) {
       /** @var \Drupal\search_api\ServerInterface $server */
       $server = Server::load($id);
diff --git a/tests/search_api_test/config/schema/search_api_test.schema.yml b/tests/search_api_test/config/schema/search_api_test.schema.yml
new file mode 100644
index 0000000..6b482e2
--- /dev/null
+++ b/tests/search_api_test/config/schema/search_api_test.schema.yml
@@ -0,0 +1,27 @@
+plugin.plugin_configuration.search_api_backend.search_api_test:
+  type: mapping
+  label: Test backend
+  mapping:
+    test:
+      type: string
+      label: Test configuration
+    dependencies:
+      type: config_dependencies
+      label: Dependencies
+
+plugin.plugin_configuration.search_api_datasource.search_api_test:
+  type: config_dependencies
+
+plugin.plugin_configuration.search_api_processor.search_api_test:
+  type: config_dependencies
+
+plugin.plugin_configuration.search_api_tracker.search_api_test:
+  type: mapping
+  label: Test tracker
+  mapping:
+    foo:
+      type: string
+      label: Foo
+    dependencies:
+      type: config_dependencies
+      label: Dependencies
diff --git a/tests/search_api_test_dependencies/search_api_test_dependencies.info.yml b/tests/search_api_test/search_api_test.info.yml
similarity index 79%
rename from tests/search_api_test_dependencies/search_api_test_dependencies.info.yml
rename to tests/search_api_test/search_api_test.info.yml
index db94294..7bf224d 100644
--- a/tests/search_api_test_dependencies/search_api_test_dependencies.info.yml
+++ b/tests/search_api_test/search_api_test.info.yml
@@ -1,4 +1,4 @@
-name: 'Search API Test Dependencies'
+name: 'Search API Test'
 type: module
 description: 'Support module for Search API tests'
 package: Search
diff --git a/tests/search_api_test_backend/search_api_test_backend.module b/tests/search_api_test/search_api_test.module
similarity index 78%
rename from tests/search_api_test_backend/search_api_test_backend.module
rename to tests/search_api_test/search_api_test.module
index 4d1671c..9101af7 100644
--- a/tests/search_api_test_backend/search_api_test_backend.module
+++ b/tests/search_api_test/search_api_test.module
@@ -11,7 +11,7 @@
 /**
  * Implement hook_node_grants().
  */
-function search_api_test_backend_node_grants(AccountInterface $account, $op) {
+function search_api_test_node_grants(AccountInterface $account, $op) {
   $grants['search_api_test'] = array($account->id());
   return $grants;
 }
@@ -19,7 +19,7 @@ function search_api_test_backend_node_grants(AccountInterface $account, $op) {
 /**
  * Implement hook_node_access_records().
  */
-function search_api_test_backend_node_access_records(NodeInterface $node) {
+function search_api_test_node_access_records(NodeInterface $node) {
   $grants[] = array(
     'realm' => 'search_api_test',
     'gid' => $node->getOwnerId(),
diff --git a/tests/search_api_test_backend/src/Plugin/search_api/backend/TestBackend.php b/tests/search_api_test/src/Plugin/search_api/backend/TestBackend.php
similarity index 77%
rename from tests/search_api_test_backend/src/Plugin/search_api/backend/TestBackend.php
rename to tests/search_api_test/src/Plugin/search_api/backend/TestBackend.php
index 3900557..09b4f08 100644
--- a/tests/search_api_test_backend/src/Plugin/search_api/backend/TestBackend.php
+++ b/tests/search_api_test/src/Plugin/search_api/backend/TestBackend.php
@@ -1,25 +1,29 @@
 <?php
 
-namespace Drupal\search_api_test_backend\Plugin\search_api\backend;
+namespace Drupal\search_api_test\Plugin\search_api\backend;
 
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\search_api\Backend\BackendPluginBase;
-use Drupal\search_api\SearchApiException;
 use Drupal\search_api\IndexInterface;
 use Drupal\search_api\Query\QueryInterface;
 use Drupal\search_api\Utility;
+use Drupal\search_api_test\TestPluginTrait;
 
 /**
  * Provides a dummy backend for testing purposes.
  *
  * @SearchApiBackend(
- *   id = "search_api_test_backend",
+ *   id = "search_api_test",
  *   label = @Translation("Test backend"),
  *   description = @Translation("Dummy backend implementation")
  * )
  */
 class TestBackend extends BackendPluginBase {
 
+  use TestPluginTrait {
+    checkError as traitCheckError;
+  }
+
   /**
    * {@inheritdoc}
    */
@@ -63,7 +67,7 @@ public function supportsFeature($feature) {
    * {@inheritdoc}
    */
   public function supportsDataType($type) {
-    return $type == 'search_api_test_data_type' || $type == 'search_api_altering_test_data_type';
+    return $type == 'search_api_test' || $type == 'search_api_test_altering';
   }
 
   /**
@@ -92,7 +96,7 @@ public function indexItems(IndexInterface $index, array $items) {
     $this->checkError(__FUNCTION__);
 
     $state = \Drupal::state();
-    $key = 'search_api_test_backend.indexed.' . $index->id();
+    $key = 'search_api_test.indexed.' . $index->id();
     $indexed_values = $state->get($key, array());
     /** @var \Drupal\search_api\Item\ItemInterface $item */
     foreach ($items as $id => $item) {
@@ -214,41 +218,11 @@ public function onDependencyRemoval(array $dependencies) {
   }
 
   /**
-   * Throws an exception if set in the Drupal state for the given method.
-   *
-   * Also records (successful) calls to these methods.
-   *
-   * @param string $method
-   *   The method on this object from which this method was called.
-   *
-   * @throws \Drupal\search_api\SearchApiException
-   *   Thrown if state "search_api_test_backend.exception.$method" is TRUE.
+   * {@inheritdoc}
    */
   protected function checkError($method) {
-    $state = \Drupal::state();
-    if ($state->get("search_api_test_backend.exception.$method")) {
-      throw new SearchApiException($method);
-    }
-    $key = 'search_api_test_backend.methods_called.' . $this->server->id();
-    $methods_called = $state->get($key, array());
-    $methods_called[] = $method;
-    $state->set($key, $methods_called);
-  }
-
-  /**
-   * Retrieves the value to return for a certain method.
-   *
-   * @param string $method
-   *   The name of the called method.
-   * @param mixed $default
-   *   (optional) The default return value.
-   *
-   * @return mixed
-   *   The value to return from the method.
-   */
-  protected function getReturnValue($method, $default = NULL) {
-    $key = "search_api_test_backend.return.$method";
-    return \Drupal::state()->get($key, $default);
+    $this->traitCheckError($method);
+    $this->logMethodCall($method);
   }
 
 }
diff --git a/tests/search_api_test_backend/src/Plugin/search_api/data_type/AlteringValueTestDataType.php b/tests/search_api_test/src/Plugin/search_api/data_type/AlteringValueTestDataType.php
similarity index 79%
rename from tests/search_api_test_backend/src/Plugin/search_api/data_type/AlteringValueTestDataType.php
rename to tests/search_api_test/src/Plugin/search_api/data_type/AlteringValueTestDataType.php
index 132ced4..7eaf09e 100644
--- a/tests/search_api_test_backend/src/Plugin/search_api/data_type/AlteringValueTestDataType.php
+++ b/tests/search_api_test/src/Plugin/search_api/data_type/AlteringValueTestDataType.php
@@ -1,6 +1,6 @@
 <?php
 
-namespace Drupal\search_api_test_backend\Plugin\search_api\data_type;
+namespace Drupal\search_api_test\Plugin\search_api\data_type;
 
 use Drupal\search_api\DataType\DataTypePluginBase;
 
@@ -8,7 +8,7 @@
  * Provides a dummy data type for testing purposes.
  *
  * @SearchApiDataType(
- *   id = "search_api_altering_test_data_type",
+ *   id = "search_api_test_altering",
  *   label = @Translation("Altering test data type"),
  *   description = @Translation("Altering dummy data type implementation")
  * )
diff --git a/tests/search_api_test_backend/src/Plugin/search_api/data_type/TestDataType.php b/tests/search_api_test/src/Plugin/search_api/data_type/TestDataType.php
similarity index 74%
rename from tests/search_api_test_backend/src/Plugin/search_api/data_type/TestDataType.php
rename to tests/search_api_test/src/Plugin/search_api/data_type/TestDataType.php
index 5d13c31..9d3cf2e 100644
--- a/tests/search_api_test_backend/src/Plugin/search_api/data_type/TestDataType.php
+++ b/tests/search_api_test/src/Plugin/search_api/data_type/TestDataType.php
@@ -1,6 +1,6 @@
 <?php
 
-namespace Drupal\search_api_test_backend\Plugin\search_api\data_type;
+namespace Drupal\search_api_test\Plugin\search_api\data_type;
 
 use Drupal\search_api\DataType\DataTypePluginBase;
 
@@ -8,7 +8,7 @@
  * Provides a dummy data type for testing purposes.
  *
  * @SearchApiDataType(
- *   id = "search_api_test_data_type",
+ *   id = "search_api_test",
  *   label = @Translation("Test data type"),
  *   description = @Translation("Dummy data type implementation")
  * )
diff --git a/tests/search_api_test_backend/src/Plugin/search_api/data_type/UnsupportedTestDataType.php b/tests/search_api_test/src/Plugin/search_api/data_type/UnsupportedTestDataType.php
similarity index 78%
rename from tests/search_api_test_backend/src/Plugin/search_api/data_type/UnsupportedTestDataType.php
rename to tests/search_api_test/src/Plugin/search_api/data_type/UnsupportedTestDataType.php
index e677157..edc7603 100644
--- a/tests/search_api_test_backend/src/Plugin/search_api/data_type/UnsupportedTestDataType.php
+++ b/tests/search_api_test/src/Plugin/search_api/data_type/UnsupportedTestDataType.php
@@ -1,6 +1,6 @@
 <?php
 
-namespace Drupal\search_api_test_backend\Plugin\search_api\data_type;
+namespace Drupal\search_api_test\Plugin\search_api\data_type;
 
 use Drupal\search_api\DataType\DataTypePluginBase;
 
@@ -8,7 +8,7 @@
  * Provides a dummy data type for testing purposes.
  *
  * @SearchApiDataType(
- *   id = "search_api_unsupported_test_data_type",
+ *   id = "search_api_test_unsupported",
  *   label = @Translation("Unsupported test data type"),
  *   description = @Translation("Unsupported dummy data type implementation")
  * )
diff --git a/tests/search_api_test_dependencies/src/Plugin/search_api/datasource/TestDatasource.php b/tests/search_api_test/src/Plugin/search_api/datasource/TestDatasource.php
similarity index 57%
rename from tests/search_api_test_dependencies/src/Plugin/search_api/datasource/TestDatasource.php
rename to tests/search_api_test/src/Plugin/search_api/datasource/TestDatasource.php
index 3cc019a..368c8cf 100644
--- a/tests/search_api_test_dependencies/src/Plugin/search_api/datasource/TestDatasource.php
+++ b/tests/search_api_test/src/Plugin/search_api/datasource/TestDatasource.php
@@ -1,20 +1,23 @@
 <?php
 
-namespace Drupal\search_api_test_dependencies\Plugin\search_api\datasource;
+namespace Drupal\search_api_test\Plugin\search_api\datasource;
 
 use Drupal\Core\TypedData\ComplexDataInterface;
 use Drupal\search_api\Datasource\DatasourcePluginBase;
+use Drupal\search_api_test\TestPluginTrait;
 
 /**
- * Provides a datasource with dependencies, for the dependency removal tests.
+ * Provides a datasource for testing purposes.
  *
  * @SearchApiDatasource(
- *   id = "search_api_test_dependencies",
- *   label = @Translation("Dependency test datasource"),
+ *   id = "search_api_test",
+ *   label = @Translation("Test datasource"),
  * )
  */
 class TestDatasource extends DatasourcePluginBase {
 
+  use TestPluginTrait;
+
   /**
    * {@inheritdoc}
    */
@@ -25,6 +28,13 @@ public function getItemId(ComplexDataInterface $item) {
   /**
    * {@inheritdoc}
    */
+  public function loadMultiple(array $ids) {
+    return $this->getReturnValue(__FUNCTION__, array());
+  }
+
+  /**
+   * {@inheritdoc}
+   */
   public function calculateDependencies() {
     return $this->configuration;
   }
@@ -33,8 +43,7 @@ public function calculateDependencies() {
    * {@inheritdoc}
    */
   public function onDependencyRemoval(array $dependencies) {
-    $remove = \Drupal::state()
-      ->get('search_api_test_dependencies.datasource.remove', FALSE);
+    $remove = $this->getReturnValue(__FUNCTION__, FALSE);
     if ($remove) {
       $this->configuration = array();
     }
diff --git a/tests/search_api_test_dependencies/src/Plugin/search_api/processor/TestProcessor.php b/tests/search_api_test/src/Plugin/search_api/processor/TestProcessor.php
similarity index 72%
rename from tests/search_api_test_dependencies/src/Plugin/search_api/processor/TestProcessor.php
rename to tests/search_api_test/src/Plugin/search_api/processor/TestProcessor.php
index 963195d..453d571 100644
--- a/tests/search_api_test_dependencies/src/Plugin/search_api/processor/TestProcessor.php
+++ b/tests/search_api_test/src/Plugin/search_api/processor/TestProcessor.php
@@ -1,19 +1,22 @@
 <?php
 
-namespace Drupal\search_api_test_dependencies\Plugin\search_api\processor;
+namespace Drupal\search_api_test\Plugin\search_api\processor;
 
 use Drupal\search_api\Processor\ProcessorPluginBase;
+use Drupal\search_api_test\TestPluginTrait;
 
 /**
  * Provides a processor with dependencies, for the dependency removal tests.
  *
  * @SearchApiProcessor(
- *   id = "search_api_test_dependencies",
+ *   id = "search_api_test",
  *   label = @Translation("Dependency test processor"),
  * )
  */
 class TestProcessor extends ProcessorPluginBase {
 
+  use TestPluginTrait;
+
   /**
    * {@inheritdoc}
    */
@@ -25,8 +28,7 @@ public function calculateDependencies() {
    * {@inheritdoc}
    */
   public function onDependencyRemoval(array $dependencies) {
-    $remove = \Drupal::state()
-      ->get('search_api_test_dependencies.processor.remove', FALSE);
+    $remove = $this->getReturnValue(__FUNCTION__, FALSE);
     if ($remove) {
       $this->configuration = array();
     }
diff --git a/tests/search_api_test/src/Plugin/search_api/tracker/TestTracker.php b/tests/search_api_test/src/Plugin/search_api/tracker/TestTracker.php
new file mode 100644
index 0000000..eb1cdf8
--- /dev/null
+++ b/tests/search_api_test/src/Plugin/search_api/tracker/TestTracker.php
@@ -0,0 +1,69 @@
+<?php
+
+namespace Drupal\search_api_test\Plugin\search_api\tracker;
+
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\search_api\Plugin\search_api\tracker\Basic;
+use Drupal\search_api_test\TestPluginTrait;
+
+/**
+ * Provides a tracker implementation which uses a FIFO-like processing order.
+ *
+ * @SearchApiTracker(
+ *   id = "search_api_test",
+ *   label = @Translation("Test tracker"),
+ * )
+ */
+class TestTracker extends Basic {
+
+  use TestPluginTrait;
+
+  /**
+   * {@inheritdoc}
+   */
+  public function defaultConfiguration() {
+    return array(
+      'foo' => 'test',
+      'dependencies' => array(),
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
+    return array(
+      'foo' => array(
+        '#type' => 'textfield',
+        '#title' => 'Foo',
+        '#default_value' => $this->configuration['foo'],
+      ),
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function trackItemsDeleted(array $ids = NULL) {
+    $this->logMethodCall(__FUNCTION__, func_get_args());
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function calculateDependencies() {
+    return $this->configuration['dependencies'];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function onDependencyRemoval(array $dependencies) {
+    $remove = $this->getReturnValue(__FUNCTION__, FALSE);
+    if ($remove) {
+      $this->configuration['dependencies'] = array();
+    }
+    return $remove;
+  }
+
+}
diff --git a/tests/search_api_test/src/PluginTestTrait.php b/tests/search_api_test/src/PluginTestTrait.php
new file mode 100644
index 0000000..94fd788
--- /dev/null
+++ b/tests/search_api_test/src/PluginTestTrait.php
@@ -0,0 +1,79 @@
+<?php
+
+namespace Drupal\search_api_test;
+
+/**
+ * Provides functionality for tests that deal with test plugins.
+ *
+ * @see \Drupal\search_api_test\TestPluginTrait
+ */
+trait PluginTestTrait {
+
+  /**
+   * Sets an exception to be thrown on calls to the given method.
+   *
+   * @param string $plugin_type
+   *   The "short" plugin type.
+   * @param string $method
+   *   The method on the plugin object which should throw an exception.
+   * @param bool $error
+   *   (optional) If TRUE, further calls to the method will throw exceptions,
+   *   otherwise they won't.
+   */
+  protected function setError($plugin_type, $method, $error = TRUE) {
+    $key = "search_api_test.$plugin_type.exception.$method";
+    \Drupal::state()->set($key, $error);
+  }
+
+  /**
+   * Sets the return value for a certain method on a test plugin.
+   *
+   * @param string $plugin_type
+   *   The "short" plugin type.
+   * @param string $method
+   *   The method name.
+   * @param mixed $value
+   *   The value that should be returned.
+   */
+  protected function setReturnValue($plugin_type, $method, $value) {
+    $key = "search_api_test.$plugin_type.return.$method";
+    \Drupal::state()->set($key, $value);
+  }
+
+  /**
+   * Retrieves the methods called on a given plugin.
+   *
+   * @param string $plugin_type
+   *   The "short" plugin type.
+   * @param bool $reset
+   *   (optional) If TRUE, also clear the list of called methods for that type.
+   *
+   * @return string[]
+   *   The methods called on the given plugin.
+   */
+  protected function getCalledMethods($plugin_type, $reset = TRUE) {
+    $key = "search_api_test.$plugin_type.methods_called";
+    $methods = \Drupal::state()->get($key, array());
+    if ($reset) {
+      \Drupal::state()->delete($key);
+    }
+    return $methods;
+  }
+
+  /**
+   * Retrieves the methods called on a given plugin.
+   *
+   * @param string $plugin_type
+   *   The "short" plugin type.
+   * @param string $method
+   *   The method name.
+   *
+   * @return array
+   *   The arguments of the last call to the method.
+   */
+  protected function getMethodArguments($plugin_type, $method) {
+    $key = "search_api_test.$plugin_type.method_arguments.$method";
+    return \Drupal::state()->get($key, array());
+  }
+
+}
diff --git a/tests/search_api_test/src/TestPluginTrait.php b/tests/search_api_test/src/TestPluginTrait.php
new file mode 100644
index 0000000..298e9b6
--- /dev/null
+++ b/tests/search_api_test/src/TestPluginTrait.php
@@ -0,0 +1,95 @@
+<?php
+
+namespace Drupal\search_api_test;
+
+use Drupal\search_api\SearchApiException;
+
+/**
+ * Provides common functionality for test plugins.
+ */
+trait TestPluginTrait {
+
+  /**
+   * This object's plugin type.
+   *
+   * @var string
+   */
+  protected $pluginType;
+
+  /**
+   * Throws an exception if set in the Drupal state for the given method.
+   *
+   * @param string $method
+   *   The method on this object from which this method was called.
+   *
+   * @throws \Drupal\search_api\SearchApiException
+   *   Thrown if state "search_api_test.TYPE.exception.$method" is TRUE.
+   */
+  protected function checkError($method) {
+    $type = $this->getPluginType();
+    $state = \Drupal::state();
+    if ($state->get("search_api_test.$type.exception.$method")) {
+      throw new SearchApiException($method);
+    }
+  }
+
+  /**
+   * Logs a method call to the site state.
+   *
+   * @param string $method
+   *   The name of the called method.
+   * @param array $args
+   *   (optional) The method arguments.
+   */
+  protected function logMethodCall($method, array $args = array()) {
+    $type = $this->getPluginType();
+    $state = \Drupal::state();
+
+    // Log call.
+    $key = "search_api_test.$type.methods_called";
+    $methods_called = $state->get($key, array());
+    $methods_called[] = $method;
+    $state->set($key, $methods_called);
+
+    // Log arguments.
+    $key = "search_api_test.$type.method_arguments.$method";
+    $state->set($key, $args);
+  }
+
+  /**
+   * Retrieves the value to return for a certain method.
+   *
+   * @param string $method
+   *   The name of the called method.
+   * @param mixed $default
+   *   (optional) The default return value.
+   *
+   * @return mixed
+   *   The value to return from the method.
+   */
+  protected function getReturnValue($method, $default = NULL) {
+    $type = $this->getPluginType();
+    $key = "search_api_test.$type.return.$method";
+    return \Drupal::state()->get($key, $default);
+  }
+
+  /**
+   * Returns the plugin type of this object.
+   *
+   * Equivalent to the last part of the namespace, i.e., without the module
+   * prefix.
+   *
+   * @return string
+   *   The "short" plugin type.
+   */
+  protected function getPluginType() {
+    if (!isset($this->pluginType)) {
+      $class = explode("\\", get_class($this));
+      array_pop($class);
+      $this->pluginType = array_pop($class);
+    }
+
+    return $this->pluginType;
+  }
+
+}
diff --git a/tests/search_api_test_backend/config/schema/search_api_test_backend.schema.yml b/tests/search_api_test_backend/config/schema/search_api_test_backend.schema.yml
deleted file mode 100644
index 131488c..0000000
--- a/tests/search_api_test_backend/config/schema/search_api_test_backend.schema.yml
+++ /dev/null
@@ -1,18 +0,0 @@
-plugin.plugin_configuration.search_api_backend.search_api_test_backend:
-  type: mapping
-  label: Test backend
-  mapping:
-    test:
-      type: string
-      label: Test configuration
-    dependencies:
-      type: config_dependencies
-      label: Dependencies
-
-plugin.plugin_configuration.search_api_tracker.search_api_test_backend:
-  type: mapping
-  label: Test tracker
-  mapping:
-    foo:
-      type: string
-      label: Foo
diff --git a/tests/search_api_test_backend/search_api_test_backend.info.yml b/tests/search_api_test_backend/search_api_test_backend.info.yml
deleted file mode 100644
index 56f3e13..0000000
--- a/tests/search_api_test_backend/search_api_test_backend.info.yml
+++ /dev/null
@@ -1,8 +0,0 @@
-name: 'Search API Backend Test'
-type: module
-description: 'Support module for Search API backend tests'
-package: Search
-dependencies:
-  - search_api:search_api
-core: 8.x
-hidden: true
diff --git a/tests/search_api_test_backend/src/Plugin/search_api/tracker/Test.php b/tests/search_api_test_backend/src/Plugin/search_api/tracker/Test.php
deleted file mode 100644
index 0c98242..0000000
--- a/tests/search_api_test_backend/src/Plugin/search_api/tracker/Test.php
+++ /dev/null
@@ -1,40 +0,0 @@
-<?php
-
-namespace Drupal\search_api_test_backend\Plugin\search_api\tracker;
-
-use Drupal\Core\Form\FormStateInterface;
-use Drupal\search_api\Plugin\search_api\tracker\Basic;
-
-/**
- * Provides a tracker implementation which uses a FIFO-like processing order.
- *
- * @SearchApiTracker(
- *   id = "search_api_test_backend",
- *   label = @Translation("Test tracker"),
- * )
- */
-class Test extends Basic {
-
-  /**
-   * {@inheritdoc}
-   */
-  public function defaultConfiguration() {
-    return array(
-      'foo' => 'test',
-    );
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
-    return array(
-      'foo' => array(
-        '#type' => 'textfield',
-        '#title' => 'Foo',
-        '#default_value' => $this->configuration['foo'],
-      ),
-    );
-  }
-
-}
diff --git a/tests/search_api_test_dependencies/config/schema/search_api_test_dependencies.schema.yml b/tests/search_api_test_dependencies/config/schema/search_api_test_dependencies.schema.yml
deleted file mode 100644
index 1944ecd..0000000
--- a/tests/search_api_test_dependencies/config/schema/search_api_test_dependencies.schema.yml
+++ /dev/null
@@ -1,8 +0,0 @@
-plugin.plugin_configuration.search_api_datasource.search_api_test_dependencies:
-  type: config_dependencies
-
-plugin.plugin_configuration.search_api_processor.search_api_test_dependencies:
-  type: config_dependencies
-
-plugin.plugin_configuration.search_api_tracker.search_api_test_dependencies:
-  type: config_dependencies
diff --git a/tests/search_api_test_dependencies/src/Plugin/search_api/tracker/TestTracker.php b/tests/search_api_test_dependencies/src/Plugin/search_api/tracker/TestTracker.php
deleted file mode 100644
index bd4084d..0000000
--- a/tests/search_api_test_dependencies/src/Plugin/search_api/tracker/TestTracker.php
+++ /dev/null
@@ -1,36 +0,0 @@
-<?php
-
-namespace Drupal\search_api_test_dependencies\Plugin\search_api\tracker;
-
-use Drupal\search_api\Plugin\search_api\tracker\Basic as BasicTracker;
-
-/**
- * Provides a tracker with dependencies, for the dependency removal tests.
- *
- * @SearchApiTracker(
- *   id = "search_api_test_dependencies",
- *   label = @Translation("Test tracker"),
- * )
- */
-class TestTracker extends BasicTracker {
-
-  /**
-   * {@inheritdoc}
-   */
-  public function calculateDependencies() {
-    return $this->configuration;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function onDependencyRemoval(array $dependencies) {
-    $remove = \Drupal::state()
-      ->get('search_api_test_dependencies.tracker.remove', FALSE);
-    if ($remove) {
-      $this->configuration = array();
-    }
-    return $remove;
-  }
-
-}
diff --git a/tests/search_api_test_hooks/search_api_test_hooks.module b/tests/search_api_test_hooks/search_api_test_hooks.module
index 19ac1b3..3e93f40 100644
--- a/tests/search_api_test_hooks/search_api_test_hooks.module
+++ b/tests/search_api_test_hooks/search_api_test_hooks.module
@@ -9,7 +9,7 @@
  * Implements hook_search_api_backend_info_alter.
  */
 function search_api_test_hooks_search_api_backend_info_alter(array &$backend_info) {
-  $backend_info['search_api_test_backend']['label'] = 'Slims return';
+  $backend_info['search_api_test']['label'] = 'Slims return';
 }
 
 /**
diff --git a/tests/src/Kernel/CliTest.php b/tests/src/Kernel/CliTest.php
index 3a36349..4fe1166 100644
--- a/tests/src/Kernel/CliTest.php
+++ b/tests/src/Kernel/CliTest.php
@@ -28,7 +28,7 @@ class CliTest extends KernelTestBase {
    */
   public static $modules = array(
     'search_api',
-    'search_api_test_backend',
+    'search_api_test',
     'user',
     'system',
     'entity_test',
@@ -48,7 +48,7 @@ public function setUp() {
       'name' => 'Test server',
       'id' => 'test',
       'status' => 1,
-      'backend' => 'search_api_test_backend',
+      'backend' => 'search_api_test',
     ));
     $this->server->save();
 
diff --git a/tests/src/Kernel/CustomDataTypesTest.php b/tests/src/Kernel/CustomDataTypesTest.php
index 5d93e7d..66d5563 100644
--- a/tests/src/Kernel/CustomDataTypesTest.php
+++ b/tests/src/Kernel/CustomDataTypesTest.php
@@ -39,7 +39,7 @@ class CustomDataTypesTest extends KernelTestBase {
     'search_api',
     'search_api_db',
     'search_api_test_db',
-    'search_api_test_backend',
+    'search_api_test',
     'user',
     'system',
     'entity_test',
@@ -102,7 +102,7 @@ public function setUp() {
       'name' => 'Server test ~',
       'id' => 'test',
       'status' => 1,
-      'backend' => 'search_api_test_backend',
+      'backend' => 'search_api_test',
     ));
     $this->server->save();
 
@@ -133,7 +133,7 @@ public function testCustomDataTypes() {
     $item->setFieldsExtracted(FALSE);
     $item->setFields(array());
     $field = $this->index->getField('name')
-      ->setType('search_api_test_data_type')
+      ->setType('search_api_test')
       ->setLabel("Test");
     $this->index->addField($field);
 
@@ -142,14 +142,14 @@ public function testCustomDataTypes() {
     $processed_type = $name_field->getType();
 
     $this->assertEquals($original_value, $processed_value, 'The processed value matches the original value');
-    $this->assertEquals('search_api_test_data_type', $processed_type, 'The processed type matches the new type.');
+    $this->assertEquals('search_api_test', $processed_type, 'The processed type matches the new type.');
     $this->assertEquals('Test', $name_field->getLabel(), 'The label is correctly set.');
 
     // Reset the fields on the item and change to the non-supported data type.
     $item->setFieldsExtracted(FALSE);
     $item->setFields(array());
     $field = $this->index->getField('name')
-      ->setType('search_api_unsupported_test_data_type');
+      ->setType('search_api_test_unsupported');
     $this->index->addField($field);
     $name_field = $item->getField('name');
 
@@ -163,7 +163,7 @@ public function testCustomDataTypes() {
     $item->setFieldsExtracted(FALSE);
     $item->setFields(array());
     $field = $this->index->getField('name')
-      ->setType('search_api_altering_test_data_type');
+      ->setType('search_api_test_altering');
     $this->index->addField($field);
     $name_field = $item->getField('name');
 
@@ -171,7 +171,7 @@ public function testCustomDataTypes() {
     $processed_type = $name_field->getType();
 
     $this->assertEquals(strlen($original_value), $processed_value, 'The processed value matches the altered original value');
-    $this->assertEquals('search_api_altering_test_data_type', $processed_type, 'The processed type matches the defined type.');
+    $this->assertEquals('search_api_test_altering', $processed_type, 'The processed type matches the defined type.');
   }
 
 }
diff --git a/tests/src/Kernel/DatasourceTaskTest.php b/tests/src/Kernel/DatasourceTaskTest.php
index 715fee2..f7d2073 100644
--- a/tests/src/Kernel/DatasourceTaskTest.php
+++ b/tests/src/Kernel/DatasourceTaskTest.php
@@ -21,7 +21,7 @@ class DatasourceTaskTest extends KernelTestBase {
    */
   public static $modules = array(
     'search_api',
-    'search_api_test_backend',
+    'search_api_test',
     'language',
     'user',
     'system',
@@ -105,7 +105,7 @@ public function setUp() {
       'name' => 'Test Server',
       'id' => 'test_server',
       'status' => 1,
-      'backend' => 'search_api_test_backend',
+      'backend' => 'search_api_test',
     ));
     $this->server->save();
 
diff --git a/tests/src/Kernel/DependencyRemovalTest.php b/tests/src/Kernel/DependencyRemovalTest.php
index b2b8f7b..a9ee37b 100644
--- a/tests/src/Kernel/DependencyRemovalTest.php
+++ b/tests/src/Kernel/DependencyRemovalTest.php
@@ -8,6 +8,7 @@
 use Drupal\search_api\Entity\Index;
 use Drupal\search_api\Entity\Server;
 use Drupal\search_api\Utility;
+use Drupal\search_api_test\PluginTestTrait;
 
 /**
  * Tests what happens when an index's dependencies are removed.
@@ -16,6 +17,8 @@
  */
 class DependencyRemovalTest extends KernelTestBase {
 
+  use PluginTestTrait;
+
   /**
    * A search index.
    *
@@ -38,8 +41,8 @@ class DependencyRemovalTest extends KernelTestBase {
     'system',
     'field',
     'search_api',
-    'search_api_test_backend',
-    'search_api_test_dependencies',
+    'search_api_test',
+    'search_api_test',
   );
 
   /**
@@ -50,6 +53,10 @@ public function setUp() {
 
     $this->installSchema('search_api', 'search_api_task');
 
+    \Drupal::configFactory()->getEditable('search_api.settings')
+      ->set('default_tracker', 'default')
+      ->save();
+
     // Create the index object, but don't save it yet since we want to change
     // its settings anyways in every test.
     $this->index = Index::create(array(
@@ -74,7 +81,7 @@ public function setUp() {
     $this->dependency = Server::create(array(
       'id' => 'dependency',
       'name' => 'Test dependency',
-      'backend' => 'search_api_test_backend',
+      'backend' => 'search_api_test',
     ));
     $this->dependency->save();
   }
@@ -141,7 +148,7 @@ public function testBackendDependency($remove_dependency) {
     $server = Server::create(array(
       'id' => 'test_server',
       'name' => 'Test server',
-      'backend' => 'search_api_test_backend',
+      'backend' => 'search_api_test',
       'backend_config' => array(
         'dependencies' => array(
           $dependency_key => array(
@@ -167,11 +174,9 @@ public function testBackendDependency($remove_dependency) {
     $index_dependencies = $this->index->getDependencies();
     $this->assertContains($server_dependency_name, $index_dependencies[$server_dependency_key], 'Server dependency correctly inserted');
 
-    // Set our magic state key to let the test plugin know whether the
-    // dependency should be removed or not. See
-    // \Drupal\search_api_test_backend\Plugin\search_api\backend\TestBackend::onDependencyRemoval().
-    $key = 'search_api_test_backend.return.onDependencyRemoval';
-    \Drupal::state()->set($key, $remove_dependency);
+    // Tell the backend plugin whether it should successfully remove the
+    // dependency.
+    $this->setReturnValue('backend', 'onDependencyRemoval', $remove_dependency);
 
     // Delete the backend's dependency.
     $this->dependency->delete();
@@ -224,8 +229,8 @@ public function testDatasourceDependency($remove_dependency) {
     $manager = \Drupal::getContainer()
       ->get('plugin.manager.search_api.datasource');
     $datasources['entity:user'] = $manager->createInstance('entity:user', array('index' => $this->index));
-    $datasources['search_api_test_dependencies'] = $manager->createInstance(
-      'search_api_test_dependencies',
+    $datasources['search_api_test'] = $manager->createInstance(
+      'search_api_test',
       array(
         $dependency_key => array($dependency_name),
         'index' => $this->index,
@@ -239,11 +244,9 @@ public function testDatasourceDependency($remove_dependency) {
     $dependencies = $this->index->getDependencies();
     $this->assertContains($dependency_name, $dependencies[$dependency_key], 'Datasource dependency correctly inserted');
 
-    // Set our magic state key to let the test plugin know whether the
-    // dependency should be removed or not. See
-    // \Drupal\search_api_test_dependencies\Plugin\search_api\datasource\TestDatasource::onDependencyRemoval().
-    $key = 'search_api_test_dependencies.datasource.remove';
-    \Drupal::state()->set($key, $remove_dependency);
+    // Tell the datasource plugin whether it should successfully remove the
+    // dependency.
+    $this->setReturnValue('datasource', 'onDependencyRemoval', $remove_dependency);
 
     // Delete the datasource's dependency.
     $this->dependency->delete();
@@ -261,11 +264,11 @@ public function testDatasourceDependency($remove_dependency) {
     // not, make sure the right action was taken.
     $datasources = $this->index->getDatasources();
     if ($remove_dependency) {
-      $this->assertArrayHasKey('search_api_test_dependencies', $datasources, 'Datasource not removed');
-      $this->assertEmpty($datasources['search_api_test_dependencies']->getConfiguration(), 'Datasource settings adapted');
+      $this->assertArrayHasKey('search_api_test', $datasources, 'Datasource not removed');
+      $this->assertEmpty($datasources['search_api_test']->getConfiguration(), 'Datasource settings adapted');
     }
     else {
-      $this->assertArrayNotHasKey('search_api_test_dependencies', $datasources, 'Datasource removed');
+      $this->assertArrayNotHasKey('search_api_test', $datasources, 'Datasource removed');
     }
   }
 
@@ -278,10 +281,10 @@ public function testSingleDatasourceDependency() {
     // server.
     $dependency_key = $this->dependency->getConfigDependencyKey();
     $dependency_name = $this->dependency->getConfigDependencyName();
-    $datasources['search_api_test_dependencies'] = \Drupal::getContainer()
+    $datasources['search_api_test'] = \Drupal::getContainer()
       ->get('plugin.manager.search_api.datasource')
       ->createInstance(
-        'search_api_test_dependencies',
+        'search_api_test',
         array($dependency_key => array($dependency_name))
       );
     $this->index->setDatasources($datasources);
@@ -321,10 +324,11 @@ public function testProcessorDependency($remove_dependency) {
     $dependency_key = $this->dependency->getConfigDependencyKey();
     $dependency_name = $this->dependency->getConfigDependencyName();
 
+    /** @var \Drupal\search_api\Processor\ProcessorInterface $processor */
     $processor = \Drupal::getContainer()
       ->get('plugin.manager.search_api.processor')
       ->createInstance(
-        'search_api_test_dependencies',
+        'search_api_test',
         array($dependency_key => array($dependency_name))
       );
     $this->index->addProcessor($processor);
@@ -334,11 +338,9 @@ public function testProcessorDependency($remove_dependency) {
     $dependencies = $this->index->getDependencies();
     $this->assertContains($dependency_name, $dependencies[$dependency_key], 'Processor dependency correctly inserted');
 
-    // Set our magic state key to let the test plugin know whether the
-    // dependency should be removed or not. See
-    // \Drupal\search_api_test_dependencies\Plugin\search_api\processor\TestProcessor::onDependencyRemoval().
-    $key = 'search_api_test_dependencies.processor.remove';
-    \Drupal::state()->set($key, $remove_dependency);
+    // Tell the processor plugin whether it should successfully remove the
+    // dependency.
+    $this->setReturnValue('processor', 'onDependencyRemoval', $remove_dependency);
 
     // Delete the processor's dependency.
     $this->dependency->delete();
@@ -356,11 +358,11 @@ public function testProcessorDependency($remove_dependency) {
     // not, make sure the right action was taken.
     $processors = $this->index->getProcessors();
     if ($remove_dependency) {
-      $this->assertArrayHasKey('search_api_test_dependencies', $processors, 'Processor not removed');
-      $this->assertEmpty($processors['search_api_test_dependencies']->getConfiguration(), 'Processor settings adapted');
+      $this->assertArrayHasKey('search_api_test', $processors, 'Processor not removed');
+      $this->assertEmpty($processors['search_api_test']->getConfiguration(), 'Processor settings adapted');
     }
     else {
-      $this->assertArrayNotHasKey('search_api_test_dependencies', $processors, 'Processor removed');
+      $this->assertArrayNotHasKey('search_api_test', $processors, 'Processor removed');
     }
   }
 
@@ -380,11 +382,14 @@ public function testTrackerDependency($remove_dependency) {
     $dependency_key = $this->dependency->getConfigDependencyKey();
     $dependency_name = $this->dependency->getConfigDependencyName();
 
+    /** @var \Drupal\search_api\Tracker\TrackerInterface $tracker */
     $tracker = \Drupal::getContainer()
       ->get('plugin.manager.search_api.tracker')
-      ->createInstance('search_api_test_dependencies', array(
-        $dependency_key => array(
-          $dependency_name,
+      ->createInstance('search_api_test', array(
+        'dependencies' => array(
+          $dependency_key => array(
+            $dependency_name,
+          ),
         ),
       ));
     $this->index->setTracker($tracker);
@@ -394,18 +399,9 @@ public function testTrackerDependency($remove_dependency) {
     $dependencies = $this->index->getDependencies();
     $this->assertContains($dependency_name, $dependencies[$dependency_key], 'Tracker dependency correctly inserted');
 
-    // Set our magic state key to let the test plugin know whether the
-    // dependency should be removed or not. See
-    // \Drupal\search_api_test_dependencies\Plugin\search_api\tracker\TestTracker::onDependencyRemoval().
-    $key = 'search_api_test_dependencies.tracker.remove';
-    \Drupal::state()->set($key, $remove_dependency);
-    // If the index resets the tracker, it needs to know the ID of the default
-    // tracker.
-    if (!$remove_dependency) {
-      \Drupal::configFactory()->getEditable('search_api.settings')
-        ->set('default_tracker', 'default')
-        ->save();
-    }
+    // Tell the datasource plugin whether it should successfully remove the
+    // dependency.
+    $this->setReturnValue('tracker', 'onDependencyRemoval', $remove_dependency);
 
     // Delete the tracker's dependency.
     $this->dependency->delete();
@@ -425,8 +421,8 @@ public function testTrackerDependency($remove_dependency) {
     $tracker_id = $tracker_instance->getPluginId();
     $tracker_config = $tracker_instance->getConfiguration();
     if ($remove_dependency) {
-      $this->assertEquals('search_api_test_dependencies', $tracker_id, 'Tracker not reset');
-      $this->assertEmpty($tracker_config, 'Tracker settings adapted');
+      $this->assertEquals('search_api_test', $tracker_id, 'Tracker not reset');
+      $this->assertEmpty($tracker_config['dependencies'], 'Tracker settings adapted');
     }
     else {
       $this->assertEquals('default', $tracker_id, 'Tracker was reset');
@@ -439,29 +435,31 @@ public function testTrackerDependency($remove_dependency) {
    */
   public function testModuleDependency() {
     // Test with all types of plugins at once.
-    $datasources['search_api_test_dependencies'] = \Drupal::getContainer()
+    $datasources['search_api_test'] = \Drupal::getContainer()
       ->get('plugin.manager.search_api.datasource')
-      ->createInstance('search_api_test_dependencies', array('index' => $this->index));
+      ->createInstance('search_api_test', array('index' => $this->index));
     $datasources['entity:user'] = \Drupal::getContainer()
       ->get('plugin.manager.search_api.datasource')
       ->createInstance('entity:user', array('index' => $this->index));
     $this->index->setDatasources($datasources);
 
+    /** @var \Drupal\search_api\Processor\ProcessorInterface $processor */
     $processor = \Drupal::getContainer()
       ->get('plugin.manager.search_api.processor')
-      ->createInstance('search_api_test_dependencies');
+      ->createInstance('search_api_test');
     $this->index->addProcessor($processor);
 
+    /** @var \Drupal\search_api\Tracker\TrackerInterface $tracker */
     $tracker = \Drupal::getContainer()
       ->get('plugin.manager.search_api.tracker')
-      ->createInstance('search_api_test_dependencies');
+      ->createInstance('search_api_test');
     $this->index->setTracker($tracker);
 
     $this->index->save();
 
     // Check the dependencies were calculated correctly.
     $dependencies = $this->index->getDependencies();
-    $this->assertContains('search_api_test_dependencies', $dependencies['module'], 'Module dependency correctly inserted');
+    $this->assertContains('search_api_test', $dependencies['module'], 'Module dependency correctly inserted');
 
     // When the index resets the tracker, it needs to know the ID of the default
     // tracker.
@@ -473,7 +471,7 @@ public function testModuleDependency() {
     // reaction, just removes it from the list of modules (e.g., to avoid
     // calling of a hook). Therefore, we have to trigger that behavior
     // ourselves.
-    \Drupal::getContainer()->get('config.manager')->uninstall('module', 'search_api_test_dependencies');
+    \Drupal::getContainer()->get('config.manager')->uninstall('module', 'search_api_test');
 
     // Reload the index and check it's still there.
     $this->reloadIndex();
@@ -482,11 +480,11 @@ public function testModuleDependency() {
     // Make sure the dependency has been removed.
     $dependencies = $this->index->getDependencies();
     $dependencies += array('module' => array());
-    $this->assertNotContains('search_api_test_dependencies', $dependencies['module'], 'Module dependency removed from index');
+    $this->assertNotContains('search_api_test', $dependencies['module'], 'Module dependency removed from index');
 
     // Make sure all the plugins have been removed.
-    $this->assertNotContains('search_api_test_dependencies', $this->index->getDatasources(), 'Datasource was removed');
-    $this->assertArrayNotHasKey('search_api_test_dependencies', $this->index->getProcessors(), 'Processor was removed');
+    $this->assertNotContains('search_api_test', $this->index->getDatasources(), 'Datasource was removed');
+    $this->assertArrayNotHasKey('search_api_test', $this->index->getProcessors(), 'Processor was removed');
     $this->assertEquals('default', $this->index->getTrackerId(), 'Tracker was reset');
   }
 
diff --git a/tests/src/Kernel/EntityStringIdTest.php b/tests/src/Kernel/EntityStringIdTest.php
index de60f5a..58e8985 100644
--- a/tests/src/Kernel/EntityStringIdTest.php
+++ b/tests/src/Kernel/EntityStringIdTest.php
@@ -43,7 +43,7 @@ class EntityStringIdTest extends KernelTestBase {
    */
   public static $modules = array(
     'search_api',
-    'search_api_test_backend',
+    'search_api_test',
     'language',
     'user',
     'system',
@@ -87,7 +87,7 @@ public function setUp() {
       'name' => 'Test Server',
       'id' => 'test_server',
       'status' => 1,
-      'backend' => 'search_api_test_backend',
+      'backend' => 'search_api_test',
     ));
     $this->server->save();
 
diff --git a/tests/src/Kernel/IndexImportTest.php b/tests/src/Kernel/IndexImportTest.php
index a361412..9ddb2a8 100644
--- a/tests/src/Kernel/IndexImportTest.php
+++ b/tests/src/Kernel/IndexImportTest.php
@@ -28,7 +28,7 @@ class IndexImportTest extends KernelTestBase {
     'search_api',
     'search_api_db',
     'search_api_test_db',
-    'search_api_test_backend',
+    'search_api_test',
     'user',
     'system',
     'entity_test',
diff --git a/tests/src/Kernel/IndexLoadItemsTest.php b/tests/src/Kernel/IndexLoadItemsTest.php
new file mode 100644
index 0000000..c259a1b
--- /dev/null
+++ b/tests/src/Kernel/IndexLoadItemsTest.php
@@ -0,0 +1,105 @@
+<?php
+
+namespace Drupal\Tests\search_api\Kernel;
+
+use Drupal\KernelTests\KernelTestBase;
+use Drupal\search_api\Entity\Index;
+use Drupal\search_api\Entity\Server;
+use Drupal\search_api_test\PluginTestTrait;
+
+/**
+ * Tests whether loading items works correctly.
+ *
+ * @group search_api
+ */
+class IndexLoadItemsTest extends KernelTestBase {
+
+  use PluginTestTrait;
+
+  /**
+   * The test index object.
+   *
+   * @var \Drupal\search_api\IndexInterface
+   */
+  protected $index;
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = array(
+    'search_api',
+    'search_api_test',
+    'user',
+    );
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    $this->installSchema('search_api', array('search_api_task'));
+
+    $server = Server::create(array(
+      'id' => 'test',
+      'backend' => 'search_api_test',
+    ));
+    $this->index = Index::create(array(
+      'tracker_settings' => array(
+        'search_api_test' => array(
+          'plugin_id' => 'search_api_test',
+          'settings' => array(),
+        ),
+      ),
+      'datasource_settings' => array(
+        'search_api_test' => array(
+          'plugin_id' => 'search_api_test',
+          'settings' => array(),
+        ),
+      ),
+    ));
+    $this->index->setServer($server);
+  }
+
+  /**
+   * Verifies that missing items are correctly detected and removed.
+   */
+  public function testMissingItems() {
+    $state = \Drupal::state();
+
+    $item_ids = array(
+      'search_api_test/1',
+      'search_api_test/2',
+    );
+    $items = $this->index->loadItemsMultiple($item_ids);
+    $this->assertEquals(array(), $items, 'No items loaded from test datasource.');
+    $methods = $this->getCalledMethods('tracker');
+    $this->assertContains('trackItemsDeleted', $methods, 'Unknown items deleted from tracker.');
+    $args = $this->getMethodArguments('tracker', 'trackItemsDeleted');
+    $this->assertEquals(array($item_ids), $args, 'Correct items deleted from tracker.');
+    $methods = $this->getCalledMethods('backend');
+    $this->assertContains('deleteItems', $methods, 'Unknown items deleted from server.');
+
+    // If an error occurs while retrieving the datasource (which will happen for
+    // "unknown/1"), the items should not be deleted from tracking and the
+    // server.
+    $expected_deletions = $item_ids;
+    $item_ids = array(
+      'search_api_test/1',
+      'search_api_test/2',
+      'search_api_test/3',
+      'unknown/1',
+    );
+    $this->setReturnValue('datasource', 'loadMultiple', array('3' => ''));
+    $items = $this->index->loadItemsMultiple($item_ids);
+    $expected_items = array('search_api_test/3' => '');
+    $this->assertEquals($expected_items, $items, 'Expected items loaded from test datasource.');
+    $methods = $this->getCalledMethods('tracker');
+    $this->assertContains('trackItemsDeleted', $methods, 'Unknown items deleted from tracker.');
+    $args = $this->getMethodArguments('tracker', 'trackItemsDeleted');
+    $this->assertEquals(array($expected_deletions), $args, 'Correct items deleted from tracker.');
+    $methods = $this->getCalledMethods('backend');
+    $this->assertContains('deleteItems', $methods, 'Unknown items deleted from server.');
+  }
+
+}
diff --git a/tests/src/Kernel/LanguageKernelTest.php b/tests/src/Kernel/LanguageKernelTest.php
index 494a395..8f7eac7 100644
--- a/tests/src/Kernel/LanguageKernelTest.php
+++ b/tests/src/Kernel/LanguageKernelTest.php
@@ -45,7 +45,7 @@ class LanguageKernelTest extends KernelTestBase {
    */
   public static $modules = array(
     'search_api',
-    'search_api_test_backend',
+    'search_api_test',
     'language',
     'user',
     'system',
@@ -103,7 +103,7 @@ public function setUp() {
       'name' => 'Test Server',
       'id' => 'test_server',
       'status' => 1,
-      'backend' => 'search_api_test_backend',
+      'backend' => 'search_api_test',
     ));
     $this->server->save();
 
diff --git a/tests/src/Kernel/Processor/ProcessorTestBase.php b/tests/src/Kernel/Processor/ProcessorTestBase.php
index 6ee4d63..6308eb3 100644
--- a/tests/src/Kernel/Processor/ProcessorTestBase.php
+++ b/tests/src/Kernel/Processor/ProcessorTestBase.php
@@ -23,7 +23,7 @@
     'field',
     'search_api',
     'search_api_db',
-    'search_api_test_backend',
+    'search_api_test',
     'comment',
     'text',
     'action',
diff --git a/tests/src/Kernel/Processor/RenderedItemTest.php b/tests/src/Kernel/Processor/RenderedItemTest.php
index 69f50b1..91ba984 100644
--- a/tests/src/Kernel/Processor/RenderedItemTest.php
+++ b/tests/src/Kernel/Processor/RenderedItemTest.php
@@ -33,7 +33,7 @@ class RenderedItemTest extends ProcessorTestBase {
     'node',
     'search_api',
     'search_api_db',
-    'search_api_test_backend',
+    'search_api_test',
     'comment',
     'system',
     'filter',
diff --git a/tests/src/Kernel/ServerStorageTest.php b/tests/src/Kernel/ServerStorageTest.php
index 1c56b3b..fac7011 100644
--- a/tests/src/Kernel/ServerStorageTest.php
+++ b/tests/src/Kernel/ServerStorageTest.php
@@ -20,7 +20,7 @@ class ServerStorageTest extends KernelTestBase {
    */
   public static $modules = array(
     'search_api',
-    'search_api_test_backend',
+    'search_api_test',
     'user',
     'system',
   );
@@ -65,14 +65,14 @@ public function serverCreate() {
     $server_data = array(
       'id' => 'test_server',
       'name' => 'Test server',
-      'backend' => 'search_api_test_backend',
+      'backend' => 'search_api_test',
     );
     $server = $this->storage->create($server_data);
 
     $this->assertTrue($server instanceof ServerInterface, 'The newly created entity is a Search API Server.');
     $server->save();
 
-    $key = 'search_api_test_backend.methods_called.' . $server->id();
+    $key = 'search_api_test.methods_called.' . $server->id();
     $methods_called = \Drupal::state()->get($key, array());
     $this->assertNotContains('preUpdate', $methods_called, 'Backend::preUpdate() not called for initial save.');
     $this->assertNotContains('postUpdate', $methods_called, 'Backend::postUpdate() not called for initial save.');
@@ -105,7 +105,7 @@ public function serverUpdate(ServerInterface $server) {
     $server->set('name', $server->label() . ' - edited');
     $server->save();
 
-    $key = 'search_api_test_backend.methods_called.' . $server->id();
+    $key = 'search_api_test.methods_called.' . $server->id();
     $methods_called = \Drupal::state()->get($key, array());
     $this->assertContains('preUpdate', $methods_called, 'Backend::preUpdate() called for update.');
     $this->assertContains('postUpdate', $methods_called, 'Backend::postUpdate() called for update.');
diff --git a/tests/src/Kernel/ServerTaskTest.php b/tests/src/Kernel/ServerTaskTest.php
index 998a55b..5cc78a3 100644
--- a/tests/src/Kernel/ServerTaskTest.php
+++ b/tests/src/Kernel/ServerTaskTest.php
@@ -7,6 +7,7 @@
 use Drupal\search_api\Entity\Server;
 use Drupal\search_api\IndexInterface;
 use Drupal\search_api\SearchApiException;
+use Drupal\search_api_test\PluginTestTrait;
 
 /**
  * Tests whether the server task system works correctly.
@@ -15,6 +16,8 @@
  */
 class ServerTaskTest extends KernelTestBase {
 
+  use PluginTestTrait;
+
   /**
    * The test server.
    *
@@ -44,17 +47,10 @@ class ServerTaskTest extends KernelTestBase {
   public static $modules = array(
     'user',
     'search_api',
-    'search_api_test_backend',
+    'search_api_test',
   );
 
   /**
-   * The state service.
-   *
-   * @var \Drupal\Core\State\StateInterface
-   */
-  protected $state;
-
-  /**
    * The task manager to use for the tests.
    *
    * @var \Drupal\search_api\Task\TaskManagerInterface
@@ -74,8 +70,6 @@ class ServerTaskTest extends KernelTestBase {
   public function setUp() {
     parent::setUp();
 
-    $this->state = $this->container->get('state');
-
     $this->installEntitySchema('user');
     $this->installSchema('search_api', array('search_api_item', 'search_api_task'));
 
@@ -90,7 +84,7 @@ public function setUp() {
       'name' => 'Test Server',
       'id' => 'test_server',
       'status' => 1,
-      'backend' => 'search_api_test_backend',
+      'backend' => 'search_api_test',
     ));
     $this->server->save();
 
@@ -120,7 +114,7 @@ public function setUp() {
     $this->serverTaskManager = $this->container->get('search_api.server_task_manager');
 
     // Reset the list of called backend methods.
-    $this->getCalledBackendMethods();
+    $this->getCalledMethods('backend');
   }
 
   /**
@@ -134,12 +128,13 @@ public function testAddIndex() {
 
     // Set exception for addIndex() and reset the list of successful backend
     // method calls.
-    $this->state->set('search_api_test_backend.exception.addIndex', TRUE);
-    $this->getCalledBackendMethods();
+    $this->setError('backend', 'addIndex');
+    $this->setError('backend', 'addIndex');
+    $this->getCalledMethods('backend');
 
     // Try to add the index.
     $this->server->addIndex($this->index);
-    $this->assertEquals(array(), $this->getCalledBackendMethods(), 'addIndex correctly threw an exception.');
+    $this->assertEquals(array(), $this->getCalledMethods('backend'), 'addIndex correctly threw an exception.');
     $tasks = $this->getServerTasks();
     if (count($tasks) == 1) {
       $task_created = $tasks[0]->type === 'addIndex';
@@ -151,7 +146,7 @@ public function testAddIndex() {
 
     // Check whether other task-system-integrated methods now fail, too.
     $this->server->updateIndex($this->index);
-    $this->assertEquals(array(), $this->getCalledBackendMethods(), 'updateIndex was not executed.');
+    $this->assertEquals(array(), $this->getCalledMethods('backend'), 'updateIndex was not executed.');
     $tasks = $this->getServerTasks();
     if (count($tasks) == 2) {
       $this->assertTrue(TRUE, "Second task ('updateIndex') was added.");
@@ -164,10 +159,10 @@ public function testAddIndex() {
 
     // Let addIndex() succeed again, then trigger the task execution with a cron
     // run.
-    $this->state->set('search_api_test_backend.exception.addIndex', FALSE);
+    $this->setError('backend', 'addIndex', FALSE);
     search_api_cron();
     $this->assertEquals(array(), $this->getServerTasks(), 'Server tasks were correctly executed.');
-    $this->assertEquals(array('addIndex', 'updateIndex'), $this->getCalledBackendMethods(), 'Right methods were called during task execution.');
+    $this->assertEquals(array('addIndex', 'updateIndex'), $this->getCalledMethods('backend'), 'Right methods were called during task execution.');
   }
 
   /**
@@ -175,11 +170,11 @@ public function testAddIndex() {
    */
   public function testUpdateIndex() {
     // Set exception for updateIndex().
-    $this->state->set('search_api_test_backend.exception.updateIndex', TRUE);
+    $this->setError('backend', 'updateIndex');
 
     // Try to update the index.
     $this->server->updateIndex($this->index);
-    $this->assertEquals(array(), $this->getCalledBackendMethods(), 'updateIndex correctly threw an exception.');
+    $this->assertEquals(array(), $this->getCalledMethods('backend'), 'updateIndex correctly threw an exception.');
     $tasks = $this->getServerTasks();
     if (count($tasks) == 1) {
       $task_created = $tasks[0]->type === 'updateIndex';
@@ -191,7 +186,7 @@ public function testUpdateIndex() {
 
     // Check whether other task-system-integrated methods now fail, too.
     $this->server->deleteAllIndexItems($this->index);
-    $this->assertEquals(array(), $this->getCalledBackendMethods(), 'deleteAllIndexItems was not executed.');
+    $this->assertEquals(array(), $this->getCalledMethods('backend'), 'deleteAllIndexItems was not executed.');
     $tasks = $this->getServerTasks();
     if (count($tasks) == 2) {
       $this->assertTrue(TRUE, "Second task ('deleteAllIndexItems') was added.");
@@ -204,7 +199,7 @@ public function testUpdateIndex() {
 
     // Let updateIndex() succeed again, then trigger the task execution with a
     // call to indexItems().
-    $this->state->set('search_api_test_backend.exception.updateIndex', FALSE);
+    $this->setError('backend', 'updateIndex', FALSE);
     $this->server->indexItems($this->index, array());
 
     $expected_methods = array(
@@ -213,7 +208,7 @@ public function testUpdateIndex() {
       'indexItems',
     );
     $this->assertEquals(array(), $this->getServerTasks(), 'Server tasks were correctly executed.');
-    $this->assertEquals($expected_methods, $this->getCalledBackendMethods(), 'Right methods were called during task execution.');
+    $this->assertEquals($expected_methods, $this->getCalledMethods('backend'), 'Right methods were called during task execution.');
   }
 
   /**
@@ -221,14 +216,14 @@ public function testUpdateIndex() {
    */
   public function testRemoveIndex() {
     // Set exception for updateIndex() and removeIndex().
-    $this->state->set('search_api_test_backend.exception.updateIndex', TRUE);
-    $this->state->set('search_api_test_backend.exception.removeIndex', TRUE);
+    $this->setError('backend', 'updateIndex');
+    $this->setError('backend', 'removeIndex');
 
     // First try to update the index and fail. Then try to remove it and check
     // that the tasks were set correctly.
     $this->server->updateIndex($this->index);
     $this->server->removeIndex($this->index);
-    $this->assertEquals(array(), $this->getCalledBackendMethods(), 'updateIndex and removeIndex correctly threw exceptions.');
+    $this->assertEquals(array(), $this->getCalledMethods('backend'), 'updateIndex and removeIndex correctly threw exceptions.');
     $tasks = $this->getServerTasks();
     if (count($tasks) == 1) {
       $task_created = $tasks[0]->type === 'removeIndex';
@@ -248,16 +243,16 @@ public function testRemoveIndex() {
       $expected_message = "Could not index items on index '$label' because pending server tasks could not be executed.";
       $this->assertEquals($expected_message, $e->getMessage(), 'Pending server tasks prevented indexing of items.');
     }
-    $this->assertEquals(array(), $this->getCalledBackendMethods(), 'indexItems was not executed.');
+    $this->assertEquals(array(), $this->getCalledMethods('backend'), 'indexItems was not executed.');
     $tasks = $this->getServerTasks();
     $this->assertEquals(1, count($tasks), 'No task added for indexItems.');
 
     // Let removeIndex() succeed again, then trigger the task execution with a
     // cron run.
-    $this->state->set("search_api_test_backend.exception.removeIndex", FALSE);
+    $this->setError('backend', 'removeIndex', FALSE);
     search_api_cron();
     $this->assertEquals(array(), $this->getServerTasks(), 'Server tasks were correctly executed.');
-    $this->assertEquals(array('removeIndex'), $this->getCalledBackendMethods(), 'Right methods were called during task execution.');
+    $this->assertEquals(array('removeIndex'), $this->getCalledMethods('backend'), 'Right methods were called during task execution.');
   }
 
   /**
@@ -265,11 +260,11 @@ public function testRemoveIndex() {
    */
   public function testDeleteItems() {
     // Set exception for deleteItems().
-    $this->state->set('search_api_test_backend.exception.deleteItems', TRUE);
+    $this->setError('backend', 'deleteItems');
 
     // Try to update the index.
     $this->server->deleteItems($this->index, array());
-    $this->assertEquals(array(), $this->getCalledBackendMethods(), 'deleteItems correctly threw an exception.');
+    $this->assertEquals(array(), $this->getCalledMethods('backend'), 'deleteItems correctly threw an exception.');
     $tasks = $this->getServerTasks();
     if (count($tasks) == 1) {
       $task_created = $tasks[0]->type === 'deleteItems';
@@ -281,7 +276,7 @@ public function testDeleteItems() {
 
     // Check whether other task-system-integrated methods now fail, too.
     $this->server->updateIndex($this->index);
-    $this->assertEquals(array(), $this->getCalledBackendMethods(), 'updateIndex was not executed.');
+    $this->assertEquals(array(), $this->getCalledMethods('backend'), 'updateIndex was not executed.');
     $tasks = $this->getServerTasks();
     if (count($tasks) == 2) {
       $this->assertTrue(TRUE, "Second task ('updateIndex') was added.");
@@ -294,10 +289,10 @@ public function testDeleteItems() {
 
     // Let deleteItems() succeed again, then trigger the task execution
     // with a cron run.
-    $this->state->set('search_api_test_backend.exception.deleteItems', FALSE);
+    $this->setError('backend', 'deleteItems', FALSE);
     search_api_cron();
     $this->assertEquals(array(), $this->getServerTasks(), 'Server tasks were correctly executed.');
-    $this->assertEquals(array('deleteItems', 'updateIndex'), $this->getCalledBackendMethods(), 'Right methods were called during task execution.');
+    $this->assertEquals(array('deleteItems', 'updateIndex'), $this->getCalledMethods('backend'), 'Right methods were called during task execution.');
   }
 
   /**
@@ -305,11 +300,11 @@ public function testDeleteItems() {
    */
   public function testDeleteAllIndexItems() {
     // Set exception for deleteAllIndexItems().
-    $this->state->set('search_api_test_backend.exception.deleteAllIndexItems', TRUE);
+    $this->setError('backend', 'deleteAllIndexItems');
 
     // Try to update the index.
     $this->server->deleteAllIndexItems($this->index);
-    $this->assertEquals(array(), $this->getCalledBackendMethods(), 'deleteAllIndexItems correctly threw an exception.');
+    $this->assertEquals(array(), $this->getCalledMethods('backend'), 'deleteAllIndexItems correctly threw an exception.');
     $tasks = $this->getServerTasks();
     if (count($tasks) == 1) {
       $task_created = $tasks[0]->type === 'deleteAllIndexItems';
@@ -321,7 +316,7 @@ public function testDeleteAllIndexItems() {
 
     // Check whether other task-system-integrated methods now fail, too.
     $this->server->updateIndex($this->index);
-    $this->assertEquals(array(), $this->getCalledBackendMethods(), 'updateIndex was not executed.');
+    $this->assertEquals(array(), $this->getCalledMethods('backend'), 'updateIndex was not executed.');
     $tasks = $this->getServerTasks();
     if (count($tasks) == 2) {
       $this->assertTrue(TRUE, "Second task ('updateIndex') was added.");
@@ -334,7 +329,7 @@ public function testDeleteAllIndexItems() {
 
     // Let deleteAllIndexItems() succeed again, then trigger the task execution
     // with a call to indexItems().
-    $this->state->set('search_api_test_backend.exception.deleteAllIndexItems', FALSE);
+    $this->setError('backend', 'deleteAllIndexItems', FALSE);
     $this->server->indexItems($this->index, array());
 
     $expected_methods = array(
@@ -343,7 +338,7 @@ public function testDeleteAllIndexItems() {
       'indexItems',
     );
     $this->assertEquals(array(), $this->getServerTasks(), 'Server tasks were correctly executed.');
-    $this->assertEquals($expected_methods, $this->getCalledBackendMethods(), 'Right methods were called during task execution.');
+    $this->assertEquals($expected_methods, $this->getCalledMethods('backend'), 'Right methods were called during task execution.');
   }
 
   /**
@@ -358,7 +353,7 @@ public function testTaskCountLimit() {
     // Verify that a new operation cannot be executed.
     $this->server->updateIndex($this->index);
 
-    $methods = $this->getCalledBackendMethods();
+    $methods = $this->getCalledMethods('backend');
     $this->assertCount(100, $methods, '100 pending tasks were executed upon new operation.');
     $filter = function ($method) { return $method != 'deleteItems'; };
     $this->assertEmpty(array_filter($methods, $filter), 'The new operation was not executed.');
@@ -374,7 +369,7 @@ public function testAutomaticTaskRemoval() {
       'name' => 'Test Server 2',
       'id' => 'test_server_2',
       'status' => 1,
-      'backend' => 'search_api_test_backend',
+      'backend' => 'search_api_test',
     ));
     $server2->save();
     $this->taskManager->addTask('removeIndex', $server2, $this->index);
@@ -385,19 +380,19 @@ public function testAutomaticTaskRemoval() {
     $index2 = Index::create($index_values);
     $index2->save();
     // Reset the called backend methods.
-    $this->getCalledBackendMethods();
+    $this->getCalledMethods('backend');
 
     // Verify that adding an index ignores all tasks related to that index.
     $this->addTasks($index2);
     $this->server->addIndex($this->index);
-    $this->assertEquals(array('addIndex', 'addIndex'), $this->getCalledBackendMethods(), 'Re-adding an index ignored all its tasks.');
+    $this->assertEquals(array('addIndex', 'addIndex'), $this->getCalledMethods('backend'), 'Re-adding an index ignored all its tasks.');
     $this->assertEquals(0, $this->serverTaskManager->getCount($this->server), 'No pending tasks for server.');
     $this->assertEquals(1, $this->serverTaskManager->getCount(), 'The tasks of other servers were not touched.');
 
     // Verify that removing an index ignores all tasks related to that index.
     $this->addTasks($index2);
     $this->server->removeIndex($this->index);
-    $this->assertEquals(array('addIndex', 'removeIndex'), $this->getCalledBackendMethods(), 'Removing an index ignored all its tasks.');
+    $this->assertEquals(array('addIndex', 'removeIndex'), $this->getCalledMethods('backend'), 'Removing an index ignored all its tasks.');
     $this->assertEquals(0, $this->serverTaskManager->getCount($this->server), 'No pending tasks for server.');
     $this->assertEquals(1, $this->serverTaskManager->getCount(), 'The tasks of other servers were not touched.');
 
@@ -405,7 +400,7 @@ public function testAutomaticTaskRemoval() {
     // tasks related to that index.
     $this->addTasks($index2);
     $this->server->deleteAllIndexItems($this->index);
-    $this->assertEquals(array('addIndex', 'removeIndex', 'addIndex', 'updateIndex', 'deleteAllIndexItems'), $this->getCalledBackendMethods(), 'Deleting all items of an index ignored all its deletion tasks.');
+    $this->assertEquals(array('addIndex', 'removeIndex', 'addIndex', 'updateIndex', 'deleteAllIndexItems'), $this->getCalledMethods('backend'), 'Deleting all items of an index ignored all its deletion tasks.');
     $this->assertEquals(0, $this->serverTaskManager->getCount($this->server), 'No pending tasks for server.');
     $this->assertEquals(1, $this->serverTaskManager->getCount(), 'The tasks of other servers were not touched.');
 
@@ -414,17 +409,17 @@ public function testAutomaticTaskRemoval() {
     $this->addTasks($index2);
     $this->server->deleteAllItems();
     // deleteAllIndexItems() is called twice – once for each index.
-    $this->assertEquals(array('deleteAllIndexItems', 'deleteAllIndexItems'), $this->getCalledBackendMethods(), "Deleting all items from a server didn't execute any tasks.");
+    $this->assertEquals(array('deleteAllIndexItems', 'deleteAllIndexItems'), $this->getCalledMethods('backend'), "Deleting all items from a server didn't execute any tasks.");
     $this->assertEquals(4, $this->serverTaskManager->getCount($this->server), 'Deleting all items from a server removed all its item deletion tasks.');
     $this->assertEquals(5, $this->serverTaskManager->getCount(), 'The tasks of other servers were not touched.');
 
     // Verify that deleting a server also deletes all of its tasks.
     $this->addTasks($index2);
-    $this->state->set('search_api_test_backend.exception.addIndex', TRUE);
-    $this->state->set('search_api_test_backend.exception.updateIndex', TRUE);
-    $this->state->set('search_api_test_backend.exception.removeIndex', TRUE);
-    $this->state->set('search_api_test_backend.exception.deleteItems', TRUE);
-    $this->state->set('search_api_test_backend.exception.deleteAllIndexItems', TRUE);
+    $this->setError('backend', 'addIndex');
+    $this->setError('backend', 'updateIndex');
+    $this->setError('backend', 'removeIndex');
+    $this->setError('backend', 'deleteItems');
+    $this->setError('backend', 'deleteAllIndexItems');
     $this->server->delete();
     $this->assertEquals(0, $this->serverTaskManager->getCount($this->server), 'Upon server deletion, all of its tasks were deleted, too.');
     $this->assertEquals(1, $this->serverTaskManager->getCount(), 'The tasks of other servers were not touched.');
@@ -446,25 +441,6 @@ protected function addTasks(IndexInterface $second_index) {
   }
 
   /**
-   * Retrieves the methods called on the test server.
-   *
-   * @param bool $reset
-   *   (optional) Whether to reset the list after the called methods are
-   *   retrieved.
-   *
-   * @return string[]
-   *   The methods called on the test server since the last reset.
-   */
-  protected function getCalledBackendMethods($reset = TRUE) {
-    $key = 'search_api_test_backend.methods_called.' . $this->server->id();
-    $methods_called = $this->state->get($key, array());
-    if ($reset) {
-      $this->state->delete($key);
-    }
-    return $methods_called;
-  }
-
-  /**
    * Retrieves the tasks set on the test server.
    *
    * @return object[]
diff --git a/tests/src/Kernel/TaskTest.php b/tests/src/Kernel/TaskTest.php
index 1305aeb..9e1b44d 100644
--- a/tests/src/Kernel/TaskTest.php
+++ b/tests/src/Kernel/TaskTest.php
@@ -37,7 +37,7 @@ class TaskTest extends KernelTestBase {
   public static $modules = array(
     'user',
     'search_api',
-    'search_api_test_backend',
+    'search_api_test',
     'search_api_test_tasks',
   );
 
@@ -71,7 +71,7 @@ public function setUp() {
       'name' => 'Test Server',
       'id' => 'test_server',
       'status' => 1,
-      'backend' => 'search_api_test_backend',
+      'backend' => 'search_api_test',
     ));
     $this->server->save();
 
