diff --git a/core/tests/Drupal/Tests/Component/Plugin/Discovery/DiscoveryCachedTraitTest.php b/core/tests/Drupal/Tests/Component/Plugin/Discovery/DiscoveryCachedTraitTest.php
new file mode 100644
index 0000000..ff34109
--- /dev/null
+++ b/core/tests/Drupal/Tests/Component/Plugin/Discovery/DiscoveryCachedTraitTest.php
@@ -0,0 +1,89 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Tests\Component\Plugin\Discovery\DiscoveryCachedTraitTest.
+ */
+
+namespace Drupal\Tests\Component\Plugin\Discovery;
+
+use Drupal\Tests\UnitTestCase;
+
+/**
+ * @coversDefaultClass Drupal\Component\Plugin\Discovery\DiscoveryCachedTrait
+ * @uses Drupal\Component\Plugin\Discovery\DiscoveryTrait
+ * @group Plugin
+ */
+class DiscoveryCachedTraitTest extends UnitTestCase {
+
+  // Temporary storage to mock a side-effect.
+  protected $trait;
+  protected $definitions_ref;
+  protected $get_definitions;
+
+
+  /**
+   * Data provider for testGetDefinition().
+   *
+   * @return array
+   *   - Expected result from getDefinition().
+   *   - Cached definitions to be placed into self::$definitions
+   *   - Definitions to be returned by getDefinitions().
+   *   - Plugin name to query for.
+   */
+  public function providerGetDefinition() {
+    return array(
+      ['definition', [], ['plugin_name' => 'definition'], 'plugin_name'],
+      ['definition', ['plugin_name' => 'definition'], [], 'plugin_name'],
+      [NULL, ['plugin_name' => 'definition'], [], 'bad_plugin_name'],
+    );
+  }
+
+  /**
+   * @covers ::getDefinition
+   * @dataProvider providerGetDefinition
+   */
+  public function testGetDefinition($expected, $cached_definitions, $get_definitions, $plugin_id) {
+    // Mock a DiscoveryCachedTrait.
+    $trait = $this->getMockForTrait('Drupal\Component\Plugin\Discovery\DiscoveryCachedTrait');
+    $definitions_ref = new \ReflectionProperty($trait, 'definitions');
+    $definitions_ref->setAccessible(TRUE);
+    // getDefinition() needs the ::$definitions property to be set in one of two
+    // ways: 1) As existing cached data, or 2) as a side-effect of calling
+    // getDefinitions().
+    // If there are no cached definitions, then we have to fake the side-effect
+    // of getDefinitions().
+    if (count($cached_definitions) < 1) {
+      $this->trait = $trait;
+      $this->definitions_ref = $definitions_ref;
+      $this->get_definitions = $get_definitions;
+      // Use a callback method, so we can perform the side-effects.
+      $trait->expects($this->once())
+        ->method('getDefinitions')
+        ->willReturnCallback(array($this, 'getDefinitionsCallback'));
+    }
+    else {
+      // Put $cached_definitions into our mocked ::$definitions.
+      $definitions_ref->setValue($trait, $cached_definitions);
+    }
+    // Call getDefinition(), with $exception_on_invalid always FALSE.
+    $this->assertSame(
+      $expected,
+      $trait->getDefinition($plugin_id, FALSE)
+    );
+  }
+
+  /**
+   * Callback method so we can mock the side-effects of getDefinitions().
+   *
+   * @see testGetDefinition
+   */
+  public function getDefinitionsCallback() {
+    $this->definitions_ref->setValue(
+      $this->trait,
+      $this->get_definitions
+    );
+    return $this->get_definitions;
+  }
+
+}
diff --git a/core/tests/Drupal/Tests/Component/Plugin/Discovery/DiscoveryTraitTest.php b/core/tests/Drupal/Tests/Component/Plugin/Discovery/DiscoveryTraitTest.php
new file mode 100644
index 0000000..9095e01
--- /dev/null
+++ b/core/tests/Drupal/Tests/Component/Plugin/Discovery/DiscoveryTraitTest.php
@@ -0,0 +1,160 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Tests\Component\Plugin\Discovery\DiscoveryTraitTest.
+ */
+
+namespace Drupal\Tests\Component\Plugin\Discovery;
+
+use Drupal\Tests\UnitTestCase;
+
+/**
+ * @group Plugin
+ * @coversDefaultClass Drupal\Component\Plugin\Discovery\DiscoveryTrait
+ */
+class DiscoveryTraitTest extends UnitTestCase {
+
+  /**
+   * Data provider for testDoGetDefinition().
+   *
+   * @return array
+   *   - Expected plugin definition.
+   *   - Plugin definition array, to pass to doGetDefinition().
+   *   - Plugin ID to get, passed to doGetDefinition().
+   */
+  public function providerDoGetDefinition() {
+    return array(
+      ['definition', ['plugin_name' => 'definition'], 'plugin_name'],
+      [NULL, ['plugin_name' => 'definition'], 'bad_plugin_name'],
+    );
+  }
+
+  /**
+   * @covers ::doGetDefinition
+   * @dataProvider providerDoGetDefinition
+   */
+  public function testDoGetDefinition($expected, $definitions, $plugin_id) {
+    // Mock the trait.
+    $trait = $this->getMockForTrait('Drupal\Component\Plugin\Discovery\DiscoveryTrait');
+    // Un-protect the method using reflection.
+    $method_ref = new \ReflectionMethod($trait, 'doGetDefinition');
+    $method_ref->setAccessible(TRUE);
+    // Call doGetDefinition, with $exception_on_invalid always FALSE.
+    $this->assertSame(
+      $expected,
+      $method_ref->invoke($trait, $definitions, $plugin_id, FALSE)
+    );
+  }
+
+  /**
+   * Data provider for testDoGetDefinitionException()
+   *
+   * @return array
+   *   - Expected plugin definition.
+   *   - Plugin definition array, to pass to doGetDefinition().
+   *   - Plugin ID to get, passed to doGetDefinition().
+   */
+  public function providerDoGetDefinitionException() {
+    return array(
+      [FALSE, ['plugin_name' => 'definition'], 'bad_plugin_name'],
+    );
+  }
+
+  /**
+   * @covers ::doGetDefinition
+   * @expectedException Drupal\Component\Plugin\Exception\PluginNotFoundException
+   * @dataProvider providerDoGetDefinitionException
+   * @uses Drupal\Component\Plugin\Exception\PluginNotFoundException
+   */
+  public function testDoGetDefinitionException($expected, $definitions, $plugin_id) {
+    // Mock the trait.
+    $trait = $this->getMockForTrait('Drupal\Component\Plugin\Discovery\DiscoveryTrait');
+    // Un-protect the method using reflection.
+    $method_ref = new \ReflectionMethod($trait, 'doGetDefinition');
+    $method_ref->setAccessible(TRUE);
+    // Call doGetDefinition, with $exception_on_invalid always TRUE.
+    $this->assertSame(
+      $expected,
+      $method_ref->invoke($trait, $definitions, $plugin_id, TRUE)
+    );
+  }
+
+  /**
+   * @covers ::getDefinition
+   * @dataProvider providerDoGetDefinition
+   */
+  public function testGetDefinition($expected, $definitions, $plugin_id) {
+    // Since getDefinition is a wrapper around doGetDefinition(), we can re-use
+    // its data provider. We just have to tell abstract method getDefinitions()
+    // to use the $definitions array.
+    $trait = $this->getMockForTrait('Drupal\Component\Plugin\Discovery\DiscoveryTrait');
+    $trait->expects($this->once())
+      ->method('getDefinitions')
+      ->willReturn($definitions);
+    // Call getDefinition(), with $exception_on_invalid always FALSE.
+    $this->assertSame(
+      $expected,
+      $trait->getDefinition($plugin_id, FALSE)
+    );
+  }
+
+  /**
+   * @covers ::getDefinition
+   * @expectedException Drupal\Component\Plugin\Exception\PluginNotFoundException
+   * @dataProvider providerDoGetDefinitionException
+   * @uses Drupal\Component\Plugin\Exception\PluginNotFoundException
+   */
+  public function testGetDefinitionException($expected, $definitions, $plugin_id) {
+    // Since getDefinition is a wrapper around doGetDefinition(), we can re-use
+    // its data provider. We just have to tell abstract method getDefinitions()
+    // to use the $definitions array.
+    $trait = $this->getMockForTrait('Drupal\Component\Plugin\Discovery\DiscoveryTrait');
+    $trait->expects($this->once())
+      ->method('getDefinitions')
+      ->willReturn($definitions);
+    // Call getDefinition(), with $exception_on_invalid always TRUE.
+    $this->assertSame(
+      $expected,
+      $trait->getDefinition($plugin_id, TRUE)
+    );
+  }
+
+  /**
+   * Data provider for testHasDefinition().
+   *
+   * @return array
+   *   - Expected TRUE or FALSE.
+   *   - Plugin ID to look for.
+   */
+  public function providerHasDefinition() {
+    return array(
+      [TRUE, 'valid'],
+      [FALSE, 'not_valid'],
+    );
+  }
+
+  /**
+   * @covers ::hasDefinition
+   * @dataProvider providerHasDefinition
+   */
+  public function testHasDefinition($expected, $plugin_id) {
+    $trait = $this->getMockBuilder('Drupal\Component\Plugin\Discovery\DiscoveryTrait')
+      ->setMethods(array('getDefinition'))
+      ->getMockForTrait();
+    // Set up our mocked getDefinition() to return TRUE for 'valid' and FALSE
+    // for 'not_valid'.
+    $trait->expects($this->once())
+      ->method('getDefinition')
+      ->will($this->returnValueMap(array(
+        ['valid', FALSE, TRUE],
+        ['not_valid', FALSE, FALSE],
+      )));
+    // Call hasDefinition().
+    $this->assertSame(
+      $expected,
+      $trait->hasDefinition($plugin_id)
+    );
+  }
+
+}
