diff --git a/core/core.services.yml b/core/core.services.yml
index 4eb462c..1350472 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -468,7 +468,12 @@ services:
     arguments: ['@theme_handler', '@config.factory', '@config.installer', '@module_handler', '@config.manager', '@asset.css.collection_optimizer', '@router.builder', '@logger.channel.default', '@state']
   entity.manager:
     class: Drupal\Core\Entity\EntityManager
-    arguments: ['@container.namespaces', '@module_handler', '@cache.discovery', '@language_manager', '@string_translation', '@class_resolver', '@typed_data_manager', '@keyvalue', '@event_dispatcher']
+    arguments: ['@module_handler', '@cache.discovery', '@language_manager', '@typed_data_manager', '@keyvalue', '@event_dispatcher', '@entity.type.manager']
+    tags:
+      - { name: plugin_manager_cache_clear }
+  entity.type.manager:
+    class: Drupal\Core\Entity\EntityTypeManager
+    arguments: ['@container.namespaces', '@module_handler', '@cache.discovery', '@string_translation', '@class_resolver']
     parent: container.trait
     tags:
       - { name: plugin_manager_cache_clear }
diff --git a/core/lib/Drupal/Core/Entity/EntityDefinitionUpdateManager.php b/core/lib/Drupal/Core/Entity/EntityDefinitionUpdateManager.php
index 902aab9..6c17bb3 100644
--- a/core/lib/Drupal/Core/Entity/EntityDefinitionUpdateManager.php
+++ b/core/lib/Drupal/Core/Entity/EntityDefinitionUpdateManager.php
@@ -95,13 +95,13 @@ public function getChangeSummary() {
    * {@inheritdoc}
    */
   public function applyUpdates() {
-    $change_list = $this->getChangeList();
-    if ($change_list) {
+    $change_list_parent = $this->getChangeList();
+    if ($change_list_parent) {
       // self::getChangeList() only disables the cache and does not invalidate.
       // In case there are changes, explicitly invalidate caches.
       $this->entityManager->clearCachedDefinitions();
     }
-    foreach ($change_list as $entity_type_id => $change_list) {
+    foreach ($change_list_parent as $entity_type_id => $change_list) {
       // Process entity type definition changes before storage definitions ones
       // this is necessary when you change an entity type from non-revisionable
       // to revisionable and at the same time add revisionable fields to the
@@ -194,12 +194,12 @@ protected function doEntityUpdate($op, $entity_type_id) {
    * @param string $op
    *   The operation to perform, possible values are static::DEFINITION_CREATED,
    *   static::DEFINITION_UPDATED or static::DEFINITION_DELETED.
-   * @param array|null $storage_definition
+   * @param \Drupal\Core\Field\FieldStorageDefinitionInterface|null $storage_definition
    *   The new field storage definition.
-   * @param array|null $original_storage_definition
+   * @param \Drupal\Core\Field\FieldStorageDefinitionInterface|null $original_storage_definition
    *   The original field storage definition.
    */
-  protected function doFieldUpdate($op, $storage_definition = NULL, $original_storage_definition = NULL) {
+  protected function doFieldUpdate($op, FieldStorageDefinitionInterface $storage_definition = NULL, FieldStorageDefinitionInterface $original_storage_definition = NULL) {
     switch ($op) {
       case static::DEFINITION_CREATED:
         $this->entityManager->onFieldStorageDefinitionCreate($storage_definition);
diff --git a/core/lib/Drupal/Core/Entity/EntityForm.php b/core/lib/Drupal/Core/Entity/EntityForm.php
index 5450e25..0ec59c0 100644
--- a/core/lib/Drupal/Core/Entity/EntityForm.php
+++ b/core/lib/Drupal/Core/Entity/EntityForm.php
@@ -41,10 +41,19 @@ class EntityForm extends FormBase implements EntityFormInterface {
    * The entity manager.
    *
    * @var \Drupal\Core\Entity\EntityManagerInterface
+   *
+   * @deprecated
    */
   protected $entityManager;
 
   /**
+   * The entity type manager.
+   *
+   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
+   */
+  protected $entityTypeManager;
+
+  /**
    * The entity being used by this form.
    *
    * @var \Drupal\Core\Entity\EntityInterface
@@ -404,4 +413,12 @@ public function setEntityManager(EntityManagerInterface $entity_manager) {
     return $this;
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function setEntityTypeManager(EntityTypeManagerInterface $entity_type_manager) {
+    $this->entityTypeManager = $entity_type_manager;
+    return $this;
+  }
+
 }
diff --git a/core/lib/Drupal/Core/Entity/EntityFormInterface.php b/core/lib/Drupal/Core/Entity/EntityFormInterface.php
index 8ae1a91..738440e 100644
--- a/core/lib/Drupal/Core/Entity/EntityFormInterface.php
+++ b/core/lib/Drupal/Core/Entity/EntityFormInterface.php
@@ -137,7 +137,19 @@ public function setModuleHandler(ModuleHandlerInterface $module_handler);
    *   The entity manager.
    *
    * @return $this
+   *
+   * @deprecated
    */
   public function setEntityManager(EntityManagerInterface $entity_manager);
 
+  /**
+   * Sets the entity type manager for this form.
+   *
+   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
+   *   The entity type manager.
+   *
+   * @return $this
+   */
+  public function setEntityTypeManager(EntityTypeManagerInterface $entity_type_manager);
+
 }
diff --git a/core/lib/Drupal/Core/Entity/EntityManager.php b/core/lib/Drupal/Core/Entity/EntityManager.php
index ad0002e..d4cc05a 100644
--- a/core/lib/Drupal/Core/Entity/EntityManager.php
+++ b/core/lib/Drupal/Core/Entity/EntityManager.php
@@ -7,14 +7,10 @@
 
 namespace Drupal\Core\Entity;
 
-use Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException;
-use Drupal\Component\Plugin\Exception\PluginNotFoundException;
 use Drupal\Core\Cache\Cache;
 use Drupal\Core\Cache\CacheBackendInterface;
 use Drupal\Core\Config\Entity\ConfigEntityType;
-use Drupal\Core\DependencyInjection\ClassResolverInterface;
 use Drupal\Core\Entity\Exception\AmbiguousEntityClassException;
-use Drupal\Core\Entity\Exception\InvalidLinkTemplateException;
 use Drupal\Core\Entity\Exception\NoCorrespondingEntityClassException;
 use Drupal\Core\Extension\ModuleHandlerInterface;
 use Drupal\Core\Field\BaseFieldDefinition;
@@ -24,17 +20,12 @@
 use Drupal\Core\Field\FieldStorageDefinitionInterface;
 use Drupal\Core\Field\FieldStorageDefinitionListenerInterface;
 use Drupal\Core\KeyValueStore\KeyValueFactoryInterface;
-use Drupal\Core\KeyValueStore\KeyValueStoreInterface;
 use Drupal\Core\Language\LanguageInterface;
 use Drupal\Core\Language\LanguageManagerInterface;
-use Drupal\Core\Plugin\DefaultPluginManager;
-use Drupal\Core\Plugin\Discovery\AnnotatedClassDiscovery;
+use Drupal\Core\Plugin\Discovery\CacheBackendTrait;
 use Drupal\Core\StringTranslation\StringTranslationTrait;
-use Drupal\Core\StringTranslation\TranslationInterface;
 use Drupal\Core\TypedData\TranslatableInterface;
 use Drupal\Core\TypedData\TypedDataManager;
-use Symfony\Component\DependencyInjection\ContainerAwareInterface;
-use Symfony\Component\DependencyInjection\ContainerAwareTrait;
 use Symfony\Component\EventDispatcher\EventDispatcherInterface;
 
 /**
@@ -51,9 +42,9 @@
  * @see \Drupal\Core\Entity\EntityTypeInterface
  * @see hook_entity_type_alter()
  */
-class EntityManager extends DefaultPluginManager implements EntityManagerInterface, ContainerAwareInterface {
+class EntityManager implements EntityManagerInterface {
 
-  use ContainerAwareTrait;
+  use CacheBackendTrait;
   use StringTranslationTrait;
 
   /**
@@ -64,13 +55,6 @@ class EntityManager extends DefaultPluginManager implements EntityManagerInterfa
   protected $extraFields = array();
 
   /**
-   * Contains instantiated handlers keyed by handler type and entity type.
-   *
-   * @var array
-   */
-  protected $handlers = array();
-
-  /**
    * Static cache of base field definitions.
    *
    * @var array
@@ -95,20 +79,6 @@ class EntityManager extends DefaultPluginManager implements EntityManagerInterfa
   protected $fieldStorageDefinitions;
 
   /**
-   * The string translationManager.
-   *
-   * @var \Drupal\Core\StringTranslation\TranslationInterface
-   */
-  protected $translationManager;
-
-  /**
-   * The class resolver.
-   *
-   * @var \Drupal\Core\DependencyInjection\ClassResolverInterface
-   */
-  protected $classResolver;
-
-  /**
    * The typed data manager.
    *
    * @var \Drupal\Core\TypedData\TypedDataManager
@@ -178,209 +148,147 @@ class EntityManager extends DefaultPluginManager implements EntityManagerInterfa
   protected $classNameEntityTypeMap = array();
 
   /**
+   * The entity type manager.
+   *
+   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
+   */
+  protected $entityTypeManager;
+
+  /**
+   * The module handler.
+   *
+   * @var \Drupal\Core\Extension\ModuleHandlerInterface
+   */
+  protected $moduleHandler;
+
+  /**
    * Constructs a new Entity plugin manager.
    *
-   * @param \Traversable $namespaces
-   *   An object that implements \Traversable which contains the root paths
-   *   keyed by the corresponding namespace to look for plugin implementations,
    * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
    *   The module handler.
    * @param \Drupal\Core\Cache\CacheBackendInterface $cache
    *   The cache backend to use.
    * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
    *   The language manager.
-   * @param \Drupal\Core\StringTranslation\TranslationInterface $translation_manager
-   *   The string translationManager.
-   * @param \Drupal\Core\DependencyInjection\ClassResolverInterface $class_resolver
-   *   The class resolver.
    * @param \Drupal\Core\TypedData\TypedDataManager $typed_data_manager
    *   The typed data manager.
    * @param \Drupal\Core\KeyValueStore\KeyValueFactoryInterface $key_value_factory
    *   The keyvalue factory.
    * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $event_dispatcher
    *   The event dispatcher.
+   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
+   *   The entity type manager.
    */
-  public function __construct(\Traversable $namespaces, ModuleHandlerInterface $module_handler, CacheBackendInterface $cache, LanguageManagerInterface $language_manager, TranslationInterface $translation_manager, ClassResolverInterface $class_resolver, TypedDataManager $typed_data_manager, KeyValueFactoryInterface $key_value_factory, EventDispatcherInterface $event_dispatcher) {
-    parent::__construct('Entity', $namespaces, $module_handler, 'Drupal\Core\Entity\EntityInterface');
-
+  public function __construct(ModuleHandlerInterface $module_handler, CacheBackendInterface $cache, LanguageManagerInterface $language_manager, TypedDataManager $typed_data_manager, KeyValueFactoryInterface $key_value_factory, EventDispatcherInterface $event_dispatcher, EntityTypeManagerInterface $entity_type_manager) {
     $this->setCacheBackend($cache, 'entity_type', array('entity_types'));
-    $this->alterInfo('entity_type');
 
-    $this->discovery = new AnnotatedClassDiscovery('Entity', $namespaces, 'Drupal\Core\Entity\Annotation\EntityType');
+    $this->moduleHandler = $module_handler;
     $this->languageManager = $language_manager;
-    $this->translationManager = $translation_manager;
-    $this->classResolver = $class_resolver;
     $this->typedDataManager = $typed_data_manager;
     $this->keyValueFactory = $key_value_factory;
     $this->eventDispatcher = $event_dispatcher;
+    $this->entityTypeManager = $entity_type_manager;
   }
 
   /**
    * {@inheritdoc}
    */
   public function clearCachedDefinitions() {
-    parent::clearCachedDefinitions();
+    $this->cacheClear();
     $this->clearCachedBundles();
     $this->clearCachedFieldDefinitions();
     $this->classNameEntityTypeMap = array();
-    $this->handlers = array();
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function processDefinition(&$definition, $plugin_id) {
-    /** @var \Drupal\Core\Entity\EntityTypeInterface $definition */
-    parent::processDefinition($definition, $plugin_id);
-
-    // All link templates must have a leading slash.
-    foreach ((array) $definition->getLinkTemplates() as $link_relation_name => $link_template) {
-      if ($link_template[0] != '/') {
-        throw new InvalidLinkTemplateException("Link template '$link_relation_name' for entity type '$plugin_id' must start with a leading slash, the current link template is '$link_template'");
-      }
-    }
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  protected function findDefinitions() {
-    $definitions = $this->getDiscovery()->getDefinitions();
-
-    // Directly call the hook implementations to pass the definitions to them
-    // by reference, so new entity types can be added.
-    foreach ($this->moduleHandler->getImplementations('entity_type_build') as $module) {
-      $function = $module . '_' . 'entity_type_build';
-      $function($definitions);
-    }
-    foreach ($definitions as $plugin_id => $definition) {
-      $this->processDefinition($definition, $plugin_id);
-    }
-    if ($this->alterHook) {
-      $this->moduleHandler->alter($this->alterHook, $definitions);
-    }
-    return $definitions;
+    $this->entityTypeManager->clearCachedDefinitions();
   }
 
   /**
    * {@inheritdoc}
+   *
+   * @deprecated
    */
   public function getDefinition($entity_type_id, $exception_on_invalid = TRUE) {
-    if (($entity_type = parent::getDefinition($entity_type_id, FALSE)) && class_exists($entity_type->getClass())) {
-      return $entity_type;
-    }
-    elseif (!$exception_on_invalid) {
-      return NULL;
-    }
-
-    throw new PluginNotFoundException($entity_type_id, sprintf('The "%s" entity type does not exist.', $entity_type_id));
+    return $this->entityTypeManager->getDefinition($entity_type_id, $exception_on_invalid);
   }
 
   /**
    * {@inheritdoc}
+   *
+   * @deprecated
    */
   public function hasHandler($entity_type, $handler_type) {
-    if ($definition = $this->getDefinition($entity_type, FALSE)) {
-      return $definition->hasHandlerClass($handler_type);
-    }
-    return FALSE;
+    return $this->entityTypeManager->hasHandler($entity_type, $handler_type);
   }
 
   /**
    * {@inheritdoc}
+   *
+   * @deprecated
    */
   public function getStorage($entity_type) {
-    return $this->getHandler($entity_type, 'storage');
+    return $this->entityTypeManager->getStorage($entity_type);
   }
 
   /**
    * {@inheritdoc}
+   *
+   * @deprecated
    */
   public function getListBuilder($entity_type) {
-    return $this->getHandler($entity_type, 'list_builder');
+    return $this->entityTypeManager->getListBuilder($entity_type);
   }
 
   /**
    * {@inheritdoc}
+   *
+   * @deprecated
    */
   public function getFormObject($entity_type, $operation) {
-    if (!isset($this->handlers['form'][$operation][$entity_type])) {
-      if (!$class = $this->getDefinition($entity_type, TRUE)->getFormClass($operation)) {
-        throw new InvalidPluginDefinitionException($entity_type, sprintf('The "%s" entity type did not specify a "%s" form class.', $entity_type, $operation));
-      }
-
-      $form_object = $this->classResolver->getInstanceFromDefinition($class);
-
-      $form_object
-        ->setStringTranslation($this->translationManager)
-        ->setModuleHandler($this->moduleHandler)
-        ->setEntityManager($this)
-        ->setOperation($operation);
-      $this->handlers['form'][$operation][$entity_type] = $form_object;
-    }
-    return $this->handlers['form'][$operation][$entity_type];
+    return $this->entityTypeManager->getFormObject($entity_type, $operation);
   }
 
   /**
    * {@inheritdoc}
+   *
+   * @deprecated
    */
   public function getRouteProviders($entity_type) {
-    if (!isset($this->handlers['route_provider'][$entity_type])) {
-      $route_provider_classes = $this->getDefinition($entity_type, TRUE)->getRouteProviderClasses();
-
-      foreach ($route_provider_classes as $type => $class) {
-        $this->handlers['route_provider'][$entity_type][$type] = $this->createHandlerInstance($class, $this->getDefinition($entity_type));
-      }
-    }
-    return isset($this->handlers['route_provider'][$entity_type]) ? $this->handlers['route_provider'][$entity_type] : [];
+    return $this->entityTypeManager->getRouteProviders($entity_type);
   }
 
   /**
    * {@inheritdoc}
+   *
+   * @deprecated
    */
   public function getViewBuilder($entity_type) {
-    return $this->getHandler($entity_type, 'view_builder');
+    return $this->entityTypeManager->getViewBuilder($entity_type);
   }
 
   /**
    * {@inheritdoc}
+   *
+   * @deprecated
    */
   public function getAccessControlHandler($entity_type) {
-    return $this->getHandler($entity_type, 'access');
+    return $this->entityTypeManager->getAccessControlHandler($entity_type);
   }
 
   /**
    * {@inheritdoc}
+   *
+   * @deprecated
    */
   public function getHandler($entity_type, $handler_type) {
-    if (!isset($this->handlers[$handler_type][$entity_type])) {
-      $definition = $this->getDefinition($entity_type);
-      $class = $definition->getHandlerClass($handler_type);
-      if (!$class) {
-        throw new InvalidPluginDefinitionException($entity_type, sprintf('The "%s" entity type did not specify a %s handler.', $entity_type, $handler_type));
-      }
-      $this->handlers[$handler_type][$entity_type] = $this->createHandlerInstance($class, $definition);
-    }
-    return $this->handlers[$handler_type][$entity_type];
+    return $this->entityTypeManager->getHandler($entity_type, $handler_type);
   }
 
   /**
    * {@inheritdoc}
+   *
+   * @deprecated
    */
   public function createHandlerInstance($class, EntityTypeInterface $definition = null) {
-    if (is_subclass_of($class, 'Drupal\Core\Entity\EntityHandlerInterface')) {
-      $handler = $class::createInstance($this->container, $definition);
-    }
-    else {
-      $handler = new $class($definition);
-    }
-    if (method_exists($handler, 'setModuleHandler')) {
-      $handler->setModuleHandler($this->moduleHandler);
-    }
-    if (method_exists($handler, 'setStringTranslation')) {
-      $handler->setStringTranslation($this->translationManager);
-    }
-    return $handler;
+    return $this->entityTypeManager->createHandlerInstance($class, $definition);
   }
 
   /**
@@ -423,7 +331,7 @@ protected function buildBaseFieldDefinitions($entity_type_id) {
     $keys = array_filter($entity_type->getKeys());
 
     // Fail with an exception for non-fieldable entity types.
-    if (!$entity_type->isSubclassOf('\Drupal\Core\Entity\FieldableEntityInterface')) {
+    if (!$entity_type->isSubclassOf(FieldableEntityInterface::class)) {
       throw new \LogicException("Getting the base fields is not supported for entity type {$entity_type->getLabel()}.");
     }
 
@@ -655,7 +563,7 @@ public function getFieldMap() {
         // bundles, and we do not expect to have so many different entity
         // types for this to become a bottleneck.
         foreach ($this->getDefinitions() as $entity_type_id => $entity_type) {
-          if ($entity_type->isSubclassOf('\Drupal\Core\Entity\FieldableEntityInterface')) {
+          if ($entity_type->isSubclassOf(FieldableEntityInterface::class)) {
             $bundles = array_keys($this->getBundleInfo($entity_type_id));
             foreach ($this->getBaseFieldDefinitions($entity_type_id) as $field_name => $base_field_definition) {
               $this->fieldMap[$entity_type_id][$field_name] = [
@@ -1205,7 +1113,7 @@ public function onEntityTypeCreate(EntityTypeInterface $entity_type) {
     $this->eventDispatcher->dispatch(EntityTypeEvents::CREATE, new EntityTypeEvent($entity_type));
 
     $this->setLastInstalledDefinition($entity_type);
-    if ($entity_type->isSubclassOf('\Drupal\Core\Entity\FieldableEntityInterface')) {
+    if ($entity_type->isSubclassOf(FieldableEntityInterface::class)) {
       $this->setLastInstalledFieldStorageDefinitions($entity_type_id, $this->getFieldStorageDefinitions($entity_type_id));
     }
   }
@@ -1368,13 +1276,12 @@ public function getLastInstalledDefinition($entity_type_id) {
    * {@inheritdoc}
    */
   public function useCaches($use_caches = FALSE) {
-    parent::useCaches($use_caches);
     if (!$use_caches) {
-      $this->handlers = [];
       $this->fieldDefinitions = [];
       $this->baseFieldDefinitions = [];
       $this->fieldStorageDefinitions = [];
     }
+    $this->entityTypeManager->useCaches($use_caches);
   }
 
   /**
@@ -1447,4 +1354,40 @@ protected function deleteLastInstalledFieldStorageDefinition(FieldStorageDefinit
     $this->setLastInstalledFieldStorageDefinitions($entity_type_id, $definitions);
   }
 
+  /**
+   * {@inheritdoc}
+   *
+   * @deprecated
+   */
+  public function getDefinitions() {
+    return $this->entityTypeManager->getDefinitions();
+  }
+
+  /**
+   * {@inheritdoc}
+   *
+   * @deprecated
+   */
+  public function hasDefinition($plugin_id) {
+    return $this->entityTypeManager->hasDefinition($plugin_id);
+  }
+
+  /**
+   * {@inheritdoc}
+   *
+   * @deprecated
+   */
+  public function createInstance($plugin_id, array $configuration = []) {
+    return $this->entityTypeManager->createInstance($plugin_id, $configuration);
+  }
+
+  /**
+   * {@inheritdoc}
+   *
+   * @deprecated
+   */
+  public function getInstance(array $options) {
+    return $this->entityTypeManager->getInstance($options);
+  }
+
 }
diff --git a/core/lib/Drupal/Core/Entity/EntityManagerInterface.php b/core/lib/Drupal/Core/Entity/EntityManagerInterface.php
index 7b180d2..ed35cef 100644
--- a/core/lib/Drupal/Core/Entity/EntityManagerInterface.php
+++ b/core/lib/Drupal/Core/Entity/EntityManagerInterface.php
@@ -14,8 +14,12 @@
 
 /**
  * Provides an interface for entity type managers.
+ *
+ * Note: The \Drupal\Component\Plugin\PluginManagerInterface and
+ *   \Drupal\Core\Entity\EntityTypeManagerInterface portions of this interface
+ *   are deprecated as of 8.0.0 and will be removed in 9.0.0.
  */
-interface EntityManagerInterface extends PluginManagerInterface, EntityTypeListenerInterface, EntityBundleListenerInterface, FieldStorageDefinitionListenerInterface, FieldDefinitionListenerInterface, CachedDiscoveryInterface {
+interface EntityManagerInterface extends PluginManagerInterface, EntityTypeListenerInterface, EntityBundleListenerInterface, FieldStorageDefinitionListenerInterface, FieldDefinitionListenerInterface, CachedDiscoveryInterface, EntityTypeManagerInterface {
 
   /**
    * Builds a list of entity type labels suitable for a Form API options list.
@@ -144,30 +148,6 @@ public function getFieldMap();
   public function getFieldMapByFieldType($field_type);
 
   /**
-   * Creates a new access control handler instance.
-   *
-   * @param string $entity_type
-   *   The entity type for this access control handler.
-   *
-   * @return \Drupal\Core\Entity\EntityAccessControlHandlerInterface.
-   *   A access control handler instance.
-   */
-  public function getAccessControlHandler($entity_type);
-
-  /**
-   * Creates a new storage instance.
-   *
-   * @param string $entity_type
-   *   The entity type for this storage.
-   *
-   * @return \Drupal\Core\Entity\EntityStorageInterface
-   *   A storage instance.
-   *
-   * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
-   */
-  public function getStorage($entity_type);
-
-  /**
    * Get the bundle info of all entity types.
    *
    * @return array
@@ -191,96 +171,6 @@ public function clearCachedFieldDefinitions();
   public function clearCachedBundles();
 
   /**
-   * Creates a new view builder instance.
-   *
-   * @param string $entity_type
-   *   The entity type for this view builder.
-   *
-   * @return \Drupal\Core\Entity\EntityViewBuilderInterface.
-   *   A view builder instance.
-   */
-  public function getViewBuilder($entity_type);
-
-  /**
-   * Creates a new entity list builder.
-   *
-   * @param string $entity_type
-   *   The entity type for this list builder.
-   *
-   * @return \Drupal\Core\Entity\EntityListBuilderInterface
-   *   An entity list builder instance.
-   */
-  public function getListBuilder($entity_type);
-
-  /**
-   * Creates a new form instance.
-   *
-   * @param string $entity_type
-   *   The entity type for this form.
-   * @param string $operation
-   *   The name of the operation to use, e.g., 'default'.
-   *
-   * @return \Drupal\Core\Entity\EntityFormInterface
-   *   A form instance.
-   */
-  public function getFormObject($entity_type, $operation);
-
-  /**
-   * Gets all route provider instances.
-   *
-   * @param string $entity_type
-   *   The entity type for this route providers.
-   *
-   * @return \Drupal\Core\Entity\Routing\EntityRouteProviderInterface[]
-   */
-  public function getRouteProviders($entity_type);
-
-  /**
-   * Checks whether a certain entity type has a certain handler.
-   *
-   * @param string $entity_type
-   *   The name of the entity type.
-   * @param string $handler_type
-   *   The name of the handler.
-   *
-   * @return bool
-   *   Returns TRUE if the entity type has the handler, else FALSE.
-   */
-  public function hasHandler($entity_type, $handler_type);
-
-  /**
-   * Creates a new handler instance for a entity type and handler type.
-   *
-   * @param string $entity_type
-   *   The entity type for this controller.
-   * @param string $handler_type
-   *   The controller type to create an instance for.
-   *
-   * @return object
-   *   A handler instance.
-   *
-   * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
-   */
-  public function getHandler($entity_type, $handler_type);
-
-  /**
-   * Creates new handler instance.
-   *
-   * Usually \Drupal\Core\Entity\EntityManagerInterface::getHandler() is
-   * preferred since that method has additional checking that the class exists
-   * and has static caches.
-   *
-   * @param mixed $class
-   *   The handler class to instantiate.
-   * @param \Drupal\Core\Entity\EntityTypeInterface $definition
-   *   The entity type definition.
-   *
-   * @return object
-   *   A handler instance.
-   */
-  public function createHandlerInstance($class, EntityTypeInterface $definition = null);
-
-  /**
    * Gets the bundle info of an entity type.
    *
    * @param string $entity_type
@@ -344,13 +234,6 @@ public function getExtraFields($entity_type_id, $bundle);
   public function getTranslationFromContext(EntityInterface $entity, $langcode = NULL, $context = array());
 
   /**
-   * {@inheritdoc}
-   *
-   * @return \Drupal\Core\Entity\EntityTypeInterface|null
-   */
-  public function getDefinition($entity_type_id, $exception_on_invalid = TRUE);
-
-  /**
    * Gets the entity type definition in its most recently installed state.
    *
    * During the application lifetime, entity type definitions can change. For
@@ -383,13 +266,6 @@ public function getDefinition($entity_type_id, $exception_on_invalid = TRUE);
   public function getLastInstalledDefinition($entity_type_id);
 
   /**
-   * {@inheritdoc}
-   *
-   * @return \Drupal\Core\Entity\EntityTypeInterface[]
-   */
-  public function getDefinitions();
-
-  /**
    * Gets the entity view mode info for all entity types.
    *
    * @return array
diff --git a/core/lib/Drupal/Core/Entity/EntityTypeManager.php b/core/lib/Drupal/Core/Entity/EntityTypeManager.php
new file mode 100644
index 0000000..844b937
--- /dev/null
+++ b/core/lib/Drupal/Core/Entity/EntityTypeManager.php
@@ -0,0 +1,246 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Entity\EntityTypeManager.
+ */
+
+namespace Drupal\Core\Entity;
+
+use Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException;
+use Drupal\Component\Plugin\Exception\PluginNotFoundException;
+use Drupal\Core\Cache\CacheBackendInterface;
+use Drupal\Core\DependencyInjection\ClassResolverInterface;
+use Drupal\Core\Entity\Exception\InvalidLinkTemplateException;
+use Drupal\Core\Extension\ModuleHandlerInterface;
+use Drupal\Core\Plugin\DefaultPluginManager;
+use Drupal\Core\Plugin\Discovery\AnnotatedClassDiscovery;
+use Drupal\Core\StringTranslation\TranslationInterface;
+use Symfony\Component\DependencyInjection\ContainerAwareInterface;
+use Symfony\Component\DependencyInjection\ContainerAwareTrait;
+
+/**
+ * @todo.
+ */
+class EntityTypeManager extends DefaultPluginManager implements EntityTypeManagerInterface, ContainerAwareInterface {
+
+  use ContainerAwareTrait;
+
+  /**
+   * Contains instantiated handlers keyed by handler type and entity type.
+   *
+   * @var array
+   */
+  protected $handlers = [];
+
+  /**
+   * Constructs a new Entity plugin manager.
+   *
+   * @param \Traversable $namespaces
+   *   An object that implements \Traversable which contains the root paths
+   *   keyed by the corresponding namespace to look for plugin implementations,
+   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
+   *   The module handler.
+   * @param \Drupal\Core\Cache\CacheBackendInterface $cache
+   *   The cache backend to use.
+   * @param \Drupal\Core\StringTranslation\TranslationInterface $translation_manager
+   *   The string translationManager.
+   * @param \Drupal\Core\DependencyInjection\ClassResolverInterface $class_resolver
+   *   The class resolver.
+   */
+  public function __construct(\Traversable $namespaces, ModuleHandlerInterface $module_handler, CacheBackendInterface $cache, TranslationInterface $translation_manager, ClassResolverInterface $class_resolver) {
+    parent::__construct('Entity', $namespaces, $module_handler, 'Drupal\Core\Entity\EntityInterface');
+
+    $this->setCacheBackend($cache, 'entity_type', ['entity_types']);
+    $this->alterInfo('entity_type');
+
+    $this->discovery = new AnnotatedClassDiscovery('Entity', $namespaces, 'Drupal\Core\Entity\Annotation\EntityType');
+    $this->translationManager = $translation_manager;
+    $this->classResolver = $class_resolver;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function processDefinition(&$definition, $plugin_id) {
+    /** @var \Drupal\Core\Entity\EntityTypeInterface $definition */
+    parent::processDefinition($definition, $plugin_id);
+
+    // All link templates must have a leading slash.
+    foreach ((array) $definition->getLinkTemplates() as $link_relation_name => $link_template) {
+      if ($link_template[0] != '/') {
+        throw new InvalidLinkTemplateException("Link template '$link_relation_name' for entity type '$plugin_id' must start with a leading slash, the current link template is '$link_template'");
+      }
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function findDefinitions() {
+    $definitions = $this->getDiscovery()->getDefinitions();
+
+    // Directly call the hook implementations to pass the definitions to them
+    // by reference, so new entity types can be added.
+    foreach ($this->moduleHandler->getImplementations('entity_type_build') as $module) {
+      $function = $module . '_' . 'entity_type_build';
+      $function($definitions);
+    }
+    foreach ($definitions as $plugin_id => $definition) {
+      $this->processDefinition($definition, $plugin_id);
+    }
+    if ($this->alterHook) {
+      $this->moduleHandler->alter($this->alterHook, $definitions);
+    }
+
+    return $definitions;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getDefinition($entity_type_id, $exception_on_invalid = TRUE) {
+    if (($entity_type = parent::getDefinition($entity_type_id, FALSE)) && class_exists($entity_type->getClass())) {
+      return $entity_type;
+    }
+    elseif (!$exception_on_invalid) {
+      return NULL;
+    }
+
+    throw new PluginNotFoundException($entity_type_id, sprintf('The "%s" entity type does not exist.', $entity_type_id));
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function clearCachedDefinitions() {
+    parent::clearCachedDefinitions();
+    $this->handlers = [];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function useCaches($use_caches = FALSE) {
+    parent::useCaches($use_caches);
+    if (!$use_caches) {
+      $this->handlers = [];
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function hasHandler($entity_type, $handler_type) {
+    if ($definition = $this->getDefinition($entity_type, FALSE)) {
+      return $definition->hasHandlerClass($handler_type);
+    }
+
+    return FALSE;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getStorage($entity_type) {
+    return $this->getHandler($entity_type, 'storage');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getListBuilder($entity_type) {
+    return $this->getHandler($entity_type, 'list_builder');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormObject($entity_type, $operation) {
+    if (!isset($this->handlers['form'][$operation][$entity_type])) {
+      if (!$class = $this->getDefinition($entity_type, TRUE)->getFormClass($operation)) {
+        throw new InvalidPluginDefinitionException($entity_type, sprintf('The "%s" entity type did not specify a "%s" form class.', $entity_type, $operation));
+      }
+
+      $form_object = $this->classResolver->getInstanceFromDefinition($class);
+
+      $form_object
+        ->setStringTranslation($this->translationManager)
+        ->setModuleHandler($this->moduleHandler)
+        ->setEntityTypeManager($this)
+        ->setOperation($operation)
+        // The entity manager cannot be injected due to a circular dependency.
+        // @todo Remove this set call.
+        ->setEntityManager(\Drupal::entityManager());
+      $this->handlers['form'][$operation][$entity_type] = $form_object;
+    }
+
+    return $this->handlers['form'][$operation][$entity_type];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getRouteProviders($entity_type) {
+    if (!isset($this->handlers['route_provider'][$entity_type])) {
+      $route_provider_classes = $this->getDefinition($entity_type, TRUE)->getRouteProviderClasses();
+
+      foreach ($route_provider_classes as $type => $class) {
+        $this->handlers['route_provider'][$entity_type][$type] = $this->createHandlerInstance($class, $this->getDefinition($entity_type));
+      }
+    }
+
+    return isset($this->handlers['route_provider'][$entity_type]) ? $this->handlers['route_provider'][$entity_type] : [];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getViewBuilder($entity_type) {
+    return $this->getHandler($entity_type, 'view_builder');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getAccessControlHandler($entity_type) {
+    return $this->getHandler($entity_type, 'access');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getHandler($entity_type, $handler_type) {
+    if (!isset($this->handlers[$handler_type][$entity_type])) {
+      $definition = $this->getDefinition($entity_type);
+      $class = $definition->getHandlerClass($handler_type);
+      if (!$class) {
+        throw new InvalidPluginDefinitionException($entity_type, sprintf('The "%s" entity type did not specify a %s handler.', $entity_type, $handler_type));
+      }
+      $this->handlers[$handler_type][$entity_type] = $this->createHandlerInstance($class, $definition);
+    }
+
+    return $this->handlers[$handler_type][$entity_type];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function createHandlerInstance($class, EntityTypeInterface $definition = NULL) {
+    if (is_subclass_of($class, 'Drupal\Core\Entity\EntityHandlerInterface')) {
+      $handler = $class::createInstance($this->container, $definition);
+    }
+    else {
+      $handler = new $class($definition);
+    }
+    if (method_exists($handler, 'setModuleHandler')) {
+      $handler->setModuleHandler($this->moduleHandler);
+    }
+    if (method_exists($handler, 'setStringTranslation')) {
+      $handler->setStringTranslation($this->translationManager);
+    }
+
+    return $handler;
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Entity/EntityTypeManagerInterface.php b/core/lib/Drupal/Core/Entity/EntityTypeManagerInterface.php
new file mode 100644
index 0000000..f58d000
--- /dev/null
+++ b/core/lib/Drupal/Core/Entity/EntityTypeManagerInterface.php
@@ -0,0 +1,146 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Entity\EntityTypeManagerInterface.
+ */
+
+namespace Drupal\Core\Entity;
+
+use Drupal\Component\Plugin\Discovery\CachedDiscoveryInterface;
+use Drupal\Component\Plugin\PluginManagerInterface;
+
+/**
+ * Provides an interface for entity type managers.
+ */
+interface EntityTypeManagerInterface extends PluginManagerInterface, CachedDiscoveryInterface {
+
+  /**
+   * Creates a new access control handler instance.
+   *
+   * @param string $entity_type
+   *   The entity type for this access control handler.
+   *
+   * @return \Drupal\Core\Entity\EntityAccessControlHandlerInterface.
+   *   A access control handler instance.
+   */
+  public function getAccessControlHandler($entity_type);
+
+  /**
+   * Creates a new storage instance.
+   *
+   * @param string $entity_type
+   *   The entity type for this storage.
+   *
+   * @return \Drupal\Core\Entity\EntityStorageInterface
+   *   A storage instance.
+   *
+   * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
+   */
+  public function getStorage($entity_type);
+
+  /**
+   * Creates a new view builder instance.
+   *
+   * @param string $entity_type
+   *   The entity type for this view builder.
+   *
+   * @return \Drupal\Core\Entity\EntityViewBuilderInterface.
+   *   A view builder instance.
+   */
+  public function getViewBuilder($entity_type);
+
+  /**
+   * Creates a new entity list builder.
+   *
+   * @param string $entity_type
+   *   The entity type for this list builder.
+   *
+   * @return \Drupal\Core\Entity\EntityListBuilderInterface
+   *   An entity list builder instance.
+   */
+  public function getListBuilder($entity_type);
+
+  /**
+   * Creates a new form instance.
+   *
+   * @param string $entity_type
+   *   The entity type for this form.
+   * @param string $operation
+   *   The name of the operation to use, e.g., 'default'.
+   *
+   * @return \Drupal\Core\Entity\EntityFormInterface
+   *   A form instance.
+   */
+  public function getFormObject($entity_type, $operation);
+
+  /**
+   * Gets all route provider instances.
+   *
+   * @param string $entity_type
+   *   The entity type for this route providers.
+   *
+   * @return \Drupal\Core\Entity\Routing\EntityRouteProviderInterface[]
+   */
+  public function getRouteProviders($entity_type);
+
+  /**
+   * Checks whether a certain entity type has a certain handler.
+   *
+   * @param string $entity_type
+   *   The name of the entity type.
+   * @param string $handler_type
+   *   The name of the handler.
+   *
+   * @return bool
+   *   Returns TRUE if the entity type has the handler, else FALSE.
+   */
+  public function hasHandler($entity_type, $handler_type);
+
+  /**
+   * Creates a new handler instance for a entity type and handler type.
+   *
+   * @param string $entity_type
+   *   The entity type for this controller.
+   * @param string $handler_type
+   *   The controller type to create an instance for.
+   *
+   * @return object
+   *   A handler instance.
+   *
+   * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
+   */
+  public function getHandler($entity_type, $handler_type);
+
+  /**
+   * Creates new handler instance.
+   *
+   * Usually \Drupal\Core\Entity\EntityManagerInterface::getHandler() is
+   * preferred since that method has additional checking that the class exists
+   * and has static caches.
+   *
+   * @param mixed $class
+   *   The handler class to instantiate.
+   * @param \Drupal\Core\Entity\EntityTypeInterface $definition
+   *   The entity type definition.
+   *
+   * @return object
+   *   A handler instance.
+   */
+  public function createHandlerInstance($class, EntityTypeInterface $definition = NULL);
+
+  /**
+   * {@inheritdoc}
+   *
+   * @return \Drupal\Core\Entity\EntityTypeInterface|null
+   */
+  public function getDefinition($entity_type_id, $exception_on_invalid = TRUE);
+
+  /**
+   * {@inheritdoc}
+   *
+   * @return \Drupal\Core\Entity\EntityTypeInterface[]
+   */
+  public function getDefinitions();
+
+}
diff --git a/core/lib/Drupal/Core/Plugin/DefaultPluginManager.php b/core/lib/Drupal/Core/Plugin/DefaultPluginManager.php
index 7c8799a..0415bc8 100644
--- a/core/lib/Drupal/Core/Plugin/DefaultPluginManager.php
+++ b/core/lib/Drupal/Core/Plugin/DefaultPluginManager.php
@@ -8,13 +8,13 @@
 namespace Drupal\Core\Plugin;
 
 use Drupal\Component\Plugin\Discovery\CachedDiscoveryInterface;
+use Drupal\Core\Plugin\Discovery\CacheBackendTrait;
 use Drupal\Component\Plugin\Discovery\DiscoveryCachedTrait;
 use Drupal\Core\Plugin\Discovery\ContainerDerivativeDiscoveryDecorator;
 use Drupal\Component\Plugin\PluginManagerBase;
 use Drupal\Component\Plugin\PluginManagerInterface;
 use Drupal\Component\Utility\NestedArray;
 use Drupal\Core\Cache\Cache;
-use Drupal\Core\Cache\CacheBackendInterface;
 use Drupal\Core\Extension\ModuleHandlerInterface;
 use Drupal\Core\Plugin\Discovery\AnnotatedClassDiscovery;
 use Drupal\Core\Plugin\Factory\ContainerFactory;
@@ -27,27 +27,7 @@
 class DefaultPluginManager extends PluginManagerBase implements PluginManagerInterface, CachedDiscoveryInterface {
 
   use DiscoveryCachedTrait;
-
-  /**
-   * Cache backend instance.
-   *
-   * @var \Drupal\Core\Cache\CacheBackendInterface
-   */
-  protected $cacheBackend;
-
-  /**
-   * The cache key.
-   *
-   * @var string
-   */
-  protected $cacheKey;
-
-  /**
-   * An array of cache tags to use for the cached definitions.
-   *
-   * @var array
-   */
-  protected $cacheTags = array();
+  use CacheBackendTrait;
 
   /**
    * Name of the alter hook if one should be invoked.
@@ -81,13 +61,6 @@ class DefaultPluginManager extends PluginManagerBase implements PluginManagerInt
   protected $defaults = array();
 
   /**
-   * Flag whether persistent caches should be used.
-   *
-   * @var bool
-   */
-  protected $useCaches = TRUE;
-
-  /**
    * The name of the annotation that contains the plugin definition.
    *
    * @var string
@@ -134,33 +107,6 @@ public function __construct($subdir, \Traversable $namespaces, ModuleHandlerInte
   }
 
   /**
-   * Initialize the cache backend.
-   *
-   * Plugin definitions are cached using the provided cache backend. The
-   * interface language is added as a suffix to the cache key.
-   *
-   * @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend
-   *   Cache backend instance to use.
-   * @param string $cache_key
-   *   Cache key prefix to use, the language code will be appended
-   *   automatically.
-   * @param array $cache_tags
-   *   (optional) When providing a list of cache tags, the cached plugin
-   *   definitions are tagged with the provided cache tags. These cache tags can
-   *   then be used to clear the corresponding cached plugin definitions. Note
-   *   that this should be used with care! For clearing all cached plugin
-   *   definitions of a plugin manager, call that plugin manager's
-   *   clearCachedDefinitions() method. Only use cache tags when cached plugin
-   *   definitions should be cleared along with other, related cache entries.
-   */
-  public function setCacheBackend(CacheBackendInterface $cache_backend, $cache_key, array $cache_tags = array()) {
-    Cache::validateTags($cache_tags);
-    $this->cacheBackend = $cache_backend;
-    $this->cacheKey = $cache_key;
-    $this->cacheTags = $cache_tags;
-  }
-
-  /**
    * Initializes the alter hook.
    *
    * @param string $alter_hook
@@ -187,15 +133,7 @@ public function getDefinitions() {
    * {@inheritdoc}
    */
   public function clearCachedDefinitions() {
-    if ($this->cacheBackend) {
-      if ($this->cacheTags) {
-        // Use the cache tags to clear the cache.
-        Cache::invalidateTags($this->cacheTags);
-      }
-      else {
-        $this->cacheBackend->delete($this->cacheKey);
-      }
-    }
+    $this->cacheClear();
     $this->definitions = NULL;
   }
 
@@ -237,30 +175,6 @@ public function useCaches($use_caches = FALSE) {
   }
 
   /**
-   * Fetches from the cache backend, respecting the use caches flag.
-   *
-   * @see \Drupal\Core\Cache\CacheBackendInterface::get()
-   */
-  protected function cacheGet($cid) {
-    if ($this->useCaches && $this->cacheBackend) {
-      return $this->cacheBackend->get($cid);
-    }
-    return FALSE;
-  }
-
-  /**
-   * Stores data in the persistent cache, respecting the use caches flag.
-   *
-   * @see \Drupal\Core\Cache\CacheBackendInterface::set()
-   */
-  protected function cacheSet($cid, $data, $expire = Cache::PERMANENT, array $tags = array()) {
-    if ($this->cacheBackend && $this->useCaches) {
-      $this->cacheBackend->set($cid, $data, $expire, $tags);
-    }
-  }
-
-
-  /**
    * Performs extra processing on plugin definitions.
    *
    * By default we add defaults for the type to the definition. If a type has
diff --git a/core/lib/Drupal/Core/Plugin/Discovery/CacheBackendTrait.php b/core/lib/Drupal/Core/Plugin/Discovery/CacheBackendTrait.php
new file mode 100644
index 0000000..4c10b6f
--- /dev/null
+++ b/core/lib/Drupal/Core/Plugin/Discovery/CacheBackendTrait.php
@@ -0,0 +1,114 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Plugin\Discovery\CacheBackendTrait.
+ */
+
+namespace Drupal\Core\Plugin\Discovery;
+
+use Drupal\Core\Cache\Cache;
+use Drupal\Core\Cache\CacheBackendInterface;
+
+/**
+ * @todo.
+ */
+trait CacheBackendTrait {
+
+  /**
+   * Cache backend instance.
+   *
+   * @var \Drupal\Core\Cache\CacheBackendInterface
+   */
+  protected $cacheBackend;
+
+  /**
+   * The cache key.
+   *
+   * @var string
+   */
+  protected $cacheKey;
+
+  /**
+   * An array of cache tags to use for the cached definitions.
+   *
+   * @var array
+   */
+  protected $cacheTags = [];
+
+  /**
+   * Flag whether persistent caches should be used.
+   *
+   * @var bool
+   */
+  protected $useCaches = TRUE;
+
+  /**
+   * {@inheritdoc}
+   */
+  public function useCaches($use_caches = FALSE) {
+    $this->useCaches = $use_caches;
+  }
+
+  /**
+   * Initialize the cache backend.
+   *
+   * Plugin definitions are cached using the provided cache backend. The
+   * interface language is added as a suffix to the cache key.
+   *
+   * @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend
+   *   Cache backend instance to use.
+   * @param string $cache_key
+   *   Cache key prefix to use, the language code will be appended
+   *   automatically.
+   * @param array $cache_tags
+   *   (optional) When providing a list of cache tags, the cached plugin
+   *   definitions are tagged with the provided cache tags. These cache tags can
+   *   then be used to clear the corresponding cached plugin definitions. Note
+   *   that this should be used with care! For clearing all cached plugin
+   *   definitions of a plugin manager, call that plugin manager's
+   *   clearCachedDefinitions() method. Only use cache tags when cached plugin
+   *   definitions should be cleared along with other, related cache entries.
+   */
+  public function setCacheBackend(CacheBackendInterface $cache_backend, $cache_key, array $cache_tags = []) {
+    Cache::validateTags($cache_tags);
+    $this->cacheBackend = $cache_backend;
+    $this->cacheKey = $cache_key;
+    $this->cacheTags = $cache_tags;
+  }
+
+  /**
+   * Fetches from the cache backend, respecting the use caches flag.
+   *
+   * @see \Drupal\Core\Cache\CacheBackendInterface::get()
+   */
+  protected function cacheGet($cid) {
+    if ($this->useCaches && $this->cacheBackend) {
+      return $this->cacheBackend->get($cid);
+    }
+    return FALSE;
+  }
+
+  /**
+   * Stores data in the persistent cache, respecting the use caches flag.
+   *
+   * @see \Drupal\Core\Cache\CacheBackendInterface::set()
+   */
+  protected function cacheSet($cid, $data, $expire = Cache::PERMANENT, array $tags = []) {
+    if ($this->cacheBackend && $this->useCaches) {
+      $this->cacheBackend->set($cid, $data, $expire, $tags);
+    }
+  }
+  protected function cacheClear() {
+    if ($this->cacheBackend) {
+      if ($this->cacheTags) {
+        // Use the cache tags to clear the cache.
+        Cache::invalidateTags($this->cacheTags);
+      }
+      else {
+        $this->cacheBackend->delete($this->cacheKey);
+      }
+    }
+  }
+
+}
diff --git a/core/tests/Drupal/Tests/Core/Entity/EntityManagerTest.php b/core/tests/Drupal/Tests/Core/Entity/EntityManagerTest.php
index a7f1c5e..bb364a1 100644
--- a/core/tests/Drupal/Tests/Core/Entity/EntityManagerTest.php
+++ b/core/tests/Drupal/Tests/Core/Entity/EntityManagerTest.php
@@ -5,23 +5,38 @@
  * Contains \Drupal\Tests\Core\Entity\EntityManagerTest.
  */
 
-namespace Drupal\Tests\Core\Entity {
+namespace Drupal\Tests\Core\Entity;
 
-use Drupal\Component\Plugin\Discovery\DiscoveryInterface;
 use Drupal\Component\Plugin\Exception\PluginNotFoundException;
-use Drupal\Core\Config\Entity\ConfigEntityStorage;
-use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
+use Drupal\Core\Cache\Cache;
+use Drupal\Core\Cache\CacheBackendInterface;
+use Drupal\Core\Cache\CacheTagsInvalidatorInterface;
+use Drupal\Core\Config\Entity\ConfigEntityStorageInterface;
 use Drupal\Core\Entity\ContentEntityInterface;
-use Drupal\Core\Entity\EntityHandlerBase;
-use Drupal\Core\Entity\EntityHandlerInterface;
+use Drupal\Core\Entity\ContentEntityTypeInterface;
+use Drupal\Core\Entity\DynamicallyFieldableEntityStorageInterface;
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Entity\EntityManager;
-use Drupal\Core\Entity\EntityManagerInterface;
 use Drupal\Core\Entity\EntityTypeInterface;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\Core\Entity\FieldableEntityInterface;
+use Drupal\Core\Extension\ModuleHandlerInterface;
+use Drupal\Core\Field\BaseFieldDefinition;
+use Drupal\Core\Field\FieldDefinitionInterface;
+use Drupal\Core\Field\FieldStorageDefinitionInterface;
+use Drupal\Core\Field\FieldTypePluginManagerInterface;
+use Drupal\Core\Field\Plugin\Field\FieldType\BooleanItem;
+use Drupal\Core\KeyValueStore\KeyValueFactoryInterface;
+use Drupal\Core\KeyValueStore\KeyValueStoreInterface;
 use Drupal\Core\Language\Language;
 use Drupal\Core\Language\LanguageInterface;
+use Drupal\Core\Language\LanguageManagerInterface;
+use Drupal\Core\StringTranslation\TranslationInterface;
+use Drupal\Core\TypedData\TypedDataManager;
 use Drupal\Tests\UnitTestCase;
+use Prophecy\Argument;
 use Symfony\Component\DependencyInjection\ContainerInterface;
+use Symfony\Component\EventDispatcher\EventDispatcherInterface;
 
 /**
  * @coversDefaultClass \Drupal\Core\Entity\EntityManager
@@ -39,100 +54,72 @@ class EntityManagerTest extends UnitTestCase {
   /**
    * The entity type definition.
    *
-   * @var \Drupal\Core\Entity\EntityTypeInterface|\PHPUnit_Framework_MockObject_MockObject
+   * @var \Drupal\Core\Entity\EntityTypeInterface|\Prophecy\Prophecy\ProphecyInterface
    */
   protected $entityType;
 
   /**
-   * An instance of the test entity.
-   *
-   * @var \Drupal\Tests\Core\Entity\EntityManagerTestEntity|\PHPUnit_Framework_MockObject_MockObject
-   */
-  protected $entity;
-
-  /**
-   * The plugin discovery.
-   *
-   * @var \Drupal\Component\Plugin\Discovery\DiscoveryInterface|\PHPUnit_Framework_MockObject_MockObject
-   */
-  protected $discovery;
-
-  /**
    * The dependency injection container.
    *
-   * @var \Symfony\Component\DependencyInjection\ContainerInterface|\PHPUnit_Framework_MockObject_MockObject
+   * @var \Symfony\Component\DependencyInjection\ContainerInterface|\Prophecy\Prophecy\ProphecyInterface
    */
   protected $container;
 
   /**
    * The module handler.
    *
-   * @var \Drupal\Core\Extension\ModuleHandlerInterface|\PHPUnit_Framework_MockObject_MockObject
+   * @var \Drupal\Core\Extension\ModuleHandlerInterface|\Prophecy\Prophecy\ProphecyInterface
    */
   protected $moduleHandler;
 
   /**
    * The cache backend to use.
    *
-   * @var \Drupal\Core\Cache\CacheBackendInterface|\PHPUnit_Framework_MockObject_MockObject
+   * @var \Drupal\Core\Cache\CacheBackendInterface|\Prophecy\Prophecy\ProphecyInterface
    */
   protected $cacheBackend;
 
   /**
    * The cache tags invalidator.
    *
-   * @var \Drupal\Core\Cache\CacheTagsInvalidatorInterface|\PHPUnit_Framework_MockObject_MockObject
+   * @var \Drupal\Core\Cache\CacheTagsInvalidatorInterface|\Prophecy\Prophecy\ProphecyInterface
    */
   protected $cacheTagsInvalidator;
 
   /**
    * The language manager.
    *
-   * @var \Drupal\Core\Language\LanguageManagerInterface|\PHPUnit_Framework_MockObject_MockObject
+   * @var \Drupal\Core\Language\LanguageManagerInterface|\Prophecy\Prophecy\ProphecyInterface
    */
   protected $languageManager;
 
   /**
-   * The string translationManager.
-   *
-   * @var \Drupal\Core\StringTranslation\TranslationInterface|\PHPUnit_Framework_MockObject_MockObject
-   */
-  protected $translationManager;
-
-  /**
-   * The controller resolver.
-   *
-   * @var \Drupal\Core\Controller\ControllerResolverInterface|\PHPUnit_Framework_MockObject_MockObject
-   */
-  protected $controllerResolver;
-
-  /**
    * The typed data manager.
    *
-   * @var \Drupal\Core\TypedData\TypedDataManager|\PHPUnit_Framework_MockObject_MockObject
+   * @var \Drupal\Core\TypedData\TypedDataManager|\Prophecy\Prophecy\ProphecyInterface
    */
   protected $typedDataManager;
 
   /**
    * The keyvalue factory.
    *
-   * @var \Drupal\Core\KeyValueStore\KeyValueFactoryInterface|\PHPUnit_Framework_MockObject_MockObject
+   * @var \Drupal\Core\KeyValueStore\KeyValueFactoryInterface|\Prophecy\Prophecy\ProphecyInterface
    */
   protected $keyValueFactory;
 
   /**
    * The event dispatcher.
    *
-   * @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
+   * @var \Symfony\Component\EventDispatcher\EventDispatcherInterface|\Prophecy\Prophecy\ProphecyInterface
    */
   protected $eventDispatcher;
 
   /**
-   * The form builder.
+   * The entity type manager.
    *
-   * @var \Drupal\Core\Form\FormBuilderInterface|\PHPUnit_Framework_MockObject_MockObject
+   * @var \Drupal\Core\Entity\EntityTypeManagerInterface|\Prophecy\Prophecy\ProphecyInterface
    */
-  protected $formBuilder;
+  protected $entityTypeManager;
 
   /**
    * {@inheritdoc}
@@ -140,93 +127,61 @@ class EntityManagerTest extends UnitTestCase {
   protected function setUp() {
     parent::setUp();
 
-    $this->moduleHandler = $this->getMock('Drupal\Core\Extension\ModuleHandlerInterface');
-    $this->moduleHandler->expects($this->any())
-      ->method('getImplementations')
-      ->with('entity_type_build')
-      ->will($this->returnValue(array()));
+    $this->moduleHandler = $this->prophesize(ModuleHandlerInterface::class);
+    $this->moduleHandler->alter('entity_base_field_info', Argument::type('array'), Argument::any())->willReturn(NULL);
+    $this->moduleHandler->alter('entity_bundle_field_info', Argument::type('array'), Argument::any(), Argument::type('string'))->willReturn(NULL);
 
-    $this->cacheBackend = $this->getMock('Drupal\Core\Cache\CacheBackendInterface');
-    $this->cacheTagsInvalidator = $this->getMock('Drupal\Core\Cache\CacheTagsInvalidatorInterface');
+    $this->cacheBackend = $this->prophesize(CacheBackendInterface::class);
+    $this->cacheTagsInvalidator = $this->prophesize(CacheTagsInvalidatorInterface::class);
 
-    $language = $this->getMock('Drupal\Core\Language\LanguageInterface');
-    $language->expects($this->any())
-      ->method('getId')
-      ->will($this->returnValue('en'));
-    $this->languageManager = $this->getMock('Drupal\Core\Language\LanguageManagerInterface');
+    $language = new Language(['id' => 'en']);
+    $this->languageManager = $this->prophesize(LanguageManagerInterface::class);
+    $this->languageManager->getCurrentLanguage()->willReturn($language);
+    $this->languageManager->getLanguages()->willReturn(['en' => (object) ['id' => 'en']]);
 
-    $this->languageManager->expects($this->any())
-      ->method('getCurrentLanguage')
-      ->will($this->returnValue($language));
-    $this->languageManager->expects($this->any())
-      ->method('getLanguages')
-      ->will($this->returnValue(array('en' => (object) array('id' => 'en'))));
+    $this->typedDataManager = $this->prophesize(TypedDataManager::class);
+    $this->typedDataManager->getDefinition('field_item:boolean')->willReturn([
+      'class' => BooleanItem::class,
+    ]);
 
-    $this->translationManager = $this->getStringTranslationStub();
+    $this->eventDispatcher = $this->prophesize(EventDispatcherInterface::class);
 
-    $this->formBuilder = $this->getMock('Drupal\Core\Form\FormBuilderInterface');
-    $this->controllerResolver = $this->getClassResolverStub();
+    $this->keyValueFactory = $this->prophesize(KeyValueFactoryInterface::class);
 
-    $this->discovery = $this->getMock('Drupal\Component\Plugin\Discovery\DiscoveryInterface');
+    $this->container = $this->prophesize(ContainerInterface::class);
+    $this->container->get('cache_tags.invalidator')->willReturn($this->cacheTagsInvalidator->reveal());
+    $this->container->get('typed_data_manager')->willReturn($this->typedDataManager->reveal());
+    \Drupal::setContainer($this->container->reveal());
 
-    $this->typedDataManager = $this->getMockBuilder('\Drupal\Core\TypedData\TypedDataManager')
-      ->disableOriginalConstructor()
-      ->getMock();
+    $this->entityTypeManager = $this->prophesize(EntityTypeManagerInterface::class);
 
-    $map = [
-      ['field_item:boolean', TRUE, ['class' => 'Drupal\Core\Field\Plugin\Field\FieldType\BooleanItem']],
-    ];
-
-    $this->typedDataManager->expects($this->any())
-      ->method('getDefinition')
-      ->willReturnMap($map);
-
-    $this->eventDispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface');
-
-    $this->keyValueFactory = $this->getMock('Drupal\Core\KeyValueStore\KeyValueFactoryInterface');
-
-    $this->container = $this->getContainerWithCacheTagsInvalidator($this->cacheTagsInvalidator);
-    $this->container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface');
-    \Drupal::setContainer($this->container);
-
-    $field_type_manager = $this->getMock('Drupal\Core\Field\FieldTypePluginManagerInterface');
-    $field_type_manager->expects($this->any())
-      ->method('getDefaultStorageSettings')
-      ->willReturn(array());
-    $field_type_manager->expects($this->any())
-      ->method('getDefaultFieldSettings')
-      ->willReturn(array());
-
-    $string_translation = $this->getMock('Drupal\Core\StringTranslation\TranslationInterface');
-
-    $map = [
-      ['cache_tags.invalidator', 1, $this->cacheTagsInvalidator],
-      ['plugin.manager.field.field_type', 1, $field_type_manager],
-      ['string_translation', 1, $string_translation],
-      ['typed_data_manager', 1, $this->typedDataManager],
-    ];
-
-    $this->container->expects($this->any())
-      ->method('get')
-      ->willReturnMap($map);
+    $this->entityManager = new TestEntityManager($this->moduleHandler->reveal(), $this->cacheBackend->reveal(), $this->languageManager->reveal(), $this->typedDataManager->reveal(), $this->keyValueFactory->reveal(), $this->eventDispatcher->reveal(), $this->entityTypeManager->reveal());
   }
 
   /**
    * Sets up the entity manager to be tested.
    *
-   * @param \Drupal\Core\Entity\EntityTypeInterface[]|\PHPUnit_Framework_MockObject_MockObject[] $definitions
+   * @param \Drupal\Core\Entity\EntityTypeInterface[]|\Prophecy\Prophecy\ProphecyInterface[] $definitions
    *   (optional) An array of entity type definitions.
    */
   protected function setUpEntityManager($definitions = array()) {
-    $class = $this->getMockClass('Drupal\Core\Entity\EntityInterface');
-    foreach ($definitions as $entity_type) {
-      $entity_type->expects($this->any())
-        ->method('getClass')
-        ->will($this->returnValue($class));
+    $class = $this->getMockClass(EntityInterface::class);
+    foreach ($definitions as $key => $entity_type) {
+      // \Drupal\Core\Entity\EntityTypeInterface::getLinkTemplates() is called
+      // by \Drupal\Core\Entity\EntityManager::processDefinition() so it must
+      // always be mocked.
+      $entity_type->getLinkTemplates()->willReturn([]);
+
+      // Give the entity type a legitimate class to return.
+      $entity_type->getClass()->willReturn($class);
+
+      $definitions[$key] = $entity_type->reveal();
     }
-    $this->discovery->expects($this->any())
-      ->method('getDefinition')
-      ->will($this->returnCallback(function ($entity_type_id, $exception_on_invalid = FALSE) use ($definitions) {
+
+    $this->entityTypeManager->getDefinition(Argument::cetera())
+      ->will(function ($args) use ($definitions) {
+        $entity_type_id = $args[0];
+        $exception_on_invalid = $args[1];
         if (isset($definitions[$entity_type_id])) {
           return $definitions[$entity_type_id];
         }
@@ -234,14 +189,8 @@ protected function setUpEntityManager($definitions = array()) {
           return NULL;
         }
         else throw new PluginNotFoundException($entity_type_id);
-      }));
-    $this->discovery->expects($this->any())
-      ->method('getDefinitions')
-      ->will($this->returnValue($definitions));
-
-    $this->entityManager = new TestEntityManager(new \ArrayObject(), $this->moduleHandler, $this->cacheBackend, $this->languageManager, $this->translationManager, $this->getClassResolverStub(), $this->typedDataManager, $this->keyValueFactory, $this->eventDispatcher);
-    $this->entityManager->setContainer($this->container);
-    $this->entityManager->setDiscovery($this->discovery);
+    });
+    $this->entityTypeManager->getDefinitions()->willReturn($definitions);
   }
 
   /**
@@ -252,288 +201,18 @@ protected function setUpEntityManager($definitions = array()) {
    */
   public function testClearCachedDefinitions() {
     $this->setUpEntityManager();
-    $this->cacheTagsInvalidator->expects($this->at(0))
-      ->method('invalidateTags')
-      ->with(array('entity_types'));
-    $this->cacheTagsInvalidator->expects($this->at(1))
-      ->method('invalidateTags')
-      ->with(array('entity_bundles'));
-    $this->cacheTagsInvalidator->expects($this->at(2))
-      ->method('invalidateTags')
-      ->with(array('entity_field_info'));
+
+    $this->typedDataManager->clearCachedDefinitions()->shouldBeCalled();
+    $this->entityTypeManager->clearCachedDefinitions()->shouldBeCalled();
+
+    $this->cacheTagsInvalidator->invalidateTags(['entity_types'])->shouldBeCalled();
+    $this->cacheTagsInvalidator->invalidateTags(['entity_bundles'])->shouldBeCalled();
+    $this->cacheTagsInvalidator->invalidateTags(['entity_field_info'])->shouldBeCalled();
 
     $this->entityManager->clearCachedDefinitions();
   }
 
   /**
-   * Tests the processDefinition() method.
-   *
-   * @covers ::processDefinition
-   *
-   * @expectedException \Drupal\Core\Entity\Exception\InvalidLinkTemplateException
-   * @expectedExceptionMessage Link template 'canonical' for entity type 'apple' must start with a leading slash, the current link template is 'path/to/apple'
-   */
-  public function testProcessDefinition() {
-    $apple = $this->getMock('Drupal\Core\Entity\EntityTypeInterface');
-    $apple->expects($this->once())
-      ->method('getLinkTemplates')
-      ->willReturn(['canonical' => 'path/to/apple']);
-
-    $this->setUpEntityManager(array('apple' => $apple));
-
-    $this->entityManager->processDefinition($apple, 'apple');
-  }
-
-  /**
-   * Tests the getDefinition() method.
-   *
-   * @covers ::getDefinition
-   *
-   * @dataProvider providerTestGetDefinition
-   */
-  public function testGetDefinition($entity_type_id, $expected) {
-    $entity = $this->getMock('Drupal\Core\Entity\EntityTypeInterface');
-    $this->setUpEntityManager(array(
-      'apple' => $entity,
-      'banana' => $entity,
-    ));
-
-    $entity_type = $this->entityManager->getDefinition($entity_type_id, FALSE);
-    if ($expected) {
-      $this->assertInstanceOf('Drupal\Core\Entity\EntityTypeInterface', $entity_type);
-    }
-    else {
-      $this->assertNull($entity_type);
-    }
-  }
-
-  /**
-   * Provides test data for testGetDefinition().
-   *
-   * @return array
-   *   Test data.
-   */
-  public function providerTestGetDefinition() {
-    return array(
-      array('apple', TRUE),
-      array('banana', TRUE),
-      array('pear', FALSE),
-    );
-  }
-
-  /**
-   * Tests the getDefinition() method with an invalid definition.
-   *
-   * @covers ::getDefinition
-   *
-   * @expectedException \Drupal\Component\Plugin\Exception\PluginNotFoundException
-   * @expectedExceptionMessage The "pear" entity type does not exist.
-   */
-  public function testGetDefinitionInvalidException() {
-    $this->setUpEntityManager();
-
-    $this->entityManager->getDefinition('pear', TRUE);
-  }
-
-  /**
-   * Tests the hasHandler() method.
-   *
-   * @covers ::hasHandler
-   *
-   * @dataProvider providerTestHasHandler
-   */
-  public function testHasHandler($entity_type_id, $expected) {
-    $apple = $this->getMock('Drupal\Core\Entity\EntityTypeInterface');
-    $apple->expects($this->any())
-      ->method('hasHandlerClass')
-      ->with('storage')
-      ->will($this->returnValue(TRUE));
-    $banana = $this->getMock('Drupal\Core\Entity\EntityTypeInterface');
-    $banana->expects($this->any())
-      ->method('hasHandlerClass')
-      ->with('storage')
-      ->will($this->returnValue(FALSE));
-    $this->setUpEntityManager(array(
-      'apple' => $apple,
-      'banana' => $banana,
-    ));
-
-    $entity_type = $this->entityManager->hasHandler($entity_type_id, 'storage');
-    $this->assertSame($expected, $entity_type);
-  }
-
-  /**
-   * Provides test data for testHasHandler().
-   *
-   * @return array
-   *   Test data.
-   */
-  public function providerTestHasHandler() {
-    return array(
-      array('apple', TRUE),
-      array('banana', FALSE),
-      array('pear', FALSE),
-    );
-  }
-
-  /**
-   * Tests the getStorage() method.
-   *
-   * @covers ::getStorage
-   */
-  public function testGetStorage() {
-    $class = $this->getTestHandlerClass();
-    $entity = $this->getMock('Drupal\Core\Entity\EntityTypeInterface');
-    $entity->expects($this->once())
-      ->method('getHandlerClass')
-      ->with('storage')
-      ->will($this->returnValue($class));
-    $this->setUpEntityManager(array('test_entity_type' => $entity));
-
-    $this->assertInstanceOf($class, $this->entityManager->getStorage('test_entity_type'));
-  }
-
-  /**
-   * Tests the getListBuilder() method.
-   *
-   * @covers ::getListBuilder
-   */
-  public function testGetListBuilder() {
-    $class = $this->getTestHandlerClass();
-    $entity = $this->getMock('Drupal\Core\Entity\EntityTypeInterface');
-    $entity->expects($this->once())
-      ->method('getHandlerClass')
-      ->with('list_builder')
-      ->will($this->returnValue($class));
-    $this->setUpEntityManager(array('test_entity_type' => $entity));
-
-    $this->assertInstanceOf($class, $this->entityManager->getListBuilder('test_entity_type'));
-  }
-
-  /**
-   * Tests the getViewBuilder() method.
-   *
-   * @covers ::getViewBuilder
-   */
-  public function testGetViewBuilder() {
-    $class = $this->getTestHandlerClass();
-    $entity = $this->getMock('Drupal\Core\Entity\EntityTypeInterface');
-    $entity->expects($this->once())
-      ->method('getHandlerClass')
-      ->with('view_builder')
-      ->will($this->returnValue($class));
-    $this->setUpEntityManager(array('test_entity_type' => $entity));
-
-    $this->assertInstanceOf($class, $this->entityManager->getViewBuilder('test_entity_type'));
-  }
-
-  /**
-   * Tests the getAccessControlHandler() method.
-   *
-   * @covers ::getAccessControlHandler
-   */
-  public function testGetAccessControlHandler() {
-    $class = $this->getTestHandlerClass();
-    $entity = $this->getMock('Drupal\Core\Entity\EntityTypeInterface');
-    $entity->expects($this->once())
-      ->method('getHandlerClass')
-      ->with('access')
-      ->will($this->returnValue($class));
-    $this->setUpEntityManager(array('test_entity_type' => $entity));
-
-    $this->assertInstanceOf($class, $this->entityManager->getAccessControlHandler('test_entity_type'));
-  }
-
-  /**
-   * Tests the getFormObject() method.
-   *
-   * @covers ::getFormObject
-   */
-  public function testGetFormObject() {
-    $apple = $this->getMock('Drupal\Core\Entity\EntityTypeInterface');
-    $apple->expects($this->once())
-      ->method('getFormClass')
-      ->with('default')
-      ->will($this->returnValue('Drupal\Tests\Core\Entity\TestEntityForm'));
-    $banana = $this->getMock('Drupal\Core\Entity\EntityTypeInterface');
-    $banana->expects($this->once())
-      ->method('getFormClass')
-      ->with('default')
-      ->will($this->returnValue('Drupal\Tests\Core\Entity\TestEntityFormInjected'));
-    $this->setUpEntityManager(array(
-      'apple' => $apple,
-      'banana' => $banana,
-    ));
-
-    $apple_form = $this->entityManager->getFormObject('apple', 'default');
-    $this->assertInstanceOf('Drupal\Tests\Core\Entity\TestEntityForm', $apple_form);
-    $this->assertAttributeInstanceOf('Drupal\Core\Extension\ModuleHandlerInterface', 'moduleHandler', $apple_form);
-    $this->assertAttributeInstanceOf('Drupal\Core\StringTranslation\TranslationInterface', 'stringTranslation', $apple_form);
-
-    $banana_form = $this->entityManager->getFormObject('banana', 'default');
-    $this->assertInstanceOf('Drupal\Tests\Core\Entity\TestEntityFormInjected', $banana_form);
-    $this->assertAttributeEquals('yellow', 'color', $banana_form);
-
-  }
-
-  /**
-   * Tests the getFormObject() method with an invalid operation.
-   *
-   * @covers ::getFormObject
-   *
-   * @expectedException \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
-   */
-  public function testGetFormObjectInvalidOperation() {
-    $entity = $this->getMock('Drupal\Core\Entity\EntityTypeInterface');
-    $entity->expects($this->once())
-      ->method('getFormClass')
-      ->with('edit')
-      ->will($this->returnValue(''));
-    $this->setUpEntityManager(array('test_entity_type' => $entity));
-
-    $this->entityManager->getFormObject('test_entity_type', 'edit');
-  }
-
-  /**
-   * Tests the getHandler() method.
-   *
-   * @covers ::getHandler
-   */
-  public function testGetHandler() {
-    $class = $this->getTestHandlerClass();
-    $apple = $this->getMock('Drupal\Core\Entity\EntityTypeInterface');
-    $apple->expects($this->once())
-      ->method('getHandlerClass')
-      ->with('storage')
-      ->will($this->returnValue($class));
-    $this->setUpEntityManager(array(
-      'apple' => $apple,
-    ));
-
-    $apple_controller = $this->entityManager->getHandler('apple', 'storage');
-    $this->assertInstanceOf($class, $apple_controller);
-    $this->assertAttributeInstanceOf('Drupal\Core\Extension\ModuleHandlerInterface', 'moduleHandler', $apple_controller);
-    $this->assertAttributeInstanceOf('Drupal\Core\StringTranslation\TranslationInterface', 'stringTranslation', $apple_controller);
-  }
-
-  /**
-   * Tests the getHandler() method when no controller is defined.
-   *
-   * @covers ::getHandler
-   *
-   * @expectedException \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
-   */
-  public function testGetHandlerMissingHandler() {
-    $entity = $this->getMock('Drupal\Core\Entity\EntityTypeInterface');
-    $entity->expects($this->once())
-      ->method('getHandlerClass')
-      ->with('storage')
-      ->will($this->returnValue(''));
-    $this->setUpEntityManager(array('test_entity_type' => $entity));
-    $this->entityManager->getHandler('test_entity_type', 'storage');
-  }
-
-  /**
    * Tests the getBaseFieldDefinitions() method.
    *
    * @covers ::getBaseFieldDefinitions
@@ -567,27 +246,19 @@ public function testGetFieldDefinitions() {
    */
   public function testGetFieldStorageDefinitions() {
     $field_definition = $this->setUpEntityWithFieldDefinition(TRUE);
-    $field_storage_definition = $this->getMock('\Drupal\Core\Field\FieldStorageDefinitionInterface');
-    $field_storage_definition->expects($this->any())
-      ->method('getName')
-      ->will($this->returnValue('field_storage'));
+    $field_storage_definition = $this->prophesize(FieldStorageDefinitionInterface::class);
+    $field_storage_definition->getName()->willReturn('field_storage');
 
-    $this->moduleHandler->expects($this->any())
-      ->method('getImplementations')
-      ->will($this->returnValueMap(array(
-        array('entity_type_build', array()),
-        array('entity_base_field_info', array()),
-        array('entity_field_storage_info', array('example_module')),
-      )));
+    $definitions = ['field_storage' => $field_storage_definition->reveal()];
 
-    $this->moduleHandler->expects($this->any())
-      ->method('invoke')
-      ->with('example_module', 'entity_field_storage_info')
-      ->will($this->returnValue(array('field_storage' => $field_storage_definition)));
+    $this->moduleHandler->getImplementations('entity_base_field_info')->willReturn([]);
+    $this->moduleHandler->getImplementations('entity_field_storage_info')->willReturn(['example_module']);
+    $this->moduleHandler->invoke('example_module', 'entity_field_storage_info', [$this->entityType])->willReturn($definitions);
+    $this->moduleHandler->alter('entity_field_storage_info', $definitions, $this->entityType)->willReturn(NULL);
 
     $expected = array(
       'id' => $field_definition,
-      'field_storage' => $field_storage_definition,
+      'field_storage' => $field_storage_definition->reveal(),
     );
     $this->assertSame($expected, $this->entityManager->getFieldStorageDefinitions('test_entity_type'));
   }
@@ -603,19 +274,13 @@ public function testGetFieldStorageDefinitions() {
   public function testGetBaseFieldDefinitionsTranslatableEntityTypeDefaultLangcode($default_langcode_key) {
     $this->setUpEntityWithFieldDefinition(FALSE, 'id', array('langcode' => 'langcode', 'default_langcode' => $default_langcode_key));
 
-    $field_definition = $this->getMockBuilder('Drupal\Core\Field\BaseFieldDefinition')
-      ->disableOriginalConstructor()
-      ->getMock();
-    $field_definition->expects($this->atLeastOnce())
-      ->method('isTranslatable')
-      ->willReturn(TRUE);
+    $field_definition = $this->prophesize()->willImplement(FieldDefinitionInterface::class)->willImplement(FieldStorageDefinitionInterface::class);
+    $field_definition->isTranslatable()->willReturn(TRUE);
 
-    $entity_class = get_class($this->entity);
+    $entity_class = EntityManagerTestEntity::class;
     $entity_class::$baseFieldDefinitions += array('langcode' => $field_definition);
 
-    $this->entityType->expects($this->atLeastOnce())
-      ->method('isTranslatable')
-      ->willReturn(TRUE);
+    $this->entityType->isTranslatable()->willReturn(TRUE);
 
     $definitions = $this->entityManager->getBaseFieldDefinitions('test_entity_type');
 
@@ -651,23 +316,18 @@ public function testGetBaseFieldDefinitionsTranslatableEntityTypeLangcode($provi
     $this->setUpEntityWithFieldDefinition(FALSE, 'id', $keys);
 
     if ($provide_field) {
-      $field_definition = $this->getMockBuilder('Drupal\Core\Field\BaseFieldDefinition')
-        ->disableOriginalConstructor()
-        ->getMock();
-      $field_definition->expects($this->any())
-        ->method('isTranslatable')
-        ->willReturn($translatable);
+      $field_definition = $this->prophesize()->willImplement(FieldDefinitionInterface::class)->willImplement(FieldStorageDefinitionInterface::class);
+      $field_definition->isTranslatable()->willReturn($translatable);
+      if (!$translatable) {
+        $field_definition->setTranslatable(!$translatable)->shouldBeCalled();
+      }
 
-      $entity_class = get_class($this->entity);
-      $entity_class::$baseFieldDefinitions += array('langcode' => $field_definition);
+      $entity_class = EntityManagerTestEntity::class;
+      $entity_class::$baseFieldDefinitions += array('langcode' => $field_definition->reveal());
     }
 
-    $this->entityType->expects($this->atLeastOnce())
-      ->method('isTranslatable')
-      ->willReturn(TRUE);
-    $this->entityType->expects($this->atLeastOnce())
-      ->method('getLabel')
-      ->willReturn('Test');
+    $this->entityType->isTranslatable()->willReturn(TRUE);
+    $this->entityType->getLabel()->willReturn('Test');
 
     $this->entityManager->getBaseFieldDefinitions('test_entity_type');
   }
@@ -696,24 +356,17 @@ public function testGetBaseFieldDefinitionsWithCaching() {
 
     $expected = array('id' => $field_definition);
 
-    $this->cacheBackend->expects($this->at(0))
-      ->method('get')
-      ->with('entity_base_field_definitions:test_entity_type:en', FALSE)
-      ->will($this->returnValue(FALSE));
-    $this->cacheBackend->expects($this->at(1))
-      ->method('get')
-      ->with('entity_type', FALSE)
-      ->will($this->returnValue(FALSE));
-    $this->cacheBackend->expects($this->at(2))
-      ->method('set')
-      ->with('entity_type');
-    $this->cacheBackend->expects($this->at(3))
-      ->method('set')
-      ->with('entity_base_field_definitions:test_entity_type:en');
-    $this->cacheBackend->expects($this->at(4))
-      ->method('get')
-      ->with('entity_base_field_definitions:test_entity_type:en', FALSE)
-      ->will($this->returnValue((object) array('data' => $expected)));
+    $this->cacheBackend->get('entity_base_field_definitions:test_entity_type:en')
+      ->willReturn(FALSE)
+      ->shouldBeCalled();
+    $this->cacheBackend->set('entity_base_field_definitions:test_entity_type:en', Argument::any(), Cache::PERMANENT, ['entity_types', 'entity_field_info'])
+      ->will(function ($args) {
+        $data = (object) ['data' => $args[1]];
+        $this->get('entity_base_field_definitions:test_entity_type:en')
+          ->willReturn($data)
+          ->shouldBeCalled();
+      })
+      ->shouldBeCalled();
 
     $this->assertSame($expected, $this->entityManager->getBaseFieldDefinitions('test_entity_type'));
     $this->entityManager->testClearEntityFieldInfo();
@@ -730,30 +383,20 @@ public function testGetFieldDefinitionsWithCaching() {
 
     $expected = array('id' => $field_definition);
 
-    $this->cacheBackend->expects($this->at(0))
-      ->method('get')
-      ->with('entity_base_field_definitions:test_entity_type:en', FALSE)
-      ->will($this->returnValue((object) array('data' => $expected)));
-    $this->cacheBackend->expects($this->at(1))
-      ->method('get')
-      ->with('entity_bundle_field_definitions:test_entity_type:test_bundle:en', FALSE)
-      ->will($this->returnValue(FALSE));
-    $this->cacheBackend->expects($this->at(2))
-      ->method('get')
-      ->with('entity_type', FALSE)
-      ->will($this->returnValue(FALSE));
-    $this->cacheBackend->expects($this->at(3))
-      ->method('set');
-    $this->cacheBackend->expects($this->at(4))
-      ->method('set');
-    $this->cacheBackend->expects($this->at(5))
-      ->method('get')
-      ->with('entity_base_field_definitions:test_entity_type:en', FALSE)
-      ->will($this->returnValue((object) array('data' => $expected)));
-    $this->cacheBackend->expects($this->at(6))
-      ->method('get')
-      ->with('entity_bundle_field_definitions:test_entity_type:test_bundle:en', FALSE)
-      ->will($this->returnValue((object) array('data' => $expected)));
+    $this->cacheBackend->get('entity_base_field_definitions:test_entity_type:en')
+      ->willReturn((object) array('data' => $expected))
+      ->shouldBeCalledTimes(2);
+    $this->cacheBackend->get('entity_bundle_field_definitions:test_entity_type:test_bundle:en')
+      ->willReturn(FALSE)
+      ->shouldBeCalledTimes(1);
+    $this->cacheBackend->set('entity_bundle_field_definitions:test_entity_type:test_bundle:en', Argument::any(), Cache::PERMANENT, ['entity_types', 'entity_field_info'])
+      ->will(function ($args) {
+        $data = (object) ['data' => $args[1]];
+        $this->get('entity_bundle_field_definitions:test_entity_type:test_bundle:en')
+          ->willReturn($data)
+          ->shouldBeCalled();
+      })
+      ->shouldBeCalled();
 
     $this->assertSame($expected, $this->entityManager->getFieldDefinitions('test_entity_type', 'test_bundle'));
     $this->entityManager->testClearEntityFieldInfo();
@@ -767,54 +410,33 @@ public function testGetFieldDefinitionsWithCaching() {
    */
   public function testGetFieldStorageDefinitionsWithCaching() {
     $field_definition = $this->setUpEntityWithFieldDefinition(TRUE, 'id');
-    $field_storage_definition = $this->getMock('\Drupal\Core\Field\FieldStorageDefinitionInterface');
-    $field_storage_definition->expects($this->any())
-      ->method('getName')
-      ->will($this->returnValue('field_storage'));
+    $field_storage_definition = $this->prophesize(FieldStorageDefinitionInterface::class);
+    $field_storage_definition->getName()->willReturn('field_storage');
 
-    $this->moduleHandler->expects($this->any())
-      ->method('getImplementations')
-      ->will($this->returnValueMap(array(
-        array('entity_field_storage_info', array('example_module')),
-        array('entity_type_build', array())
-      )));
+    $definitions = ['field_storage' => $field_storage_definition->reveal()];
 
-    $this->moduleHandler->expects($this->once())
-      ->method('invoke')
-      ->with('example_module')
-      ->will($this->returnValue(array('field_storage' => $field_storage_definition)));
+    $this->moduleHandler->getImplementations('entity_field_storage_info')->willReturn(['example_module']);
+    $this->moduleHandler->invoke('example_module', 'entity_field_storage_info', [$this->entityType])->willReturn($definitions);
+    $this->moduleHandler->alter('entity_field_storage_info', $definitions, $this->entityType)->willReturn(NULL);
 
     $expected = array(
       'id' => $field_definition,
-      'field_storage' => $field_storage_definition,
+      'field_storage' => $field_storage_definition->reveal(),
     );
 
-    $this->cacheBackend->expects($this->at(0))
-      ->method('get')
-      ->with('entity_base_field_definitions:test_entity_type:en', FALSE)
-      ->will($this->returnValue((object) array('data' => array('id' => $expected['id']))));
-    $this->cacheBackend->expects($this->at(1))
-      ->method('get')
-      ->with('entity_field_storage_definitions:test_entity_type:en', FALSE)
-      ->will($this->returnValue(FALSE));
-    $this->cacheBackend->expects($this->at(2))
-      ->method('get')
-      ->with('entity_type', FALSE)
-      ->will($this->returnValue(FALSE));
-    $this->cacheBackend->expects($this->at(3))
-      ->method('set')
-      ->with('entity_type');
-    $this->cacheBackend->expects($this->at(4))
-      ->method('set')
-      ->with('entity_field_storage_definitions:test_entity_type:en');
-    $this->cacheBackend->expects($this->at(5))
-      ->method('get')
-      ->with('entity_base_field_definitions:test_entity_type:en', FALSE)
-      ->will($this->returnValue((object) array('data' => array('id' => $expected['id']))));
-    $this->cacheBackend->expects($this->at(6))
-      ->method('get')
-      ->with('entity_field_storage_definitions:test_entity_type:en', FALSE)
-      ->will($this->returnValue((object) array('data' => $expected)));
+    $this->cacheBackend->get('entity_base_field_definitions:test_entity_type:en')
+      ->willReturn((object) ['data' => ['id' => $expected['id']]])
+      ->shouldBeCalledTimes(2);
+    $this->cacheBackend->get('entity_field_storage_definitions:test_entity_type:en')->willReturn(FALSE);
+
+    $this->cacheBackend->set('entity_field_storage_definitions:test_entity_type:en', Argument::any(), Cache::PERMANENT, ['entity_types', 'entity_field_info'])
+      ->will(function () use ($expected) {
+        $this->get('entity_field_storage_definitions:test_entity_type:en')
+          ->willReturn((object) ['data' => $expected])
+          ->shouldBeCalled();
+      })
+      ->shouldBeCalled();
+
 
     $this->assertSame($expected, $this->entityManager->getFieldStorageDefinitions('test_entity_type'));
     $this->entityManager->testClearEntityFieldInfo();
@@ -830,14 +452,10 @@ public function testGetFieldStorageDefinitionsWithCaching() {
    * @expectedException \LogicException
    */
   public function testGetBaseFieldDefinitionsInvalidDefinition() {
-    $langcode_definition = $this->setUpEntityWithFieldDefinition(FALSE, 'langcode', array('langcode' => 'langcode'));
-    $langcode_definition->expects($this->once())
-      ->method('isTranslatable')
-      ->will($this->returnValue(FALSE));
+    $this->setUpEntityWithFieldDefinition(FALSE, 'langcode', array('langcode' => 'langcode'));
 
-    $this->entityType->expects($this->any())
-      ->method('isTranslatable')
-      ->will($this->returnValue(TRUE));
+    $this->entityType->isTranslatable()->willReturn(TRUE);
+    $this->entityType->getLabel()->willReturn('the_label');
 
     $this->entityManager->getBaseFieldDefinitions('test_entity_type');
   }
@@ -855,25 +473,20 @@ public function testGetFieldDefinitionsProvider() {
 
     // @todo Mock FieldDefinitionInterface once it exposes a proper provider
     //   setter. See https://www.drupal.org/node/2225961.
-    $field_definition = $this->getMockBuilder('Drupal\Core\Field\BaseFieldDefinition')
-      ->disableOriginalConstructor()
-      ->getMock();
+    $field_definition = $this->prophesize(BaseFieldDefinition::class);
 
     // We expect two calls as the field definition will be returned from both
     // base and bundle entity field info hook implementations.
-    $field_definition
-      ->expects($this->exactly(2))
-      ->method('setProvider')
-      ->with($this->matches($module));
+    $field_definition->getProvider()->shouldBeCalled();
+    $field_definition->setProvider($module)->shouldBeCalledTimes(2);
+    $field_definition->setName(0)->shouldBeCalledTimes(2);
+    $field_definition->setTargetEntityTypeId('test_entity_type')->shouldBeCalled();
+    $field_definition->setTargetBundle(NULL)->shouldBeCalled();
+    $field_definition->setTargetBundle('test_bundle')->shouldBeCalled();
 
-    $this->moduleHandler->expects($this->any())
-      ->method('getImplementations')
-      ->will($this->returnValue(array($module)));
-
-    $this->moduleHandler->expects($this->any())
-      ->method('invoke')
-      ->with($this->matches($module))
-      ->will($this->returnValue(array($field_definition)));
+    $this->moduleHandler->getImplementations(Argument::type('string'))->willReturn([$module]);
+    $this->moduleHandler->invoke($module, 'entity_base_field_info', [$this->entityType])->willReturn([$field_definition->reveal()]);
+    $this->moduleHandler->invoke($module, 'entity_bundle_field_info', Argument::type('array'))->willReturn([$field_definition->reveal()]);
 
     $this->entityManager->getFieldDefinitions('test_entity_type', 'test_bundle');
   }
@@ -890,58 +503,48 @@ public function testGetFieldDefinitionsProvider() {
    *   (optional) An array of entity keys for the mocked entity type. Defaults
    *   to an empty array.
    *
-   * @return \Drupal\Core\Field\BaseFieldDefinition|\PHPUnit_Framework_MockObject_MockObject
+   * @return \Drupal\Core\Field\BaseFieldDefinition|\Prophecy\Prophecy\ProphecyInterface
    *   A field definition object.
    */
   protected function setUpEntityWithFieldDefinition($custom_invoke_all = FALSE, $field_definition_id = 'id', $entity_keys = array()) {
-    $this->entityType = $this->getMock('Drupal\Core\Entity\EntityTypeInterface');
+    $field_type_manager = $this->prophesize(FieldTypePluginManagerInterface::class);
+    $field_type_manager->getDefaultStorageSettings('boolean')->willReturn([]);
+    $field_type_manager->getDefaultFieldSettings('boolean')->willReturn([]);
+    $this->container->get('plugin.manager.field.field_type')->willReturn($field_type_manager->reveal());
 
-    $this->entity = $this->getMockBuilder('Drupal\Tests\Core\Entity\EntityManagerTestEntity')
-      ->disableOriginalConstructor()
-      ->getMockForAbstractClass();
-    $entity_class = get_class($this->entity);
+    $string_translation = $this->prophesize(TranslationInterface::class);
+    $this->container->get('string_translation')->willReturn($string_translation->reveal());
 
-    $this->entityType->expects($this->any())
-      ->method('getClass')
-      ->will($this->returnValue($entity_class));
-    $this->entityType->expects($this->any())
-      ->method('getKeys')
-      ->will($this->returnValue($entity_keys + array('default_langcode' => 'default_langcode')));
-    $this->entityType->expects($this->any())
-      ->method('isSubclassOf')
-      ->with($this->equalTo('\Drupal\Core\Entity\FieldableEntityInterface'))
-      ->will($this->returnValue(TRUE));
-    $field_definition = $this->getMockBuilder('Drupal\Core\Field\BaseFieldDefinition')
-      ->disableOriginalConstructor()
-      ->getMock();
+    $entity_class = EntityManagerTestEntity::class;
+
+    $field_definition = $this->prophesize()->willImplement(FieldDefinitionInterface::class)->willImplement(FieldStorageDefinitionInterface::class);
     $entity_class::$baseFieldDefinitions = array(
-      $field_definition_id => $field_definition,
+      $field_definition_id => $field_definition->reveal(),
     );
     $entity_class::$bundleFieldDefinitions = array();
 
-    $this->moduleHandler = $this->getMock('Drupal\Core\Extension\ModuleHandlerInterface');
-    $this->moduleHandler->expects($this->any())
-      ->method('alter');
     if (!$custom_invoke_all) {
-      $this->moduleHandler->expects($this->any())
-        ->method('getImplementations')
-        ->will($this->returnValue(array()));
+      $this->moduleHandler->getImplementations(Argument::cetera())->willReturn([]);
     }
 
     // Mock the base field definition override.
-    $override_entity_type = $this->getMock('Drupal\Core\Entity\EntityTypeInterface');
-    $override_entity_type->expects($this->any())
-       ->method('getClass')
-       ->will($this->returnValue(get_class($this->entity)));
-
-    $override_entity_type->expects($this->any())
-      ->method('getHandlerClass')
-      ->with('storage')
-      ->will($this->returnValue('\Drupal\Tests\Core\Entity\TestConfigEntityStorage'));
+    $override_entity_type = $this->prophesize(EntityTypeInterface::class);
 
+    $this->entityType = $this->prophesize(EntityTypeInterface::class);
     $this->setUpEntityManager(array('test_entity_type' => $this->entityType, 'base_field_override' => $override_entity_type));
 
-    return $field_definition;
+    $storage = $this->prophesize(ConfigEntityStorageInterface::class);
+    $storage->loadMultiple(Argument::type('array'))->willReturn([]);
+    $this->entityTypeManager->getStorage('base_field_override')->willReturn($storage->reveal());
+
+    $this->entityType->getClass()->willReturn($entity_class);
+    $this->entityType->getKeys()->willReturn($entity_keys + ['default_langcode' => 'default_langcode']);
+    $this->entityType->isSubclassOf(FieldableEntityInterface::class)->willReturn(TRUE);
+    $this->entityType->isTranslatable()->willReturn(FALSE);
+    $this->entityType->getProvider()->willReturn('the_provider');
+    $this->entityType->id()->willReturn('the_entity_id');
+
+    return $field_definition->reveal();
   }
 
   /**
@@ -951,11 +554,10 @@ protected function setUpEntityWithFieldDefinition($custom_invoke_all = FALSE, $f
    */
   public function testClearCachedFieldDefinitions() {
     $this->setUpEntityManager();
-    $this->cacheTagsInvalidator->expects($this->once())
-      ->method('invalidateTags')
-      ->with(array('entity_field_info'));
-    $this->typedDataManager->expects($this->once())
-      ->method('clearCachedDefinitions');
+
+    $this->cacheTagsInvalidator->invalidateTags(['entity_field_info'])->shouldBeCalled();
+
+    $this->typedDataManager->clearCachedDefinitions()->shouldBeCalled();
 
     $this->entityManager->clearCachedFieldDefinitions();
   }
@@ -967,9 +569,10 @@ public function testClearCachedFieldDefinitions() {
    */
   public function testClearCachedBundles() {
     $this->setUpEntityManager();
-    $this->cacheTagsInvalidator->expects($this->once())
-      ->method('invalidateTags')
-      ->with(array('entity_bundles'));
+
+    $this->typedDataManager->clearCachedDefinitions()->shouldBeCalled();
+
+    $this->cacheTagsInvalidator->invalidateTags(['entity_bundles'])->shouldBeCalled();
 
     $this->entityManager->clearCachedBundles();
   }
@@ -982,14 +585,17 @@ public function testClearCachedBundles() {
    * @dataProvider providerTestGetBundleInfo
    */
   public function testGetBundleInfo($entity_type_id, $expected) {
-    $apple = $this->getMock('Drupal\Core\Entity\EntityTypeInterface');
-    $apple->expects($this->once())
-      ->method('getLabel')
-      ->will($this->returnValue('Apple'));
-    $banana = $this->getMock('Drupal\Core\Entity\EntityTypeInterface');
-    $banana->expects($this->once())
-      ->method('getLabel')
-      ->will($this->returnValue('Banana'));
+    $this->moduleHandler->invokeAll('entity_bundle_info')->willReturn([]);
+    $this->moduleHandler->alter('entity_bundle_info', Argument::type('array'))->willReturn(NULL);
+
+    $apple = $this->prophesize(EntityTypeInterface::class);
+    $apple->getLabel()->willReturn('Apple');
+    $apple->getBundleOf()->willReturn(NULL);
+
+    $banana = $this->prophesize(EntityTypeInterface::class);
+    $banana->getLabel()->willReturn('Banana');
+    $banana->getBundleOf()->willReturn(NULL);
+
     $this->setUpEntityManager(array(
       'apple' => $apple,
       'banana' => $banana,
@@ -1027,45 +633,37 @@ public function providerTestGetBundleInfo() {
    * @covers ::getAllBundleInfo
    */
   public function testGetAllBundleInfo() {
-    $apple = $this->getMock('Drupal\Core\Entity\EntityTypeInterface');
-    $apple->expects($this->once())
-      ->method('getLabel')
-      ->will($this->returnValue('Apple'));
-    $banana = $this->getMock('Drupal\Core\Entity\EntityTypeInterface');
-    $banana->expects($this->once())
-      ->method('getLabel')
-      ->will($this->returnValue('Banana'));
+    $this->moduleHandler->invokeAll('entity_bundle_info')->willReturn([]);
+    $this->moduleHandler->alter('entity_bundle_info', Argument::type('array'))->willReturn(NULL);
+
+    $apple = $this->prophesize(EntityTypeInterface::class);
+    $apple->getLabel()->willReturn('Apple');
+    $apple->getBundleOf()->willReturn(NULL);
+
+    $banana = $this->prophesize(EntityTypeInterface::class);
+    $banana->getLabel()->willReturn('Banana');
+    $banana->getBundleOf()->willReturn(NULL);
+
     $this->setUpEntityManager(array(
       'apple' => $apple,
       'banana' => $banana,
     ));
-    $this->cacheBackend->expects($this->at(0))
-      ->method('get')
-      ->with("entity_bundle_info:en", FALSE)
-      ->will($this->returnValue(FALSE));
-    $this->cacheBackend->expects($this->at(1))
-      ->method('get')
-      ->with("entity_type", FALSE)
-      ->will($this->returnValue(FALSE));
-    $this->cacheBackend->expects($this->at(2))
-      ->method('set')
-      ->with("entity_type");
-    $this->cacheBackend->expects($this->at(3))
-      ->method('set')
-      ->with("entity_bundle_info:en");
-    $this->cacheTagsInvalidator->expects($this->at(0))
-      ->method('invalidateTags')
-      ->with(array('entity_types'));
-    $this->cacheTagsInvalidator->expects($this->at(1))
-      ->method('invalidateTags')
-      ->with(array('entity_bundles'));
-    $this->cacheTagsInvalidator->expects($this->at(2))
-      ->method('invalidateTags')
-      ->with(array('entity_field_info'));
-    $this->cacheBackend->expects($this->at(4))
-      ->method('get')
-      ->with("entity_bundle_info:en", FALSE)
-      ->will($this->returnValue((object) array('data' => 'cached data')));
+
+    $this->cacheBackend->get('entity_bundle_info:en')->willReturn(FALSE);
+    $this->cacheBackend->set('entity_bundle_info:en', Argument::any(), Cache::PERMANENT, ['entity_types', 'entity_bundles'])
+      ->will(function () {
+        $this->get('entity_bundle_info:en')
+          ->willReturn((object) ['data' => 'cached data'])
+          ->shouldBeCalled();
+      })
+      ->shouldBeCalled();
+
+    $this->cacheTagsInvalidator->invalidateTags(['entity_types'])->shouldBeCalled();
+    $this->cacheTagsInvalidator->invalidateTags(['entity_bundles'])->shouldBeCalled();
+    $this->cacheTagsInvalidator->invalidateTags(['entity_field_info'])->shouldBeCalled();
+
+    $this->typedDataManager->clearCachedDefinitions()->shouldBeCalled();
+    $this->entityTypeManager->clearCachedDefinitions()->shouldBeCalled();
 
     $expected = array(
       'apple' => array(
@@ -1097,14 +695,14 @@ public function testGetAllBundleInfo() {
    * @covers ::getEntityTypeLabels
    */
   public function testGetEntityTypeLabels() {
-    $apple = $this->getMock('Drupal\Core\Entity\EntityTypeInterface');
-    $apple->expects($this->once())
-      ->method('getLabel')
-      ->will($this->returnValue('Apple'));
-    $banana = $this->getMock('Drupal\Core\Entity\EntityTypeInterface');
-    $banana->expects($this->once())
-      ->method('getLabel')
-      ->will($this->returnValue('Banana'));
+    $apple = $this->prophesize(EntityTypeInterface::class);
+    $apple->getLabel()->willReturn('Apple');
+    $apple->getBundleOf()->willReturn(NULL);
+
+    $banana = $this->prophesize(EntityTypeInterface::class);
+    $banana->getLabel()->willReturn('Banana');
+    $banana->getBundleOf()->willReturn(NULL);
+
     $this->setUpEntityManager(array(
       'apple' => $apple,
       'banana' => $banana,
@@ -1125,51 +723,40 @@ public function testGetEntityTypeLabels() {
   public function testGetTranslationFromContext() {
     $this->setUpEntityManager();
 
-    $this->languageManager->expects($this->exactly(1))
-      ->method('getFallbackCandidates')
-      ->will($this->returnCallback(function (array $context = array()) {
+    $language = new Language(['id' => 'en']);
+    $this->languageManager->getCurrentLanguage(LanguageInterface::TYPE_CONTENT)
+      ->willReturn($language)
+      ->shouldBeCalledTimes(1);
+    $this->languageManager->getFallbackCandidates(Argument::type('array'))
+      ->will(function ($args) {
+        $context = $args[0];
         $candidates = array();
         if (!empty($context['langcode'])) {
           $candidates[$context['langcode']] = $context['langcode'];
         }
         return $candidates;
-      }));
+      })
+      ->shouldBeCalledTimes(1);
 
-    $entity = $this->getMock('Drupal\Tests\Core\Entity\TestContentEntityInterface');
-    $entity->expects($this->exactly(1))
-      ->method('getUntranslated')
-      ->will($this->returnValue($entity));
-    $language = $this->getMock('\Drupal\Core\Language\LanguageInterface');
-    $language->expects($this->any())
-      ->method('getId')
-      ->will($this->returnValue('en'));
-    $entity->expects($this->exactly(3))
-      ->method('language')
-      ->will($this->returnValue($language));
-    $entity->expects($this->exactly(1))
-      ->method('hasTranslation')
-      ->will($this->returnValueMap(array(
-        array(LanguageInterface::LANGCODE_DEFAULT, FALSE),
-        array('custom_langcode', TRUE),
-      )));
+    $translated_entity = $this->prophesize(ContentEntityInterface::class);
 
-    $translated_entity = $this->getMock('Drupal\Tests\Core\Entity\TestContentEntityInterface');
-    $entity->expects($this->once())
-      ->method('getTranslation')
-      ->with('custom_langcode')
-      ->will($this->returnValue($translated_entity));
-    $entity->expects($this->any())
-      ->method('getTranslationLanguages')
-      ->will($this->returnValue([new Language(['id' => 'en']), new Language(['id' => 'custom_langcode'])]));
+    $entity = $this->prophesize(ContentEntityInterface::class);
+    $entity->getUntranslated()->willReturn($entity);
+    $entity->language()->willReturn($language);
+    $entity->hasTranslation(LanguageInterface::LANGCODE_DEFAULT)->willReturn(FALSE);
+    $entity->hasTranslation('custom_langcode')->willReturn(TRUE);
+    $entity->getTranslation('custom_langcode')->willReturn($translated_entity->reveal());
+    $entity->getTranslationLanguages()->willReturn([new Language(['id' => 'en']), new Language(['id' => 'custom_langcode'])]);
+    $entity->addCacheContexts(['languages:language_content'])->shouldBeCalled();
 
-    $this->assertSame($entity, $this->entityManager->getTranslationFromContext($entity));
-    $this->assertSame($translated_entity, $this->entityManager->getTranslationFromContext($entity, 'custom_langcode'));
+    $this->assertSame($entity->reveal(), $this->entityManager->getTranslationFromContext($entity->reveal()));
+    $this->assertSame($translated_entity->reveal(), $this->entityManager->getTranslationFromContext($entity->reveal(), 'custom_langcode'));
   }
 
   /**
    * @covers ::getExtraFields
    */
-  function testgetExtraFields() {
+  function testGetExtraFields() {
     $this->setUpEntityManager();
 
     $entity_type_id = $this->randomMachineName();
@@ -1193,26 +780,16 @@ function testgetExtraFields() {
     $cache_id = 'entity_bundle_extra_fields:' . $entity_type_id . ':' . $bundle . ':' . $language_code;
 
     $language = new Language(array('id' => $language_code));
+    $this->languageManager->getCurrentLanguage()
+      ->willReturn($language)
+      ->shouldBeCalledTimes(1);
 
-    $this->languageManager->expects($this->once())
-      ->method('getCurrentLanguage')
-      ->will($this->returnValue($language));
+    $this->cacheBackend->get($cache_id)->shouldBeCalled();
 
-    $this->cacheBackend->expects($this->once())
-      ->method('get')
-      ->with($cache_id);
+    $this->moduleHandler->invokeAll('entity_extra_field_info')->willReturn($hook_bundle_extra_fields);
+    $this->moduleHandler->alter('entity_extra_field_info', $hook_bundle_extra_fields)->shouldBeCalled();
 
-    $this->moduleHandler->expects($this->once())
-      ->method('invokeAll')
-      ->with('entity_extra_field_info')
-      ->will($this->returnValue($hook_bundle_extra_fields));
-    $this->moduleHandler->expects($this->once())
-      ->method('alter')
-      ->with('entity_extra_field_info', $hook_bundle_extra_fields);
-
-    $this->cacheBackend->expects($this->once())
-      ->method('set')
-      ->with($cache_id, $processed_hook_bundle_extra_fields[$entity_type_id][$bundle]);
+    $this->cacheBackend->set($cache_id, $processed_hook_bundle_extra_fields[$entity_type_id][$bundle], Cache::PERMANENT, ['entity_field_info'])->shouldBeCalled();
 
     $this->assertSame($processed_hook_bundle_extra_fields[$entity_type_id][$bundle], $this->entityManager->getExtraFields($entity_type_id, $bundle));
   }
@@ -1221,95 +798,38 @@ function testgetExtraFields() {
    * @covers ::getFieldMap
    */
   public function testGetFieldMap() {
+    $this->moduleHandler->invokeAll('entity_bundle_info')->willReturn([]);
+    $this->moduleHandler->alter('entity_bundle_info', Argument::type('array'))->willReturn(NULL);
+
     // Set up a content entity type.
-    $entity_type = $this->getMock('Drupal\Core\Entity\ContentEntityTypeInterface');
-    $entity = $this->getMockBuilder('Drupal\Tests\Core\Entity\EntityManagerTestEntity')
-      ->disableOriginalConstructor()
-      ->getMockForAbstractClass();
-    $entity_class = get_class($entity);
-    $entity_type->expects($this->any())
-      ->method('getClass')
-      ->will($this->returnValue($entity_class));
-    $entity_type->expects($this->any())
-      ->method('getKeys')
-      ->will($this->returnValue(array('default_langcode' => 'default_langcode')));
-    $entity_type->expects($this->any())
-      ->method('id')
-      ->will($this->returnValue('test_entity_type'));
-    $entity_type->expects($this->any())
-      ->method('isSubclassOf')
-      ->with('\Drupal\Core\Entity\FieldableEntityInterface')
-      ->will($this->returnValue(TRUE));
-
-    // Set up the module handler to return two bundles for the fieldable entity
-    // type.
-    $this->moduleHandler = $this->getMock('Drupal\Core\Extension\ModuleHandlerInterface');
-    $this->moduleHandler->expects($this->any())
-      ->method('alter');
-    $this->moduleHandler->expects($this->any())
-      ->method('getImplementations')
-      ->will($this->returnValue(array()));
-    $module_implements_value_map = array(
-      array(
-        'entity_bundle_info', array(),
-        array(
-          'test_entity_type' => array(
-            'first_bundle' => array(),
-            'second_bundle' => array(),
-          ),
-        ),
-      ),
-    );
-    $this->moduleHandler->expects($this->any())
-      ->method('invokeAll')
-      ->will($this->returnValueMap($module_implements_value_map));
-
+    $entity_type = $this->prophesize(ContentEntityTypeInterface::class);
+    $entity_class = EntityManagerTestEntity::class;
 
     // Define an ID field definition as a base field.
-    $id_definition = $this->getMockBuilder('Drupal\Core\Field\BaseFieldDefinition')
-      ->disableOriginalConstructor()
-      ->getMock();
-    $id_definition->expects($this->once())
-      ->method('getType')
-      ->will($this->returnValue('integer'));
+    $id_definition = $this->prophesize(FieldDefinitionInterface::class);
+    $id_definition->getType()->willReturn('integer');
     $base_field_definitions = array(
-      'id' => $id_definition,
+      'id' => $id_definition->reveal(),
     );
     $entity_class::$baseFieldDefinitions = $base_field_definitions;
 
     // Set up the stored bundle field map.
-    $key_value_store = $this->getMock('Drupal\Core\KeyValueStore\KeyValueStoreInterface');
-    $this->keyValueFactory->expects($this->once())
-      ->method('get')
-      ->with('entity.definitions.bundle_field_map')
-      ->willReturn($key_value_store);
-    $key_value_store->expects($this->once())
-      ->method('getAll')
-      ->willReturn(['test_entity_type' => [
+    $key_value_store = $this->prophesize(KeyValueStoreInterface::class);
+    $this->keyValueFactory->get('entity.definitions.bundle_field_map')->willReturn($key_value_store->reveal());
+    $key_value_store->getAll()->willReturn([
+      'test_entity_type' => [
         'by_bundle' => [
           'type' => 'string',
           'bundles' => ['second_bundle' => 'second_bundle'],
         ],
-      ]]);
+      ],
+    ]);
 
     // Set up a non-content entity type.
-    $non_content_entity_type = $this->getMock('Drupal\Core\Entity\EntityTypeInterface');
-    $entity_type->expects($this->any())
-      ->method('isSubclassOf')
-      ->with('\Drupal\Core\Entity\FieldableEntityInterface')
-      ->will($this->returnValue(FALSE));
+    $non_content_entity_type = $this->prophesize(EntityTypeInterface::class);
 
     // Mock the base field definition override.
-    $override_entity_type = $this->getMock('Drupal\Core\Entity\EntityTypeInterface');
-    $override_entity_class = get_class($entity);
-    $override_entity_type->expects($this->any())
-      ->method('getClass')
-      ->will($this->returnValue($override_entity_class));
-
-    $override_entity_type->expects($this->any())
-      ->method('getHandlerClass')
-      ->with('storage')
-      ->will($this->returnValue('\Drupal\Tests\Core\Entity\TestConfigEntityStorage'));
+    $override_entity_type = $this->prophesize(EntityTypeInterface::class);
 
     $this->setUpEntityManager(array(
       'test_entity_type' => $entity_type,
@@ -1317,6 +837,34 @@ public function testGetFieldMap() {
       'base_field_override' => $override_entity_type,
     ));
 
+    $entity_type->getClass()->willReturn($entity_class);
+    $entity_type->getKeys()->willReturn(['default_langcode' => 'default_langcode']);
+    $entity_type->getBundleOf()->willReturn(NULL);
+    $entity_type->id()->willReturn('test_entity_type');
+    $entity_type->isSubclassOf(FieldableEntityInterface::class)->willReturn(TRUE);
+    $entity_type->isTranslatable()->shouldBeCalled();
+    $entity_type->getProvider()->shouldBeCalled();
+
+    $non_content_entity_type->isSubclassOf(FieldableEntityInterface::class)->willReturn(FALSE);
+    $non_content_entity_type->getBundleOf()->willReturn(NULL);
+    $non_content_entity_type->getLabel()->shouldBeCalled();
+
+    $override_entity_type->isSubclassOf(FieldableEntityInterface::class)->willReturn(FALSE);
+    $override_entity_type->getHandlerClass('storage')->willReturn(ConfigEntityStorageInterface::class);
+    $override_entity_type->getBundleOf()->willReturn(NULL);
+    $override_entity_type->getLabel()->shouldBeCalled();
+
+    // Set up the module handler to return two bundles for the fieldable entity
+    // type.
+    $this->moduleHandler->alter(Argument::type('string'), Argument::type('array'));
+    $this->moduleHandler->getImplementations('entity_base_field_info')->willReturn([]);
+    $this->moduleHandler->invokeAll('entity_bundle_info')->willReturn([
+      'test_entity_type' => [
+        'first_bundle' => [],
+        'second_bundle' => [],
+      ],
+    ]);
+
     $expected = array(
       'test_entity_type' => array(
         'id' => array(
@@ -1349,10 +897,7 @@ public function testGetFieldMapFromCache() {
       )
     );
     $this->setUpEntityManager();
-    $this->cacheBackend->expects($this->once())
-      ->method('get')
-      ->with('entity_field_map')
-      ->will($this->returnValue((object) array('data' => $expected)));
+    $this->cacheBackend->get('entity_field_map')->willReturn((object) array('data' => $expected));
 
     // Call the field map twice to make sure the static cache works.
     $this->assertEquals($expected, $this->entityManager->getFieldMap());
@@ -1364,92 +909,62 @@ public function testGetFieldMapFromCache() {
    */
   public function testGetFieldMapByFieldType() {
     // Set up a content entity type.
-    $entity_type = $this->getMock('Drupal\Core\Entity\ContentEntityTypeInterface');
-    $entity = $this->getMockBuilder('Drupal\Tests\Core\Entity\EntityManagerTestEntity')
-      ->disableOriginalConstructor()
-      ->getMockForAbstractClass();
-    $entity_class = get_class($entity);
-    $entity_type->expects($this->any())
-      ->method('getClass')
-      ->will($this->returnValue($entity_class));
-    $entity_type->expects($this->any())
-      ->method('getKeys')
-      ->will($this->returnValue(array('default_langcode' => 'default_langcode')));
-    $entity_type->expects($this->any())
-      ->method('id')
-      ->will($this->returnValue('test_entity_type'));
-    $entity_type->expects($this->any())
-      ->method('isSubclassOf')
-      ->with('\Drupal\Core\Entity\FieldableEntityInterface')
-      ->will($this->returnValue(TRUE));
+    $entity_type = $this->prophesize(ContentEntityTypeInterface::class);
+    $entity_class = EntityManagerTestEntity::class;
 
     // Set up the module handler to return two bundles for the fieldable entity
     // type.
-    $this->moduleHandler = $this->getMock('Drupal\Core\Extension\ModuleHandlerInterface');
-    $this->moduleHandler->expects($this->any())
-      ->method('alter');
-    $this->moduleHandler->expects($this->any())
-      ->method('getImplementations')
-      ->will($this->returnValue(array()));
-    $module_implements_value_map = array(
-      array(
-        'entity_bundle_info', array(),
-        array(
-          'test_entity_type' => array(
-            'first_bundle' => array(),
-            'second_bundle' => array(),
-          ),
-        ),
-      ),
-    );
-    $this->moduleHandler->expects($this->any())
-      ->method('invokeAll')
-      ->will($this->returnValueMap($module_implements_value_map));
-
+    $this->moduleHandler->getImplementations('entity_base_field_info')->willReturn([]);
+    $this->moduleHandler->invokeAll('entity_bundle_info')->willReturn([
+      'test_entity_type' => [
+        'first_bundle' => [],
+        'second_bundle' => [],
+      ],
+    ]);
+    $this->moduleHandler->alter('entity_bundle_info', Argument::type('array'))->willReturn(NULL);
 
     // Define an ID field definition as a base field.
-    $id_definition = $this->getMockBuilder('Drupal\Core\Field\BaseFieldDefinition')
-      ->disableOriginalConstructor()
-      ->getMock();
-    $id_definition->expects($this->once())
-      ->method('getType')
-      ->will($this->returnValue('integer'));
+    $id_definition = $this->prophesize(FieldDefinitionInterface::class);
+    $id_definition->getType()->willReturn('integer');
     $base_field_definitions = array(
-      'id' => $id_definition,
+      'id' => $id_definition->reveal(),
     );
     $entity_class::$baseFieldDefinitions = $base_field_definitions;
 
     // Set up the stored bundle field map.
-    $key_value_store = $this->getMock('Drupal\Core\KeyValueStore\KeyValueStoreInterface');
-    $this->keyValueFactory->expects($this->once())
-      ->method('get')
-      ->with('entity.definitions.bundle_field_map')
-      ->willReturn($key_value_store);
-    $key_value_store->expects($this->once())
-      ->method('getAll')
-      ->willReturn(['test_entity_type' => [
+    $key_value_store = $this->prophesize(KeyValueStoreInterface::class);
+    $this->keyValueFactory->get('entity.definitions.bundle_field_map')->willReturn($key_value_store->reveal());
+    $key_value_store->getAll()->willReturn([
+      'test_entity_type' => [
         'by_bundle' => [
           'type' => 'string',
           'bundles' => ['second_bundle' => 'second_bundle'],
         ],
-      ]]);
+      ],
+    ]);
 
     // Mock the base field definition override.
-    $override_entity_type = $this->getMock('Drupal\Core\Entity\EntityTypeInterface');
-    $override_entity_type->expects($this->any())
-      ->method('getClass')
-      ->will($this->returnValue(get_class($entity)));
-
-    $override_entity_type->expects($this->any())
-      ->method('getHandlerClass')
-      ->with('storage')
-      ->will($this->returnValue('\Drupal\Tests\Core\Entity\TestConfigEntityStorage'));
+    $override_entity_type = $this->prophesize(EntityTypeInterface::class);
 
     $this->setUpEntityManager(array(
       'test_entity_type' => $entity_type,
       'base_field_override' => $override_entity_type,
     ));
 
+    $entity_type->getClass()->willReturn($entity_class);
+    $entity_type->getKeys()->willReturn(['default_langcode' => 'default_langcode']);
+    $entity_type->id()->willReturn('test_entity_type');
+    $entity_type->isSubclassOf(FieldableEntityInterface::class)->willReturn(TRUE);
+    $entity_type->getBundleOf()->shouldBeCalled();
+    $entity_type->isTranslatable()->shouldBeCalled();
+    $entity_type->getProvider()->shouldBeCalled();
+
+    $override_entity_type->getClass()->willReturn($entity_class);
+    $override_entity_type->isSubclassOf(FieldableEntityInterface::class)->willReturn(FALSE);
+    $override_entity_type->getHandlerClass('storage')->willReturn(ConfigEntityStorageInterface::class);
+    $override_entity_type->getBundleOf()->shouldBeCalled();
+    $override_entity_type->getLabel()->shouldBeCalled();
+
     $integerFields = $this->entityManager->getFieldMapByFieldType('integer');
     $this->assertCount(1, $integerFields['test_entity_type']);
     $this->assertArrayNotHasKey('non_fieldable', $integerFields);
@@ -1466,188 +981,107 @@ public function testGetFieldMapByFieldType() {
   /**
    * @covers ::onFieldDefinitionCreate
    */
-  public function testonFieldDefinitionCreateNewField() {
-    $field_definition = $this->getMock('Drupal\Core\Field\FieldDefinitionInterface');
-    $field_definition->expects($this->atLeastOnce())
-      ->method('getTargetEntityTypeId')
-      ->willReturn('test_entity_type');
-    $field_definition->expects($this->atLeastOnce())
-      ->method('getTargetBundle')
-      ->willReturn('test_bundle');
-    $field_definition->expects($this->atLeastOnce())
-      ->method('getName')
-      ->willReturn('test_field');
-    $field_definition->expects($this->atLeastOnce())
-      ->method('getType')
-      ->willReturn('test_type');
+  public function testOnFieldDefinitionCreateNewField() {
+    $field_definition = $this->prophesize(FieldDefinitionInterface::class);
+    $field_definition->getTargetEntityTypeId()->willReturn('test_entity_type');
+    $field_definition->getTargetBundle()->willReturn('test_bundle');
+    $field_definition->getName()->willReturn('test_field');
+    $field_definition->getType()->willReturn('test_type');
 
-    $storage = $this->getMock('Drupal\Core\Entity\DynamicallyFieldableEntityStorageInterface');
+    $storage = $this->prophesize(DynamicallyFieldableEntityStorageInterface::class);
+    $storage->onFieldDefinitionCreate($field_definition->reveal())->shouldBeCalledTimes(1);
+    $this->entityTypeManager->getStorage('test_entity_type')->willReturn($storage->reveal());
 
-    $class = get_class($storage);
-    $entity = $this->getMock('Drupal\Core\Entity\EntityTypeInterface');
-    $entity->expects($this->once())
-      ->method('getHandlerClass')
-      ->with('storage')
-      ->will($this->returnValue($class));
+    $entity = $this->prophesize(EntityTypeInterface::class);
     $this->setUpEntityManager(array('test_entity_type' => $entity));
 
-    // The entity manager will instantiate a new object with the given class
-    // name. Define the mock expectations on that.
-    $storage = $this->entityManager->getStorage('test_entity_type');
-    $storage->expects($this->once())
-      ->method('onFieldDefinitionCreate')
-      ->with($field_definition);
-
     // Set up the stored bundle field map.
-    $key_value_store = $this->getMock('Drupal\Core\KeyValueStore\KeyValueStoreInterface');
-    $this->keyValueFactory->expects($this->exactly(2))
-      ->method('get')
-      ->with('entity.definitions.bundle_field_map')
-      ->willReturn($key_value_store);
-    $key_value_store->expects($this->once())
-      ->method('get')
-      ->with('test_entity_type')
-      ->willReturn([]);
-    $key_value_store->expects($this->once())
-      ->method('set')
-      ->with('test_entity_type', [
+    $key_value_store = $this->prophesize(KeyValueStoreInterface::class);
+    $this->keyValueFactory->get('entity.definitions.bundle_field_map')->willReturn($key_value_store->reveal());
+    $key_value_store->get('test_entity_type')->willReturn([]);
+    $key_value_store->set('test_entity_type', [
         'test_field' => [
           'type' => 'test_type',
           'bundles' => ['test_bundle' => 'test_bundle'],
         ],
-      ]);
+      ])->shouldBeCalled();
 
-    $this->entityManager->onFieldDefinitionCreate($field_definition);
+    $this->entityManager->onFieldDefinitionCreate($field_definition->reveal());
   }
 
   /**
    * @covers ::onFieldDefinitionCreate
    */
-  public function testonFieldDefinitionCreateExistingField() {
-    $field_definition = $this->getMock('Drupal\Core\Field\FieldDefinitionInterface');
-    $field_definition->expects($this->atLeastOnce())
-      ->method('getTargetEntityTypeId')
-      ->willReturn('test_entity_type');
-    $field_definition->expects($this->atLeastOnce())
-      ->method('getTargetBundle')
-      ->willReturn('test_bundle');
-    $field_definition->expects($this->atLeastOnce())
-      ->method('getName')
-      ->willReturn('test_field');
+  public function testOnFieldDefinitionCreateExistingField() {
+    $field_definition = $this->prophesize(FieldDefinitionInterface::class);
+    $field_definition->getTargetEntityTypeId()->willReturn('test_entity_type');
+    $field_definition->getTargetBundle()->willReturn('test_bundle');
+    $field_definition->getName()->willReturn('test_field');
 
-    $storage = $this->getMock('Drupal\Core\Entity\DynamicallyFieldableEntityStorageInterface');
-    $class = get_class($storage);
-    $entity = $this->getMock('Drupal\Core\Entity\EntityTypeInterface');
-    $entity->expects($this->once())
-      ->method('getHandlerClass')
-      ->with('storage')
-      ->will($this->returnValue($class));
+    $storage = $this->prophesize(DynamicallyFieldableEntityStorageInterface::class);
+    $storage->onFieldDefinitionCreate($field_definition->reveal())->shouldBeCalledTimes(1);
+    $this->entityTypeManager->getStorage('test_entity_type')->willReturn($storage->reveal());
+
+    $entity = $this->prophesize(EntityTypeInterface::class);
     $this->setUpEntityManager(array('test_entity_type' => $entity));
 
-    // The entity manager will instantiate a new object with the given class
-    // name. Define the mock expectations on that.
-    $storage = $this->entityManager->getStorage('test_entity_type');
-    $storage->expects($this->once())
-      ->method('onFieldDefinitionCreate')
-      ->with($field_definition);
-
     // Set up the stored bundle field map.
-    $key_value_store = $this->getMock('Drupal\Core\KeyValueStore\KeyValueStoreInterface');
-    $this->keyValueFactory->expects($this->exactly(2))
-      ->method('get')
-      ->with('entity.definitions.bundle_field_map')
-      ->willReturn($key_value_store);
-    $key_value_store->expects($this->once())
-      ->method('get')
-      ->with('test_entity_type')
-      ->willReturn([
+    $key_value_store = $this->prophesize(KeyValueStoreInterface::class);
+    $this->keyValueFactory->get('entity.definitions.bundle_field_map')->willReturn($key_value_store->reveal());
+    $key_value_store->get('test_entity_type')->willReturn([
         'test_field' => [
           'type' => 'test_type',
           'bundles' => ['existing_bundle' => 'existing_bundle'],
         ],
       ]);
-    $key_value_store->expects($this->once())
-      ->method('set')
-      ->with('test_entity_type', [
+    $key_value_store->set('test_entity_type', [
         'test_field' => [
           'type' => 'test_type',
           'bundles' => ['existing_bundle' => 'existing_bundle', 'test_bundle' => 'test_bundle'],
         ],
-      ]);
+      ])
+      ->shouldBeCalled();
 
-    $this->entityManager->onFieldDefinitionCreate($field_definition);
+    $this->entityManager->onFieldDefinitionCreate($field_definition->reveal());
   }
 
   /**
    * @covers ::onFieldDefinitionUpdate
    */
-  public function testonFieldDefinitionUpdate() {
-    $field_definition = $this->getMock('Drupal\Core\Field\FieldDefinitionInterface');
-    $field_definition->expects($this->atLeastOnce())
-      ->method('getTargetEntityTypeId')
-      ->willReturn('test_entity_type');
+  public function testOnFieldDefinitionUpdate() {
+    $field_definition = $this->prophesize(FieldDefinitionInterface::class);
+    $field_definition->getTargetEntityTypeId()->willReturn('test_entity_type');
 
-    $storage = $this->getMock('Drupal\Core\Entity\DynamicallyFieldableEntityStorageInterface');
+    $storage = $this->prophesize(DynamicallyFieldableEntityStorageInterface::class);
+    $storage->onFieldDefinitionUpdate($field_definition->reveal(), $field_definition->reveal())->shouldBeCalledTimes(1);
+    $this->entityTypeManager->getStorage('test_entity_type')->willReturn($storage->reveal());
 
-    $class = get_class($storage);
-    $entity = $this->getMock('Drupal\Core\Entity\EntityTypeInterface');
-    $entity->expects($this->once())
-      ->method('getHandlerClass')
-      ->with('storage')
-      ->will($this->returnValue($class));
+    $entity = $this->prophesize(EntityTypeInterface::class);
     $this->setUpEntityManager(array('test_entity_type' => $entity));
 
-    // The entity manager will instantiate a new object with the given class
-    // name. Define the mock expectations on that.
-    $storage = $this->entityManager->getStorage('test_entity_type');
-    $storage->expects($this->once())
-      ->method('onFieldDefinitionUpdate')
-      ->with($field_definition);
-
-    $this->entityManager->onFieldDefinitionUpdate($field_definition, $field_definition);
+    $this->entityManager->onFieldDefinitionUpdate($field_definition->reveal(), $field_definition->reveal());
   }
 
   /**
    * @covers ::onFieldDefinitionDelete
    */
-  public function testonFieldDefinitionDeleteMultipleBundles() {
-    $field_definition = $this->getMock('Drupal\Core\Field\FieldDefinitionInterface');
-    $field_definition->expects($this->atLeastOnce())
-      ->method('getTargetEntityTypeId')
-      ->willReturn('test_entity_type');
-    $field_definition->expects($this->atLeastOnce())
-      ->method('getTargetBundle')
-      ->willReturn('test_bundle');
-    $field_definition->expects($this->atLeastOnce())
-      ->method('getName')
-      ->willReturn('test_field');
+  public function testOnFieldDefinitionDeleteMultipleBundles() {
+    $field_definition = $this->prophesize(FieldDefinitionInterface::class);
+    $field_definition->getTargetEntityTypeId()->willReturn('test_entity_type');
+    $field_definition->getTargetBundle()->willReturn('test_bundle');
+    $field_definition->getName()->willReturn('test_field');
 
-    $storage = $this->getMock('Drupal\Core\Entity\DynamicallyFieldableEntityStorageInterface');
-    $class = get_class($storage);
-    $entity = $this->getMock('Drupal\Core\Entity\EntityTypeInterface');
-    $entity->expects($this->once())
-      ->method('getHandlerClass')
-      ->with('storage')
-      ->will($this->returnValue($class));
+    $storage = $this->prophesize(DynamicallyFieldableEntityStorageInterface::class);
+    $storage->onFieldDefinitionDelete($field_definition->reveal())->shouldBeCalledTimes(1);
+    $this->entityTypeManager->getStorage('test_entity_type')->willReturn($storage->reveal());
+
+    $entity = $this->prophesize(EntityTypeInterface::class);
     $this->setUpEntityManager(array('test_entity_type' => $entity));
 
-    // The entity manager will instantiate a new object with the given class
-    // name. Define the mock expectations on that.
-    $storage = $this->entityManager->getStorage('test_entity_type');
-    $storage->expects($this->once())
-      ->method('onFieldDefinitionDelete')
-      ->with($field_definition);
-
     // Set up the stored bundle field map.
-    $key_value_store = $this->getMock('Drupal\Core\KeyValueStore\KeyValueStoreInterface');
-    $this->keyValueFactory->expects($this->exactly(2))
-      ->method('get')
-      ->with('entity.definitions.bundle_field_map')
-      ->willReturn($key_value_store);
-    $key_value_store->expects($this->once())
-      ->method('get')
-      ->with('test_entity_type')
-      ->willReturn([
+    $key_value_store = $this->prophesize(KeyValueStoreInterface::class);
+    $this->keyValueFactory->get('entity.definitions.bundle_field_map')->willReturn($key_value_store->reveal());
+    $key_value_store->get('test_entity_type')->willReturn([
         'test_field' => [
           'type' => 'test_type',
           'bundles' => ['test_bundle' => 'test_bundle'],
@@ -1657,100 +1091,74 @@ public function testonFieldDefinitionDeleteMultipleBundles() {
           'bundles' => ['test_bundle' => 'test_bundle'],
         ],
       ]);
-    $key_value_store->expects($this->once())
-      ->method('set')
-      ->with('test_entity_type', [
+    $key_value_store->set('test_entity_type', [
         'second_field' => [
           'type' => 'test_type',
           'bundles' => ['test_bundle' => 'test_bundle'],
         ],
-      ]);
+      ])
+      ->shouldBeCalled();
 
-    $this->entityManager->onFieldDefinitionDelete($field_definition);
+    $this->entityManager->onFieldDefinitionDelete($field_definition->reveal());
   }
 
 
   /**
    * @covers ::onFieldDefinitionDelete
    */
-  public function testonFieldDefinitionDeleteSingleBundles() {
-    $field_definition = $this->getMock('Drupal\Core\Field\FieldDefinitionInterface');
-    $field_definition->expects($this->atLeastOnce())
-      ->method('getTargetEntityTypeId')
-      ->willReturn('test_entity_type');
-    $field_definition->expects($this->atLeastOnce())
-      ->method('getTargetBundle')
-      ->willReturn('test_bundle');
-    $field_definition->expects($this->atLeastOnce())
-      ->method('getName')
-      ->willReturn('test_field');
+  public function testOnFieldDefinitionDeleteSingleBundles() {
+    $field_definition = $this->prophesize(FieldDefinitionInterface::class);
+    $field_definition->getTargetEntityTypeId()->willReturn('test_entity_type');
+    $field_definition->getTargetBundle()->willReturn('test_bundle');
+    $field_definition->getName()->willReturn('test_field');
 
-    $storage = $this->getMock('Drupal\Core\Entity\DynamicallyFieldableEntityStorageInterface');
-    $class = get_class($storage);
-    $entity = $this->getMock('Drupal\Core\Entity\EntityTypeInterface');
-    $entity->expects($this->once())
-      ->method('getHandlerClass')
-      ->with('storage')
-      ->will($this->returnValue($class));
+    $storage = $this->prophesize(DynamicallyFieldableEntityStorageInterface::class);
+    $storage->onFieldDefinitionDelete($field_definition->reveal())->shouldBeCalledTimes(1);
+    $this->entityTypeManager->getStorage('test_entity_type')->willReturn($storage->reveal());
+
+    $entity = $this->prophesize(EntityTypeInterface::class);
     $this->setUpEntityManager(array('test_entity_type' => $entity));
 
-    // The entity manager will instantiate a new object with the given class
-    // name. Define the mock expectations on that.
-    $storage = $this->entityManager->getStorage('test_entity_type');
-    $storage->expects($this->once())
-      ->method('onFieldDefinitionDelete')
-      ->with($field_definition);
-
     // Set up the stored bundle field map.
-    $key_value_store = $this->getMock('Drupal\Core\KeyValueStore\KeyValueStoreInterface');
-    $this->keyValueFactory->expects($this->exactly(2))
-      ->method('get')
-      ->with('entity.definitions.bundle_field_map')
-      ->willReturn($key_value_store);
-    $key_value_store->expects($this->once())
-      ->method('get')
-      ->with('test_entity_type')
-      ->willReturn([
+    $key_value_store = $this->prophesize(KeyValueStoreInterface::class);
+    $this->keyValueFactory->get('entity.definitions.bundle_field_map')->willReturn($key_value_store->reveal());
+    $key_value_store->get('test_entity_type')->willReturn([
         'test_field' => [
           'type' => 'test_type',
           'bundles' => ['test_bundle' => 'test_bundle', 'second_bundle' => 'second_bundle'],
         ],
       ]);
-    $key_value_store->expects($this->once())
-      ->method('set')
-      ->with('test_entity_type', [
+    $key_value_store->set('test_entity_type', [
         'test_field' => [
           'type' => 'test_type',
           'bundles' => ['second_bundle' => 'second_bundle'],
         ],
-      ]);
+      ])
+      ->shouldBeCalled();
 
-    $this->entityManager->onFieldDefinitionDelete($field_definition);
+    $this->entityManager->onFieldDefinitionDelete($field_definition->reveal());
   }
 
   /**
    * @covers ::getEntityTypeFromClass
    */
   public function testGetEntityTypeFromClass() {
-    $apple = $this->getMock('Drupal\Core\Entity\EntityTypeInterface');
-    $apple->expects($this->exactly(2))
-      ->method('getOriginalClass')
-      ->will($this->returnValue('\Drupal\apple\Entity\Apple'));
-    $banana = $this->getMock('Drupal\Core\Entity\EntityTypeInterface');
-    $banana->expects($this->exactly(2))
-      ->method('getOriginalClass')
-      ->will($this->returnValue('\Drupal\banana\Entity\Banana'));
-    $banana->expects($this->once())
-      ->method('getClass')
-      ->will($this->returnValue('\Drupal\mango\Entity\Mango'));
-    $banana->expects($this->exactly(2))
-      ->method('id')
-      ->will($this->returnValue('banana'));
+    $apple = $this->prophesize(EntityTypeInterface::class);
+    $banana = $this->prophesize(EntityTypeInterface::class);
+
     $this->setUpEntityManager(array(
       'apple' => $apple,
       'banana' => $banana,
     ));
 
+    $apple->getOriginalClass()->willReturn('\Drupal\apple\Entity\Apple');
+
+    $banana->getOriginalClass()->willReturn('\Drupal\banana\Entity\Banana');
+    $banana->getClass()->willReturn('\Drupal\mango\Entity\Mango');
+    $banana->id()
+      ->willReturn('banana')
+      ->shouldBeCalledTimes(2);
+
     $entity_type_id = $this->entityManager->getEntityTypeFromClass('\Drupal\banana\Entity\Banana');
     $this->assertSame('banana', $entity_type_id);
     $entity_type_id = $this->entityManager->getEntityTypeFromClass('\Drupal\mango\Entity\Mango');
@@ -1764,19 +1172,17 @@ public function testGetEntityTypeFromClass() {
    * @expectedExceptionMessage The \Drupal\pear\Entity\Pear class does not correspond to an entity type.
    */
   public function testGetEntityTypeFromClassNoMatch() {
-    $apple = $this->getMock('Drupal\Core\Entity\EntityTypeInterface');
-    $apple->expects($this->once())
-      ->method('getOriginalClass')
-      ->will($this->returnValue('\Drupal\apple\Entity\Apple'));
-    $banana = $this->getMock('Drupal\Core\Entity\EntityTypeInterface');
-    $banana->expects($this->once())
-      ->method('getOriginalClass')
-      ->will($this->returnValue('\Drupal\banana\Entity\Banana'));
+    $apple = $this->prophesize(EntityTypeInterface::class);
+    $banana = $this->prophesize(EntityTypeInterface::class);
+
     $this->setUpEntityManager(array(
       'apple' => $apple,
       'banana' => $banana,
     ));
 
+    $apple->getOriginalClass()->willReturn('\Drupal\apple\Entity\Apple');
+    $banana->getOriginalClass()->willReturn('\Drupal\banana\Entity\Banana');
+
     $this->entityManager->getEntityTypeFromClass('\Drupal\pear\Entity\Pear');
   }
 
@@ -1787,20 +1193,14 @@ public function testGetEntityTypeFromClassNoMatch() {
    * @expectedExceptionMessage Multiple entity types found for \Drupal\apple\Entity\Apple.
    */
   public function testGetEntityTypeFromClassAmbiguous() {
-    $boskoop = $this->getMock('Drupal\Core\Entity\EntityTypeInterface');
-    $boskoop->expects($this->once())
-      ->method('getOriginalClass')
-      ->will($this->returnValue('\Drupal\apple\Entity\Apple'));
-    $boskoop->expects($this->once())
-      ->method('id')
-      ->will($this->returnValue('boskop'));
-    $gala = $this->getMock('Drupal\Core\Entity\EntityTypeInterface');
-    $gala->expects($this->once())
-      ->method('getOriginalClass')
-      ->will($this->returnValue('\Drupal\apple\Entity\Apple'));
-    $gala->expects($this->once())
-      ->method('id')
-      ->will($this->returnValue('gala'));
+    $boskoop = $this->prophesize(EntityTypeInterface::class);
+    $boskoop->getOriginalClass()->willReturn('\Drupal\apple\Entity\Apple');
+    $boskoop->id()->willReturn('boskop');
+
+    $gala = $this->prophesize(EntityTypeInterface::class);
+    $gala->getOriginalClass()->willReturn('\Drupal\apple\Entity\Apple');
+    $gala->id()->willReturn('gala');
+
     $this->setUpEntityManager(array(
       'boskoop' => $boskoop,
       'gala' => $gala,
@@ -1809,37 +1209,9 @@ public function testGetEntityTypeFromClassAmbiguous() {
     $this->entityManager->getEntityTypeFromClass('\Drupal\apple\Entity\Apple');
   }
 
-  /**
-   * @covers ::getRouteProviders
-   */
-  public function testGetRouteProviders() {
-    $apple = $this->getMock('Drupal\Core\Entity\EntityTypeInterface');
-    $apple->expects($this->once())
-      ->method('getRouteProviderClasses')
-      ->willReturn(['default' => 'Drupal\Tests\Core\Entity\TestRouteProvider']);
-    $this->setUpEntityManager(array(
-      'apple' => $apple,
-    ));
-
-    $apple_route_provider = $this->entityManager->getRouteProviders('apple');
-    $this->assertInstanceOf('Drupal\Tests\Core\Entity\TestRouteProvider', $apple_route_provider['default']);
-    $this->assertAttributeInstanceOf('Drupal\Core\Extension\ModuleHandlerInterface', 'moduleHandler', $apple_route_provider['default']);
-    $this->assertAttributeInstanceOf('Drupal\Core\StringTranslation\TranslationInterface', 'stringTranslation', $apple_route_provider['default']);
-  }
-
-  /**
-   * Gets a mock controller class name.
-   *
-   * @return string
-   *   A mock controller class name.
-   */
-  protected function getTestHandlerClass() {
-    return get_class($this->getMockForAbstractClass('Drupal\Core\Entity\EntityHandlerBase'));
-  }
-
 }
 
-/*
+/**
  * Provides a content entity with dummy static method implementations.
  */
 abstract class EntityManagerTestEntity implements \Iterator, ContentEntityInterface {
@@ -1877,29 +1249,11 @@ public static function bundleFieldDefinitions(EntityTypeInterface $entity_type,
 }
 
 /**
- * Provides a testable version of ContentEntityInterface.
- *
- * @see https://github.com/sebastianbergmann/phpunit-mock-objects/commit/96a6794
- */
-interface TestContentEntityInterface extends \Iterator, ContentEntityInterface {
-}
-
-/**
  * Provides a testing version of EntityManager with an empty constructor.
  */
 class TestEntityManager extends EntityManager {
 
   /**
-   * Sets the discovery for the manager.
-   *
-   * @param \Drupal\Component\Plugin\Discovery\DiscoveryInterface $discovery
-   *   The discovery object.
-   */
-  public function setDiscovery(DiscoveryInterface $discovery) {
-    $this->discovery = $discovery;
-  }
-
-  /**
    * Allows the static caches to be cleared.
    */
   public function testClearEntityFieldInfo() {
@@ -1909,153 +1263,3 @@ public function testClearEntityFieldInfo() {
   }
 
 }
-
-/**
- * Provides a test entity handler that uses injection.
- */
-class TestEntityHandlerInjected implements EntityHandlerInterface {
-
-  /**
-   * The color of the entity type.
-   *
-   * @var string
-   */
-  protected $color;
-
-  /**
-   * Constructs a new TestEntityHandlerInjected.
-   *
-   * @param string $color
-   *   The color of the entity type.
-   */
-  public function __construct($color) {
-    $this->color = $color;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
-    return new static('yellow');
-  }
-
-}
-
-/**
- * Provides a test entity form.
- */
-class TestEntityForm extends EntityHandlerBase {
-
-  /**
-   * The entity manager.
-   *
-   * @var \Drupal\Tests\Core\Entity\TestEntityManager
-   */
-  protected $entityManager;
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getBaseFormId() {
-    return 'the_base_form_id';
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getFormId() {
-    return 'the_form_id';
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function setEntity(EntityInterface $entity) {
-    return $this;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function setOperation($operation) {
-    return $this;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function setEntityManager(EntityManagerInterface $entity_manager) {
-    $this->entityManager = $entity_manager;
-    return $this;
-  }
-
-}
-
-/**
- * Provides a test entity form that uses injection.
- */
-class TestEntityFormInjected extends TestEntityForm implements ContainerInjectionInterface {
-
-  /**
-   * The color of the entity type.
-   *
-   * @var string
-   */
-  protected $color;
-
-  /**
-   * Constructs a new TestEntityFormInjected.
-   *
-   * @param string $color
-   *   The color of the entity type.
-   */
-  public function __construct($color) {
-    $this->color = $color;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public static function create(ContainerInterface $container) {
-    return new static('yellow');
-  }
-
-}
-
-/**
- * Provides a test entity route provider.
- */
-class TestRouteProvider extends EntityHandlerBase {
-
-}
-
-
-/**
- * Provides a test config entity storage for base field overrides.
- */
-class TestConfigEntityStorage extends ConfigEntityStorage {
-
-  public function __construct($entity_type) {
-  }
-
-  public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_type) {
-    return new static(
-      $entity_type
-    );
-  }
-
-  public function loadMultiple(array $ids = NULL) {
-    return array();
-  }
-}
-
-}
-
-namespace {
-
-  /**
-   * Implements hook_entity_type_build().
-   */
-  function entity_manager_test_module_entity_type_build() {
-  }
-}
diff --git a/core/tests/Drupal/Tests/Core/Entity/EntityTypeManagerTest.php b/core/tests/Drupal/Tests/Core/Entity/EntityTypeManagerTest.php
new file mode 100644
index 0000000..d1bde6f
--- /dev/null
+++ b/core/tests/Drupal/Tests/Core/Entity/EntityTypeManagerTest.php
@@ -0,0 +1,513 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Tests\Core\Entity\EntityTypeManagerTest.
+ */
+
+namespace Drupal\Tests\Core\Entity {
+
+use Drupal\Component\Plugin\Discovery\DiscoveryInterface;
+use Drupal\Component\Plugin\Exception\PluginNotFoundException;
+use Drupal\Core\Cache\CacheBackendInterface;
+use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
+use Drupal\Core\Entity\EntityHandlerBase;
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Entity\EntityManagerInterface;
+use Drupal\Core\Entity\EntityTypeInterface;
+use Drupal\Core\Entity\EntityTypeManager;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\Core\Extension\ModuleHandlerInterface;
+use Drupal\Core\StringTranslation\TranslationInterface;
+use Drupal\Tests\UnitTestCase;
+use Prophecy\Argument;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * @coversDefaultClass \Drupal\Core\Entity\EntityTypeManager
+ * @group Entity
+ */
+class EntityTypeManagerTest extends UnitTestCase {
+
+  /**
+   * @var \Drupal\Core\Entity\EntityTypeManager
+   */
+  protected $entityTypeManager;
+
+  /**
+   * The translation manager.
+   *
+   * @var \Drupal\Core\StringTranslation\TranslationInterface|\Prophecy\Prophecy\ProphecyInterface
+   */
+  protected $translationManager;
+
+  /**
+   * The plugin discovery.
+   *
+   * @var \Drupal\Component\Plugin\Discovery\DiscoveryInterface|\Prophecy\Prophecy\ProphecyInterface
+   */
+  protected $discovery;
+
+  /**
+   * The module handler.
+   *
+   * @var \Drupal\Core\Extension\ModuleHandlerInterface|\Prophecy\Prophecy\ProphecyInterface
+   */
+  protected $moduleHandler;
+
+  /**
+   * The cache backend.
+   *
+   * @var \Drupal\Core\Cache\CacheBackendInterface|\Prophecy\Prophecy\ProphecyInterface
+   */
+  protected $cacheBackend;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    $this->moduleHandler = $this->prophesize(ModuleHandlerInterface::class);
+    $this->moduleHandler->getImplementations('entity_type_build')->willReturn([]);
+    $this->moduleHandler->alter('entity_type', Argument::type('array'))->willReturn(NULL);
+
+    $this->cacheBackend = $this->prophesize(CacheBackendInterface::class);
+    $this->translationManager = $this->prophesize(TranslationInterface::class);
+
+    $this->entityTypeManager = new TestEntityTypeManager(new \ArrayObject(), $this->moduleHandler->reveal(), $this->cacheBackend->reveal(), $this->translationManager->reveal(), $this->getClassResolverStub());
+    $this->discovery = $this->prophesize(DiscoveryInterface::class);
+    $this->entityTypeManager->setDiscovery($this->discovery->reveal());
+  }
+
+  /**
+   * Sets up the entity type manager to be tested.
+   *
+   * @param \Drupal\Core\Entity\EntityTypeInterface[]|\Prophecy\Prophecy\ProphecyInterface[] $definitions
+   *   (optional) An array of entity type definitions.
+   */
+  protected function setUpEntityTypeDefinitions($definitions = []) {
+    $class = $this->getMockClass(EntityInterface::class);
+    foreach ($definitions as $key => $entity_type) {
+      // \Drupal\Core\Entity\EntityTypeInterface::getLinkTemplates() is called
+      // by \Drupal\Core\Entity\EntityManager::processDefinition() so it must
+      // always be mocked.
+      $entity_type->getLinkTemplates()->willReturn([]);
+
+      // Give the entity type a legitimate class to return.
+      $entity_type->getClass()->willReturn($class);
+
+      $definitions[$key] = $entity_type->reveal();
+    }
+
+    $this->discovery->getDefinition(Argument::cetera())
+      ->will(function ($args) use ($definitions) {
+        $entity_type_id = $args[0];
+        $exception_on_invalid = $args[1];
+        if (isset($definitions[$entity_type_id])) {
+          return $definitions[$entity_type_id];
+        }
+        elseif (!$exception_on_invalid) {
+          return NULL;
+        }
+        else throw new PluginNotFoundException($entity_type_id);
+      });
+    $this->discovery->getDefinitions()->willReturn($definitions);
+
+  }
+
+  /**
+   * Tests the hasHandler() method.
+   *
+   * @covers ::hasHandler
+   *
+   * @dataProvider providerTestHasHandler
+   */
+  public function testHasHandler($entity_type_id, $expected) {
+    $apple = $this->prophesize(EntityTypeInterface::class);
+    $apple->hasHandlerClass('storage')->willReturn(TRUE);
+
+    $banana = $this->prophesize(EntityTypeInterface::class);
+    $banana->hasHandlerClass('storage')->willReturn(FALSE);
+
+    $this->setUpEntityTypeDefinitions(array(
+      'apple' => $apple,
+      'banana' => $banana,
+    ));
+
+    $entity_type = $this->entityTypeManager->hasHandler($entity_type_id, 'storage');
+    $this->assertSame($expected, $entity_type);
+  }
+
+  /**
+   * Provides test data for testHasHandler().
+   *
+   * @return array
+   *   Test data.
+   */
+  public function providerTestHasHandler() {
+    return array(
+      array('apple', TRUE),
+      array('banana', FALSE),
+      array('pear', FALSE),
+    );
+  }
+
+  /**
+   * Tests the getStorage() method.
+   *
+   * @covers ::getStorage
+   */
+  public function testGetStorage() {
+    $class = $this->getTestHandlerClass();
+    $entity = $this->prophesize(EntityTypeInterface::class);
+    $entity->getHandlerClass('storage')->willReturn($class);
+    $this->setUpEntityTypeDefinitions(array('test_entity_type' => $entity));
+
+    $this->assertInstanceOf($class, $this->entityTypeManager->getStorage('test_entity_type'));
+  }
+
+  /**
+   * Tests the getListBuilder() method.
+   *
+   * @covers ::getListBuilder
+   */
+  public function testGetListBuilder() {
+    $class = $this->getTestHandlerClass();
+    $entity = $this->prophesize(EntityTypeInterface::class);
+    $entity->getHandlerClass('list_builder')->willReturn($class);
+    $this->setUpEntityTypeDefinitions(array('test_entity_type' => $entity));
+
+    $this->assertInstanceOf($class, $this->entityTypeManager->getListBuilder('test_entity_type'));
+  }
+
+  /**
+   * Tests the getViewBuilder() method.
+   *
+   * @covers ::getViewBuilder
+   */
+  public function testGetViewBuilder() {
+    $class = $this->getTestHandlerClass();
+    $entity = $this->prophesize(EntityTypeInterface::class);
+    $entity->getHandlerClass('view_builder')->willReturn($class);
+    $this->setUpEntityTypeDefinitions(array('test_entity_type' => $entity));
+
+    $this->assertInstanceOf($class, $this->entityTypeManager->getViewBuilder('test_entity_type'));
+  }
+
+  /**
+   * Tests the getAccessControlHandler() method.
+   *
+   * @covers ::getAccessControlHandler
+   */
+  public function testGetAccessControlHandler() {
+    $class = $this->getTestHandlerClass();
+    $entity = $this->prophesize(EntityTypeInterface::class);
+    $entity->getHandlerClass('access')->willReturn($class);
+    $this->setUpEntityTypeDefinitions(array('test_entity_type' => $entity));
+
+    $this->assertInstanceOf($class, $this->entityTypeManager->getAccessControlHandler('test_entity_type'));
+  }
+
+  /**
+   * Tests the getFormObject() method.
+   *
+   * @covers ::getFormObject
+   */
+  public function testGetFormObject() {
+    $entity_manager = $this->prophesize(EntityManagerInterface::class);
+    $container = $this->prophesize(ContainerInterface::class);
+    $container->get('entity.manager')->willReturn($entity_manager->reveal());
+    \Drupal::setContainer($container->reveal());
+
+    $apple = $this->prophesize(EntityTypeInterface::class);
+    $apple->getFormClass('default')->willReturn(TestEntityForm::class);
+
+    $banana = $this->prophesize(EntityTypeInterface::class);
+    $banana->getFormClass('default')->willReturn(TestEntityFormInjected::class);
+
+    $this->setUpEntityTypeDefinitions(array(
+      'apple' => $apple,
+      'banana' => $banana,
+    ));
+
+    $apple_form = $this->entityTypeManager->getFormObject('apple', 'default');
+    $this->assertInstanceOf(TestEntityForm::class, $apple_form);
+    $this->assertAttributeInstanceOf(ModuleHandlerInterface::class, 'moduleHandler', $apple_form);
+    $this->assertAttributeInstanceOf(TranslationInterface::class, 'stringTranslation', $apple_form);
+
+    $banana_form = $this->entityTypeManager->getFormObject('banana', 'default');
+    $this->assertInstanceOf(TestEntityFormInjected::class, $banana_form);
+    $this->assertAttributeEquals('yellow', 'color', $banana_form);
+
+  }
+
+  /**
+   * Tests the getFormObject() method with an invalid operation.
+   *
+   * @covers ::getFormObject
+   *
+   * @expectedException \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
+   */
+  public function testGetFormObjectInvalidOperation() {
+    $entity = $this->prophesize(EntityTypeInterface::class);
+    $entity->getFormClass('edit')->willReturn('');
+    $this->setUpEntityTypeDefinitions(array('test_entity_type' => $entity));
+
+    $this->entityTypeManager->getFormObject('test_entity_type', 'edit');
+  }
+
+  /**
+   * Tests the getHandler() method.
+   *
+   * @covers ::getHandler
+   */
+  public function testGetHandler() {
+    $class = $this->getTestHandlerClass();
+    $apple = $this->prophesize(EntityTypeInterface::class);
+    $apple->getHandlerClass('storage')->willReturn($class);
+
+    $this->setUpEntityTypeDefinitions(array(
+      'apple' => $apple,
+    ));
+
+    $apple_controller = $this->entityTypeManager->getHandler('apple', 'storage');
+    $this->assertInstanceOf($class, $apple_controller);
+    $this->assertAttributeInstanceOf(ModuleHandlerInterface::class, 'moduleHandler', $apple_controller);
+    $this->assertAttributeInstanceOf(TranslationInterface::class, 'stringTranslation', $apple_controller);
+  }
+
+  /**
+   * Tests the getHandler() method when no controller is defined.
+   *
+   * @covers ::getHandler
+   *
+   * @expectedException \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
+   */
+  public function testGetHandlerMissingHandler() {
+    $entity = $this->prophesize(EntityTypeInterface::class);
+    $entity->getHandlerClass('storage')->willReturn('');
+    $this->setUpEntityTypeDefinitions(array('test_entity_type' => $entity));
+    $this->entityTypeManager->getHandler('test_entity_type', 'storage');
+  }
+
+  /**
+   * @covers ::getRouteProviders
+   */
+  public function testGetRouteProviders() {
+    $apple = $this->prophesize(EntityTypeInterface::class);
+    $apple->getRouteProviderClasses()->willReturn(['default' => TestRouteProvider::class]);
+
+    $this->setUpEntityTypeDefinitions(array(
+      'apple' => $apple,
+    ));
+
+    $apple_route_provider = $this->entityTypeManager->getRouteProviders('apple');
+    $this->assertInstanceOf(TestRouteProvider::class, $apple_route_provider['default']);
+    $this->assertAttributeInstanceOf(ModuleHandlerInterface::class, 'moduleHandler', $apple_route_provider['default']);
+    $this->assertAttributeInstanceOf(TranslationInterface::class, 'stringTranslation', $apple_route_provider['default']);
+  }
+
+  /**
+   * Tests the processDefinition() method.
+   *
+   * @covers ::processDefinition
+   *
+   * @expectedException \Drupal\Core\Entity\Exception\InvalidLinkTemplateException
+   * @expectedExceptionMessage Link template 'canonical' for entity type 'apple' must start with a leading slash, the current link template is 'path/to/apple'
+   */
+  public function testProcessDefinition() {
+    $apple = $this->prophesize(EntityTypeInterface::class);
+    $this->setUpEntityTypeDefinitions(array('apple' => $apple));
+
+    $apple->getLinkTemplates()->willReturn(['canonical' => 'path/to/apple']);
+
+    $definition = $apple->reveal();
+    $this->entityTypeManager->processDefinition($definition, 'apple');
+  }
+
+  /**
+   * Tests the getDefinition() method.
+   *
+   * @covers ::getDefinition
+   *
+   * @dataProvider providerTestGetDefinition
+   */
+  public function testGetDefinition($entity_type_id, $expected) {
+    $entity = $this->prophesize(EntityTypeInterface::class);
+
+    $this->setUpEntityTypeDefinitions(array(
+      'apple' => $entity,
+      'banana' => $entity,
+    ));
+
+    $entity_type = $this->entityTypeManager->getDefinition($entity_type_id, FALSE);
+    if ($expected) {
+      $this->assertInstanceOf(EntityTypeInterface::class, $entity_type);
+    }
+    else {
+      $this->assertNull($entity_type);
+    }
+  }
+
+  /**
+   * Provides test data for testGetDefinition().
+   *
+   * @return array
+   *   Test data.
+   */
+  public function providerTestGetDefinition() {
+    return array(
+      array('apple', TRUE),
+      array('banana', TRUE),
+      array('pear', FALSE),
+    );
+  }
+
+  /**
+   * Tests the getDefinition() method with an invalid definition.
+   *
+   * @covers ::getDefinition
+   *
+   * @expectedException \Drupal\Component\Plugin\Exception\PluginNotFoundException
+   * @expectedExceptionMessage The "pear" entity type does not exist.
+   */
+  public function testGetDefinitionInvalidException() {
+    $this->setUpEntityTypeDefinitions();
+
+    $this->entityTypeManager->getDefinition('pear', TRUE);
+  }
+  protected function getTestHandlerClass() {
+    return get_class($this->getMockForAbstractClass(EntityHandlerBase::class));
+  }
+
+}
+
+class TestEntityTypeManager extends EntityTypeManager {
+
+  /**
+   * Sets the discovery for the manager.
+   *
+   * @param \Drupal\Component\Plugin\Discovery\DiscoveryInterface $discovery
+   *   The discovery object.
+   */
+  public function setDiscovery(DiscoveryInterface $discovery) {
+    $this->discovery = $discovery;
+  }
+
+}
+
+/**
+ * Provides a test entity form.
+ */
+class TestEntityForm extends EntityHandlerBase {
+
+  /**
+   * The entity manager.
+   *
+   * @var \Drupal\Core\Entity\EntityManagerInterface
+   */
+  protected $entityManager;
+
+  /**
+   * The entity type manager.
+   *
+   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
+   */
+  protected $entityTypeManager;
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getBaseFormId() {
+    return 'the_base_form_id';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormId() {
+    return 'the_form_id';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setEntity(EntityInterface $entity) {
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setOperation($operation) {
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setEntityManager(EntityManagerInterface $entity_manager) {
+    $this->entityManager = $entity_manager;
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setEntityTypeManager(EntityTypeManagerInterface $entity_type_manager) {
+    $this->entityTypeManager = $entity_type_manager;
+    return $this;
+  }
+
+}
+
+/**
+ * Provides a test entity form that uses injection.
+ */
+class TestEntityFormInjected extends TestEntityForm implements ContainerInjectionInterface {
+
+  /**
+   * The color of the entity type.
+   *
+   * @var string
+   */
+  protected $color;
+
+  /**
+   * Constructs a new TestEntityFormInjected.
+   *
+   * @param string $color
+   *   The color of the entity type.
+   */
+  public function __construct($color) {
+    $this->color = $color;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static('yellow');
+  }
+
+}
+
+/**
+ * Provides a test entity route provider.
+ */
+class TestRouteProvider extends EntityHandlerBase {
+
+}
+
+}
+
+namespace {
+
+  /**
+   * Implements hook_entity_type_build().
+   */
+  function entity_manager_test_module_entity_type_build() {
+  }
+
+}
