diff --git a/core/lib/Drupal/Core/Entity/EntityManagerInterface.php b/core/lib/Drupal/Core/Entity/EntityManagerInterface.php
index 18c7c8b..4d602be 100644
--- a/core/lib/Drupal/Core/Entity/EntityManagerInterface.php
+++ b/core/lib/Drupal/Core/Entity/EntityManagerInterface.php
@@ -124,6 +124,8 @@ public function getAccessControlHandler($entity_type);
    *
    * @return \Drupal\Core\Entity\EntityStorageInterface
    *   A storage instance.
+   *
+   * @throws \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
    */
   public function getStorage($entity_type);
 
diff --git a/core/lib/Drupal/Core/ParamConverter/AdminPathConfigEntityConverter.php b/core/lib/Drupal/Core/ParamConverter/AdminPathConfigEntityConverter.php
index 7213f16..45a6c61 100644
--- a/core/lib/Drupal/Core/ParamConverter/AdminPathConfigEntityConverter.php
+++ b/core/lib/Drupal/Core/ParamConverter/AdminPathConfigEntityConverter.php
@@ -64,8 +64,18 @@ public function __construct(EntityManagerInterface $entity_manager, ConfigFactor
    * {@inheritdoc}
    */
   public function convert($value, $definition, $name, array $defaults, Request $request) {
-    $entity_type = substr($definition['type'], strlen('entity:'));
-    if ($storage = $this->entityManager->getStorage($entity_type)) {
+    $entity_type_id = $this->getEntityTypeFromDefaults($definition, $name, $defaults);
+
+    // If the entity type is dynamic, confirm it to be a config entity. Static
+    // entity types will have performed this check in self::applies().
+    if (strpos($definition['type'], 'entity:{') === 0) {
+      $entity_type = $this->entityManager->getDefinition($entity_type_id);
+      if (!$entity_type->isSubclassOf('\Drupal\Core\Config\Entity\ConfigEntityInterface')) {
+        return parent::convert($value, $definition, $name, $defaults, $request);
+      }
+    }
+
+    if ($storage = $this->entityManager->getStorage($entity_type_id)) {
       // Make sure no overrides are loaded.
       $old_state = $this->configFactory->getOverrideState();
       $this->configFactory->setOverrideState(FALSE);
@@ -80,9 +90,13 @@ public function convert($value, $definition, $name, array $defaults, Request $re
    */
   public function applies($definition, $name, Route $route) {
     if (parent::applies($definition, $name, $route)) {
+      $entity_type_id = substr($definition['type'], strlen('entity:'));
+      // If the entity type is dynamic, defer checking to self::convert().
+      if (strpos($entity_type_id, '{') === 0) {
+        return TRUE;
+      }
       // As we only want to override EntityConverter for ConfigEntities, find
       // out whether the current entity is a ConfigEntity.
-      $entity_type_id = substr($definition['type'], strlen('entity:'));
       $entity_type = $this->entityManager->getDefinition($entity_type_id);
       if ($entity_type->isSubclassOf('\Drupal\Core\Config\Entity\ConfigEntityInterface')) {
         return $this->adminContext->isAdminRoute($route);
diff --git a/core/lib/Drupal/Core/ParamConverter/EntityConverter.php b/core/lib/Drupal/Core/ParamConverter/EntityConverter.php
index 04665e6..aee0519 100644
--- a/core/lib/Drupal/Core/ParamConverter/EntityConverter.php
+++ b/core/lib/Drupal/Core/ParamConverter/EntityConverter.php
@@ -13,6 +13,27 @@
 
 /**
  * Parameter converter for upcasting entity ids to full objects.
+ *
+ * In order to use it you should specify some additional options in your route:
+ * @code
+ * example.route:
+ *   path: foo/{example}
+ *   options:
+ *     parameters:
+ *       example:
+ *         type: entity:node
+ * @endcode
+ *
+ * If you want to have the entity type itself dynamic in the url you can
+ * specify it like the following:
+ * @code
+ * example.route:
+ *   path: foo/{entity_type}/{example}
+ *   options:
+ *     parameters:
+ *       example:
+ *         type: entity:{entity_type}
+ * @endcode
  */
 class EntityConverter implements ParamConverterInterface {
 
@@ -26,7 +47,7 @@ class EntityConverter implements ParamConverterInterface {
   /**
    * Constructs a new EntityConverter.
    *
-   * @param \Drupal\Core\Entity\EntityManagerInterface $entityManager
+   * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
    *   The entity manager.
    */
   public function __construct(EntityManagerInterface $entity_manager) {
@@ -37,8 +58,8 @@ public function __construct(EntityManagerInterface $entity_manager) {
    * {@inheritdoc}
    */
   public function convert($value, $definition, $name, array $defaults, Request $request) {
-    $entity_type = substr($definition['type'], strlen('entity:'));
-    if ($storage = $this->entityManager->getStorage($entity_type)) {
+    $entity_type_id = $this->getEntityTypeFromDefaults($definition, $name, $defaults);
+    if ($storage = $this->entityManager->getStorage($entity_type_id)) {
       return $storage->load($value);
     }
   }
@@ -48,10 +69,44 @@ public function convert($value, $definition, $name, array $defaults, Request $re
    */
   public function applies($definition, $name, Route $route) {
     if (!empty($definition['type']) && strpos($definition['type'], 'entity:') === 0) {
-      $entity_type = substr($definition['type'], strlen('entity:'));
-      return $this->entityManager->hasDefinition($entity_type);
+      $entity_type_id = substr($definition['type'], strlen('entity:'));
+      if (strpos($definition['type'], '{') !== FALSE) {
+        $entity_type_slug = substr($entity_type_id, 1, -1);
+        return $name != $entity_type_slug && in_array($entity_type_slug, $route->compile()->getVariables(), TRUE);
+      }
+      return $this->entityManager->hasDefinition($entity_type_id);
     }
     return FALSE;
   }
 
+  /**
+   * Determines the entity type ID given a route definition and route defaults.
+   *
+   * @param mixed $definition
+   *   The parameter definition provided in the route options.
+   * @param string $name
+   *   The name of the parameter.
+   * @param array $defaults
+   *   The route defaults array.
+   *
+   * @throws \Drupal\Core\ParamConverter\ParamNotConvertedException
+   *   Thrown when the dynamic entity type is not found in the route defaults.
+   *
+   * @return string
+   *   The entity type ID.
+   */
+  protected function getEntityTypeFromDefaults($definition, $name, array $defaults) {
+    $entity_type_id = substr($definition['type'], strlen('entity:'));
+
+    // If the entity type is dynamic, it will be pulled from the route defaults.
+    if (strpos($entity_type_id, '{') === 0) {
+      $entity_type_slug = substr($entity_type_id, 1, -1);
+      if (!isset($defaults[$entity_type_slug])) {
+        throw new ParamNotConvertedException(sprintf('The "%s" parameter was not converted because the "%s" parameter is missing', $name, $entity_type_slug));
+      }
+      $entity_type_id = $defaults[$entity_type_slug];
+    }
+    return $entity_type_id;
+  }
+
 }
diff --git a/core/modules/editor/editor.routing.yml b/core/modules/editor/editor.routing.yml
index 8b5e5c1..dc41d18 100644
--- a/core/modules/editor/editor.routing.yml
+++ b/core/modules/editor/editor.routing.yml
@@ -11,6 +11,9 @@ editor.field_untransformed_text:
     _controller: '\Drupal\editor\EditorController::getUntransformedText'
   options:
     _theme: ajax_base_page
+    parameters:
+      entity:
+        type: entity:{entity_type}
   requirements:
     _permission: 'access in-place editing'
     _access_quickedit_entity_field: 'TRUE'
diff --git a/core/modules/quickedit/quickedit.routing.yml b/core/modules/quickedit/quickedit.routing.yml
index 9e143b7..0a13988 100644
--- a/core/modules/quickedit/quickedit.routing.yml
+++ b/core/modules/quickedit/quickedit.routing.yml
@@ -21,6 +21,9 @@ quickedit.field_form:
   options:
     _access_mode: 'ALL'
     _theme: ajax_base_page
+    parameters:
+      entity:
+        type: entity:{entity_type}
   requirements:
     _permission: 'access in-place editing'
     _access_quickedit_entity_field: 'TRUE'
@@ -31,4 +34,8 @@ quickedit.entity_save:
     _controller: '\Drupal\quickedit\QuickEditController::entitySave'
   requirements:
     _permission: 'access in-place editing'
-    _access_quickedit_entity: 'TRUE'
+    _entity_access: 'entity.update'
+  options:
+    parameters:
+      entity:
+        type: entity:{entity_type}
diff --git a/core/modules/quickedit/quickedit.services.yml b/core/modules/quickedit/quickedit.services.yml
index 933f255..692ba2f 100644
--- a/core/modules/quickedit/quickedit.services.yml
+++ b/core/modules/quickedit/quickedit.services.yml
@@ -4,14 +4,8 @@ services:
     parent: default_plugin_manager
   access_check.quickedit.entity_field:
     class: Drupal\quickedit\Access\EditEntityFieldAccessCheck
-    arguments: ['@entity.manager']
     tags:
       - { name: access_check, applies_to: _access_quickedit_entity_field }
-  access_check.quickedit.entity:
-    class: Drupal\quickedit\Access\EditEntityAccessCheck
-    arguments: ['@entity.manager']
-    tags:
-      - { name: access_check, applies_to: _access_quickedit_entity }
   quickedit.editor.selector:
     class: Drupal\quickedit\EditorSelector
     arguments: ['@plugin.manager.quickedit.editor', '@plugin.manager.field.formatter']
diff --git a/core/modules/quickedit/src/Access/EditEntityAccessCheck.php b/core/modules/quickedit/src/Access/EditEntityAccessCheck.php
deleted file mode 100644
index a80aa2e..0000000
--- a/core/modules/quickedit/src/Access/EditEntityAccessCheck.php
+++ /dev/null
@@ -1,90 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\quickedit\Access\EditEntityAccessCheck.
- */
-
-namespace Drupal\quickedit\Access;
-
-use Drupal\Core\Entity\EntityManagerInterface;
-use Drupal\Core\Routing\Access\AccessInterface;
-use Drupal\Core\Session\AccountInterface;
-use Symfony\Component\HttpFoundation\Request;
-use Drupal\Core\Entity\EntityInterface;
-
-/**
- * Access check for editing entities with QuickEdit.
- */
-class EditEntityAccessCheck implements AccessInterface {
-
-  /**
-   * The entity manager.
-   *
-   * @var \Drupal\Core\Entity\EntityManagerInterface
-   */
-  protected $entityManager;
-
-  /**
-   * Constructs a EditEntityAccessCheck object.
-   *
-   * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
-   *   The entity manager.
-   */
-  public function __construct(EntityManagerInterface $entity_manager) {
-    $this->entityManager = $entity_manager;
-  }
-
-  /**
-   * Checks Quick Edit access to the entity.
-   *
-   * @param \Symfony\Component\HttpFoundation\Request $request
-   *   The request object.
-   * @param \Drupal\Core\Session\AccountInterface $account
-   *   The currently logged in account.
-   *
-   * @return string
-   *   A \Drupal\Core\Access\AccessInterface constant value.
-   *
-   * @todo Replace $request parameter with $entity once
-   *   https://drupal.org/node/1837388 is fixed.
-   */
-  public function access(Request $request, AccountInterface $account) {
-    if (!$this->validateAndUpcastRequestAttributes($request)) {
-      return static::KILL;
-    }
-
-    return $this->accessEditEntity($request->attributes->get('entity'), $account)  ? static::ALLOW : static::DENY;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  protected function accessEditEntity(EntityInterface $entity, $account) {
-    return $entity->access('update', $account);
-  }
-
-  /**
-   * Validates and upcasts request attributes.
-   *
-   * @todo Remove once https://drupal.org/node/1837388 is fixed.
-   */
-  protected function validateAndUpcastRequestAttributes(Request $request) {
-    // Load the entity.
-    if (!is_object($entity = $request->attributes->get('entity'))) {
-      $entity_id = $entity;
-      $entity_type = $request->attributes->get('entity_type');
-      if (!$entity_type || !$this->entityManager->getDefinition($entity_type)) {
-        return FALSE;
-      }
-      $entity = $this->entityManager->getStorage($entity_type)->load($entity_id);
-      if (!$entity) {
-        return FALSE;
-      }
-      $request->attributes->set('entity', $entity);
-    }
-
-    return TRUE;
-  }
-
-}
diff --git a/core/modules/quickedit/src/Access/EditEntityFieldAccessCheck.php b/core/modules/quickedit/src/Access/EditEntityFieldAccessCheck.php
index 6eaba8a..9edb49e 100644
--- a/core/modules/quickedit/src/Access/EditEntityFieldAccessCheck.php
+++ b/core/modules/quickedit/src/Access/EditEntityFieldAccessCheck.php
@@ -7,10 +7,8 @@
 
 namespace Drupal\quickedit\Access;
 
-use Drupal\Core\Entity\EntityManagerInterface;
 use Drupal\Core\Routing\Access\AccessInterface;
 use Drupal\Core\Session\AccountInterface;
-use Symfony\Component\HttpFoundation\Request;
 use Drupal\Core\Entity\EntityInterface;
 
 /**
@@ -19,46 +17,28 @@
 class EditEntityFieldAccessCheck implements AccessInterface, EditEntityFieldAccessCheckInterface {
 
   /**
-   * The entity manager.
-   *
-   * @var \Drupal\Core\Entity\EntityManagerInterface
-   */
-  protected $entityManager;
-
-  /**
-   * Constructs a EditEntityFieldAccessCheck object.
-   *
-   * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
-   *   The entity manager.
-   */
-  public function __construct(EntityManagerInterface $entity_manager) {
-    $this->entityManager = $entity_manager;
-  }
-
-  /**
    * Checks Quick Edit access to the field.
    *
-   * @param \Symfony\Component\HttpFoundation\Request $request
-   *   The request object.
-   * @param string $field_name.
+   * @param \Drupal\Core\Entity\EntityInterface $entity
+   *   The entity containing the field.
+   * @param string $field_name
    *   The field name.
+   * @param string $langcode
+   *   The langcode.
    * @param \Drupal\Core\Session\AccountInterface $account
    *   The currently logged in account.
    *
    * @return string
    *   A \Drupal\Core\Access\AccessInterface constant value.
    *
-   * @todo Replace $request parameter with $entity once
-   *   https://drupal.org/node/1837388 is fixed.
-   *
    * @todo Use the $account argument: https://drupal.org/node/2266809.
    */
-  public function access(Request $request, $field_name, AccountInterface $account) {
-    if (!$this->validateAndUpcastRequestAttributes($request)) {
+  public function access(EntityInterface $entity, $field_name, $langcode, AccountInterface $account) {
+    if (!$this->validateRequestAttributes($entity, $field_name, $langcode)) {
       return static::KILL;
     }
 
-    return $this->accessEditEntityField($request->attributes->get('entity'), $field_name)  ? static::ALLOW : static::DENY;
+    return $this->accessEditEntityField($entity, $field_name) ? static::ALLOW : static::DENY;
   }
 
   /**
@@ -69,31 +49,13 @@ public function accessEditEntityField(EntityInterface $entity, $field_name) {
   }
 
   /**
-   * Validates and upcasts request attributes.
-   *
-   * @todo Remove once https://drupal.org/node/1837388 is fixed.
+   * Validates request attributes.
    */
-  protected function validateAndUpcastRequestAttributes(Request $request) {
-    // Load the entity.
-    if (!is_object($entity = $request->attributes->get('entity'))) {
-      $entity_id = $entity;
-      $entity_type = $request->attributes->get('entity_type');
-      if (!$entity_type || !$this->entityManager->getDefinition($entity_type)) {
-        return FALSE;
-      }
-      $entity = $this->entityManager->getStorage($entity_type)->load($entity_id);
-      if (!$entity) {
-        return FALSE;
-      }
-      $request->attributes->set('entity', $entity);
-    }
-
+  protected function validateRequestAttributes(EntityInterface $entity, $field_name, $langcode) {
     // Validate the field name and language.
-    $field_name = $request->attributes->get('field_name');
     if (!$field_name || !$entity->hasField($field_name)) {
       return FALSE;
     }
-    $langcode = $request->attributes->get('langcode');
     if (!$langcode || !$entity->hasTranslation($langcode)) {
       return FALSE;
     }
diff --git a/core/modules/quickedit/tests/src/Access/EditEntityAccessCheckTest.php b/core/modules/quickedit/tests/src/Access/EditEntityAccessCheckTest.php
deleted file mode 100644
index 33d4ed4..0000000
--- a/core/modules/quickedit/tests/src/Access/EditEntityAccessCheckTest.php
+++ /dev/null
@@ -1,142 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\quickedit\Tests\Access\EditEntityAccessCheckTest.
- */
-
-namespace Drupal\quickedit\Tests\Access;
-
-use Symfony\Component\HttpFoundation\Request;
-use Drupal\Core\Access\AccessCheckInterface;
-use Drupal\quickedit\Access\EditEntityAccessCheck;
-use Drupal\Tests\UnitTestCase;
-use Drupal\Core\Entity\EntityInterface;
-
-/**
- * @coversDefaultClass \Drupal\quickedit\Access\EditEntityAccessCheck
- * @group quickedit
- */
-class EditEntityAccessCheckTest extends UnitTestCase {
-
-  /**
-   * The tested access checker.
-   *
-   * @var \Drupal\quickedit\Access\EditEntityAccessCheck
-   */
-  protected $editAccessCheck;
-
-  /**
-   * The mocked entity manager.
-   *
-   * @var \Drupal\Core\Entity\EntityManagerInterface|\PHPUnit_Framework_MockObject_MockObject
-   */
-  protected $entityManager;
-
-  /**
-   * The mocked entity storage.
-   *
-   * @var \Drupal\Core\Entity\EntityStorageInterface|\PHPUnit_Framework_MockObject_MockObject
-   */
-  protected $entityStorage;
-
-  protected function setUp() {
-    $this->entityManager = $this->getMock('Drupal\Core\Entity\EntityManagerInterface');
-
-    $this->entityStorage = $this->getMock('Drupal\Core\Entity\EntityStorageInterface');
-
-    $this->entityManager->expects($this->any())
-      ->method('getStorage')
-      ->will($this->returnValue($this->entityStorage));
-
-    $this->editAccessCheck = new EditEntityAccessCheck($this->entityManager);
-  }
-
-  /**
-   * Provides test data for testAccess().
-   *
-   * @see \Drupal\quickedit\Tests\quickedit\Access\EditEntityAccessCheckTest::testAccess()
-   */
-  public function providerTestAccess() {
-    $editable_entity = $this->getMockBuilder('Drupal\entity_test\Entity\EntityTest')
-      ->disableOriginalConstructor()
-      ->getMock();
-    $editable_entity->expects($this->any())
-      ->method('access')
-      ->will($this->returnValue(TRUE));
-
-    $non_editable_entity = $this->getMockBuilder('Drupal\entity_test\Entity\EntityTest')
-      ->disableOriginalConstructor()
-      ->getMock();
-    $non_editable_entity->expects($this->any())
-      ->method('access')
-      ->will($this->returnValue(FALSE));
-
-    $data = array();
-    $data[] = array($editable_entity, AccessCheckInterface::ALLOW);
-    $data[] = array($non_editable_entity, AccessCheckInterface::DENY);
-
-    return $data;
-  }
-
-  /**
-   * Tests the method for checking access to routes.
-   *
-   * @param \Drupal\Core\Entity\EntityInterface $entity
-   *   A mocked entity.
-   * @param bool|null $expected_result
-   *   The expected result of the access call.
-   *
-   * @dataProvider providerTestAccess
-   */
-  public function testAccess(EntityInterface $entity, $expected_result) {
-    $request = new Request();
-
-    // Prepare the request to be valid.
-    $request->attributes->set('entity', $entity);
-    $request->attributes->set('entity_type', 'test_entity');
-
-    $account = $this->getMock('Drupal\Core\Session\AccountInterface');
-    $access = $this->editAccessCheck->access($request, $account);
-    $this->assertSame($expected_result, $access);
-  }
-
-  /**
-   * Tests the access method with an undefined entity type.
-   */
-  public function testAccessWithUndefinedEntityType() {
-    $request = new Request();
-    $request->attributes->set('entity_type', 'non_valid');
-
-    $this->entityManager->expects($this->once())
-      ->method('getDefinition')
-      ->with('non_valid')
-      ->will($this->returnValue(NULL));
-
-    $account = $this->getMock('Drupal\Core\Session\AccountInterface');
-    $this->assertSame(AccessCheckInterface::KILL, $this->editAccessCheck->access($request, $account));
-  }
-
-  /**
-   * Tests the access method with a non existing entity.
-   */
-  public function testAccessWithNotExistingEntity() {
-    $request = new Request();
-    $request->attributes->set('entity_type', 'entity_test');
-    $request->attributes->set('entity', 1);
-
-    $this->entityManager->expects($this->once())
-      ->method('getDefinition')
-      ->with('entity_test')
-      ->will($this->returnValue(array('id' => 'entity_test')));
-
-    $this->entityStorage->expects($this->once())
-      ->method('load')
-      ->with(1)
-      ->will($this->returnValue(NULL));
-
-    $account = $this->getMock('Drupal\Core\Session\AccountInterface');
-    $this->assertSame(AccessCheckInterface::KILL, $this->editAccessCheck->access($request, $account));
-  }
-
-}
diff --git a/core/modules/quickedit/tests/src/Access/EditEntityFieldAccessCheckTest.php b/core/modules/quickedit/tests/src/Access/EditEntityFieldAccessCheckTest.php
index 5a97dd2..a863046 100644
--- a/core/modules/quickedit/tests/src/Access/EditEntityFieldAccessCheckTest.php
+++ b/core/modules/quickedit/tests/src/Access/EditEntityFieldAccessCheckTest.php
@@ -7,7 +7,6 @@
 
 namespace Drupal\quickedit\Tests\Access;
 
-use Symfony\Component\HttpFoundation\Request;
 use Drupal\Core\Access\AccessCheckInterface;
 use Drupal\quickedit\Access\EditEntityFieldAccessCheck;
 use Drupal\Tests\UnitTestCase;
@@ -29,29 +28,10 @@ class EditEntityFieldAccessCheckTest extends UnitTestCase {
   protected $editAccessCheck;
 
   /**
-   * The mocked entity manager.
-   *
-   * @var \Drupal\Core\Entity\EntityManagerInterface|\PHPUnit_Framework_MockObject_MockObject
+   * {@inheritdoc}
    */
-  protected $entityManager;
-
-  /**
-   * The mocked entity storage.
-   *
-   * @var \Drupal\Core\Entity\EntityStorageInterface|\PHPUnit_Framework_MockObject_MockObject
-   */
-  protected $entityStorage;
-
   protected function setUp() {
-    $this->entityManager = $this->getMock('Drupal\Core\Entity\EntityManagerInterface');
-
-    $this->entityStorage = $this->getMock('Drupal\Core\Entity\EntityStorageInterface');
-
-    $this->entityManager->expects($this->any())
-      ->method('getStorage')
-      ->will($this->returnValue($this->entityStorage));
-
-    $this->editAccessCheck = new EditEntityFieldAccessCheck($this->entityManager);
+    $this->editAccessCheck = new EditEntityFieldAccessCheck();
   }
 
   /**
@@ -105,8 +85,6 @@ public function providerTestAccess() {
    * @dataProvider providerTestAccess
    */
   public function testAccess(EntityInterface $entity, FieldStorageConfigInterface $field_storage = NULL, $expected_result) {
-    $request = new Request();
-
     $field_name = 'valid';
     $entity_with_field = clone $entity;
     $entity_with_field->expects($this->any())
@@ -118,118 +96,42 @@ public function testAccess(EntityInterface $entity, FieldStorageConfigInterface
       ->with(LanguageInterface::LANGCODE_NOT_SPECIFIED)
       ->will($this->returnValue(TRUE));
 
-    // Prepare the request to be valid.
-    $request->attributes->set('entity_type', 'test_entity');
-    $request->attributes->set('entity', $entity_with_field);
-    $request->attributes->set('field_name', $field_name);
-    $request->attributes->set('langcode', LanguageInterface::LANGCODE_NOT_SPECIFIED);
-
     $account = $this->getMock('Drupal\Core\Session\AccountInterface');
-    $access = $this->editAccessCheck->access($request, $field_name, $account);
+    $access = $this->editAccessCheck->access($entity_with_field, $field_name, LanguageInterface::LANGCODE_NOT_SPECIFIED, $account);
     $this->assertSame($expected_result, $access);
   }
 
   /**
-   * Tests the access method with an undefined entity type.
-   */
-  public function testAccessWithUndefinedEntityType() {
-    $request = new Request();
-    $request->attributes->set('entity_type', 'non_valid');
-
-    $this->entityManager->expects($this->once())
-      ->method('getDefinition')
-      ->with('non_valid')
-      ->will($this->returnValue(NULL));
-
-    $account = $this->getMock('Drupal\Core\Session\AccountInterface');
-    $this->assertSame(AccessCheckInterface::KILL, $this->editAccessCheck->access($request, NULL, $account));
-  }
-
-  /**
-   * Tests the access method with a non existing entity.
-   */
-  public function testAccessWithNotExistingEntity() {
-    $request = new Request();
-    $request->attributes->set('entity_type', 'entity_test');
-    $request->attributes->set('entity', 1);
-
-    $this->entityManager->expects($this->once())
-      ->method('getDefinition')
-      ->with('entity_test')
-      ->will($this->returnValue(array('id' => 'entity_test')));
-
-    $this->entityStorage->expects($this->once())
-      ->method('load')
-      ->with(1)
-      ->will($this->returnValue(NULL));
-
-    $account = $this->getMock('Drupal\Core\Session\AccountInterface');
-    $this->assertSame(AccessCheckInterface::KILL, $this->editAccessCheck->access($request, NULL, $account));
-  }
-
-  /**
-   * Tests the access method with a forgotten passed field_name.
-   */
-  public function testAccessWithNotPassedFieldName() {
-    $request = new Request();
-    $request->attributes->set('entity_type', 'entity_test');
-    $request->attributes->set('entity', $this->createMockEntity());
-
-    $account = $this->getMock('Drupal\Core\Session\AccountInterface');
-    $this->assertSame(AccessCheckInterface::KILL, $this->editAccessCheck->access($request, NULL, $account));
-  }
-
-  /**
-   * Tests the access method with a non existing field.
-   */
-  public function testAccessWithNonExistingField() {
-    $request = new Request();
-    $field_name = 'not_valid';
-    $request->attributes->set('entity_type', 'entity_test');
-    $request->attributes->set('entity', $this->createMockEntity());
-    $request->attributes->set('field_name', $field_name);
-
-    $account = $this->getMock('Drupal\Core\Session\AccountInterface');
-    $this->assertSame(AccessCheckInterface::KILL, $this->editAccessCheck->access($request, $field_name, $account));
-  }
-
-  /**
-   * Tests the access method with a forgotten passed language.
+   * Tests checking access to routes that result in AccessCheckInterface::KILL.
+   *
+   * @dataProvider providerTestAccessKill
    */
-  public function testAccessWithNotPassedLanguage() {
-    $request = new Request();
-    $field_name = 'valid';
-    $request->attributes->set('entity_type', 'entity_test');
-    $request->attributes->set('entity', $this->createMockEntity());
-    $request->attributes->set('field_name', $field_name);
-
+  public function testAccessKill($field_name, $langcode) {
     $account = $this->getMock('Drupal\Core\Session\AccountInterface');
-    $this->assertSame(AccessCheckInterface::KILL, $this->editAccessCheck->access($request, $field_name, $account));
+    $entity = $this->createMockEntity();
+    $this->assertSame(AccessCheckInterface::KILL, $this->editAccessCheck->access($entity, $field_name, $langcode, $account));
   }
 
   /**
-   * Tests the access method with an invalid language.
+   * Provides test data for testAccessKill.
    */
-  public function testAccessWithInvalidLanguage() {
-    $entity = $this->createMockEntity();
-    $entity->expects($this->once())
-      ->method('hasTranslation')
-      ->with('xx-lolspeak')
-      ->will($this->returnValue(FALSE));
-
-    $request = new Request();
-    $field_name = 'valid';
-    $request->attributes->set('entity_type', 'entity_test');
-    $request->attributes->set('entity', $entity);
-    $request->attributes->set('field_name', $field_name);
-    $request->attributes->set('langcode', 'xx-lolspeak');
-
-    $account = $this->getMock('Drupal\Core\Session\AccountInterface');
-    $this->assertSame(AccessCheckInterface::KILL, $this->editAccessCheck->access($request, $field_name, $account));
+  public function providerTestAccessKill() {
+    $data = array();
+    // Tests the access method without a field_name.
+    $data[] = array(NULL, LanguageInterface::LANGCODE_NOT_SPECIFIED);
+    // Tests the access method with a non-existent field.
+    $data[] = array('not_valid', LanguageInterface::LANGCODE_NOT_SPECIFIED);
+    // Tests the access method without a langcode.
+    $data[] = array('valid', NULL);
+    // Tests the access method with an invalid langcode.
+    $data[] = array('valid', 'xx-lolspeak');
+    return $data;
   }
 
   /**
    * Returns a mock entity.
+   *
+   * @return \Drupal\Core\Entity\EntityInterface|\PHPUnit_Framework_MockObject_MockObject
    */
   protected function createMockEntity() {
     $entity = $this->getMockBuilder('Drupal\entity_test\Entity\EntityTest')
@@ -237,6 +139,12 @@ protected function createMockEntity() {
       ->getMock();
 
     $entity->expects($this->any())
+      ->method('hasTranslation')
+      ->will($this->returnValueMap(array(
+        array(LanguageInterface::LANGCODE_NOT_SPECIFIED, TRUE),
+        array('xx-lolspeak', FALSE),
+      )));
+    $entity->expects($this->any())
       ->method('hasField')
       ->will($this->returnValueMap(array(
         array('valid', TRUE),
diff --git a/core/tests/Drupal/Tests/Core/ParamConverter/EntityConverterTest.php b/core/tests/Drupal/Tests/Core/ParamConverter/EntityConverterTest.php
new file mode 100644
index 0000000..baeb0c4
--- /dev/null
+++ b/core/tests/Drupal/Tests/Core/ParamConverter/EntityConverterTest.php
@@ -0,0 +1,140 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Tests\Core\ParamConverter\EntityConverterTest.
+ */
+
+namespace Drupal\Tests\Core\ParamConverter;
+
+use Drupal\Core\ParamConverter\EntityConverter;
+use Drupal\Tests\UnitTestCase;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\Routing\Route;
+
+/**
+ * @coversDefaultClass \Drupal\Core\ParamConverter\EntityConverter
+ * @group ParamConverter
+ * @group Entity
+ */
+class EntityConverterTest extends UnitTestCase {
+
+  /**
+   * The mocked entity manager.
+   *
+   * @var \Drupal\Core\Entity\EntityManagerInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $entityManager;
+
+  /**
+   * The tested entity converter.
+   *
+   * @var \Drupal\Core\ParamConverter\EntityConverter
+   */
+  protected $entityConverter;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    $this->entityManager = $this->getMock('Drupal\Core\Entity\EntityManagerInterface');
+
+    $this->entityConverter = new EntityConverter($this->entityManager);
+  }
+
+  /**
+   * Tests the applies() method.
+   *
+   * @dataProvider providerTestApplies
+   *
+   * @covers ::applies
+   */
+  public function testApplies(array $definition, $name, Route $route, $applies) {
+    $this->entityManager->expects($this->any())
+      ->method('hasDefinition')
+      ->willReturnCallback(function($entity_type) {
+        return 'entity_test' == $entity_type;
+      });
+    $this->assertEquals($applies, $this->entityConverter->applies($definition, $name, $route));
+  }
+
+  /**
+   * Provides test data for testApplies()
+   */
+  public function providerTestApplies() {
+    $data = [];
+    $data[] = [['type' => 'entity:foo'], 'foo', new Route('/test/{foo}/bar'), FALSE];
+    $data[] = [['type' => 'entity:entity_test'], 'foo', new Route('/test/{foo}/bar'), TRUE];
+    $data[] = [['type' => 'entity:entity_test'], 'entity_test', new Route('/test/{entity_test}/bar'), TRUE];
+    $data[] = [['type' => 'entity:{entity_test}'], 'entity_test', new Route('/test/{entity_test}/bar'), FALSE];
+    $data[] = [['type' => 'entity:{entity_type}'], 'entity_test', new Route('/test/{entity_type}/{entity_test}/bar'), TRUE];
+    $data[] = [['type' => 'foo'], 'entity_test', new Route('/test/{entity_type}/{entity_test}/bar'), FALSE];
+
+    return $data;
+  }
+
+  /**
+   * Tests the convert() method.
+   *
+   * @dataProvider providerTestConvert
+   *
+   * @covers ::convert
+   */
+  public function testConvert($value, array $definition, array $defaults, $expected_result) {
+    $entity_storage = $this->getMock('Drupal\Core\Entity\EntityStorageInterface');
+    $this->entityManager->expects($this->once())
+      ->method('getStorage')
+      ->with('entity_test')
+      ->willReturn($entity_storage);
+    $entity_storage->expects($this->any())
+      ->method('load')
+      ->willReturnMap([
+        ['valid_id', (object) ['id' => 'valid_id']],
+        ['invalid_id', NULL],
+      ]);
+
+    $this->assertEquals($expected_result, $this->entityConverter->convert($value, $definition, 'foo', $defaults, new Request()));
+  }
+
+  /**
+   * Provides test data for testConvert
+   */
+  public function providerTestConvert() {
+    $data = [];
+    // Existing entity type.
+    $data[] = ['valid_id', ['type' => 'entity:entity_test'], ['foo' => 'valid_id'], (object) ['id' => 'valid_id']];
+    // Invalid ID.
+    $data[] = ['invalid_id', ['type' => 'entity:entity_test'], ['foo' => 'invalid_id'], NULL];
+    // Entity type placeholder.
+    $data[] = ['valid_id', ['type' => 'entity:{entity_type}'], ['foo' => 'valid_id', 'entity_type' => 'entity_test'], (object) ['id' => 'valid_id']];
+
+    return $data;
+  }
+
+  /**
+   * Tests the convert() method with an invalid entity type.
+   *
+   * @expectedException \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException
+   */
+  public function testConvertWithInvalidEntityType() {
+    $this->entityManager->expects($this->once())
+      ->method('getStorage')
+      ->with('invalid_id')
+      ->willThrowException(new \Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException('invalid_id'));
+
+    $this->entityConverter->convert('id', ['type' => 'entity:invalid_id'], 'foo', ['foo' => 'id'], new Request());
+  }
+
+  /**
+   * Tests the convert() method with an invalid dynamic entity type.
+   *
+   * @expectedException \Drupal\Core\ParamConverter\ParamNotConvertedException
+   * @expectedExceptionMessage The "foo" parameter was not converted because the "invalid_id" parameter is missing
+   */
+  public function testConvertWithInvalidDynamicEntityType() {
+    $this->entityConverter->convert('id', ['type' => 'entity:{invalid_id}'], 'foo', ['foo' => 'id'], new Request());
+  }
+
+}
