diff --git a/core/modules/aggregator/src/Tests/Migrate/MigrateAggregatorStubTest.php b/core/modules/aggregator/src/Tests/Migrate/MigrateAggregatorStubTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..d620259a52bdc649e28bc38072851b4c851dfcd2
--- /dev/null
+++ b/core/modules/aggregator/src/Tests/Migrate/MigrateAggregatorStubTest.php
@@ -0,0 +1,63 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\aggregator\Tests\Migrate\MigrateAggregatorStubTest.
+ */
+
+namespace Drupal\aggregator\Tests\Migrate;
+
+use Drupal\migrate\MigrateException;
+use Drupal\migrate_drupal\Tests\MigrateDrupalTestBase;
+use Drupal\migrate_drupal\Tests\StubTestTrait;
+
+/**
+ * Test stub creation for aggregator feeds and items.
+ *
+ * @group aggregator
+ */
+class MigrateAggregatorStubTest extends MigrateDrupalTestBase {
+
+  use StubTestTrait;
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = ['aggregator'];
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+    $this->installEntitySchema('aggregator_feed');
+    $this->installEntitySchema('aggregator_item');
+  }
+
+  /**
+   * Tests creation of aggregator feed stubs.
+   */
+  public function testFeedStub() {
+    $this->performStubTest('aggregator_feed');
+  }
+
+  /**
+   * Tests creation of aggregator feed items.
+   */
+  public function testItemStub() {
+    try {
+      // We expect an exception, because there's no feed to reference.
+      $this->performStubTest('aggregator_item');
+      $this->fail('Expected exception has not been thrown.');
+    }
+    catch (MigrateException $e) {
+      $this->assertIdentical($e->getMessage(),
+        'Stubbing failed, unable to generate value for field fid');
+    }
+
+    // The stub should pass when there's a feed to point to.
+    $this->createStub('aggregator_feed');
+    $this->performStubTest('aggregator_item');
+  }
+
+}
diff --git a/core/modules/block_content/src/Tests/Migrate/MigrateBlockContentStubTest.php b/core/modules/block_content/src/Tests/Migrate/MigrateBlockContentStubTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..1971a9da057411dc9593f99bddb09efcb97d5356
--- /dev/null
+++ b/core/modules/block_content/src/Tests/Migrate/MigrateBlockContentStubTest.php
@@ -0,0 +1,60 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\block_content\Tests\Migrate\MigrateBlockContentStubTest.
+ */
+
+namespace Drupal\block_content\Tests\Migrate;
+
+use Drupal\block_content\Entity\BlockContentType;
+use Drupal\migrate_drupal\Tests\MigrateDrupalTestBase;
+use Drupal\migrate_drupal\Tests\StubTestTrait;
+
+/**
+ * Test stub creation for block_content entities.
+ *
+ * @group block_content
+ */
+class MigrateBlockContentStubTest extends MigrateDrupalTestBase {
+
+  use StubTestTrait;
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = ['block_content'];
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+    $this->installEntitySchema('block_content');
+  }
+
+  /**
+   * Tests creation of block content stubs with no block_content_type available.
+   */
+  public function testStubFailure() {
+    $entity_id = $this->createStub('block_content');
+    $violations = $this->validateStub('block_content', $entity_id);
+    $this->assertIdentical(count($violations), 1);
+    $this->assertEqual($violations[0]->getMessage(), t('The referenced entity (%type: %id) does not exist.', [
+      '%type' => 'block_content_type',
+      '%id' => 'block_content',
+    ]));
+  }
+
+  /**
+   * Tests creation of block content stubs when there is a block_content_type.
+   */
+  public function testStubSuccess() {
+    BlockContentType::create([
+      'id' => 'test_block_content_type',
+      'label' => 'Test block content type',
+    ])->save();
+    $this->performStubTest('block_content');
+  }
+
+}
diff --git a/core/modules/comment/src/Plugin/migrate/destination/EntityComment.php b/core/modules/comment/src/Plugin/migrate/destination/EntityComment.php
index 477cdcd0ca3ed8fb5775e695a2eb7a6953f0f086..7ebbc83388e77c5c2b5a64554d05d2ea1b1e77bf 100644
--- a/core/modules/comment/src/Plugin/migrate/destination/EntityComment.php
+++ b/core/modules/comment/src/Plugin/migrate/destination/EntityComment.php
@@ -10,6 +10,7 @@
 use Drupal\Core\Entity\EntityManagerInterface;
 use Drupal\Core\Entity\EntityStorageInterface;
 use Drupal\Core\Entity\Query\QueryFactory;
+use Drupal\Core\Field\FieldTypePluginManagerInterface;
 use Drupal\Core\State\StateInterface;
 use Drupal\migrate\Entity\MigrationInterface;
 use Drupal\migrate\MigrateException;
@@ -62,13 +63,15 @@ class EntityComment extends EntityContentBase {
    *   The list of bundles this entity type has.
    * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
    *   The entity manager service.
+   * @param \Drupal\Core\Field\FieldTypePluginManagerInterface $field_type_manager
+   *   The field type plugin manager service.
    * @param \Drupal\Core\State\StateInterface $state
    *   The state storage object.
    * @param \Drupal\Core\Entity\Query\QueryFactory $entity_query
    *   The query object that can query the given entity type.
    */
-  public function __construct(array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration, EntityStorageInterface $storage, array $bundles, EntityManagerInterface $entity_manager, StateInterface $state, QueryFactory $entity_query) {
-    parent::__construct($configuration, $plugin_id, $plugin_definition, $migration, $storage, $bundles, $entity_manager);
+  public function __construct(array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration, EntityStorageInterface $storage, array $bundles, EntityManagerInterface $entity_manager, FieldTypePluginManagerInterface $field_type_manager, StateInterface $state, QueryFactory $entity_query) {
+    parent::__construct($configuration, $plugin_id, $plugin_definition, $migration, $storage, $bundles, $entity_manager, $field_type_manager);
     $this->state = $state;
     $this->entityQuery = $entity_query;
   }
@@ -86,6 +89,7 @@ public static function create(ContainerInterface $container, array $configuratio
       $container->get('entity.manager')->getStorage($entity_type),
       array_keys($container->get('entity.manager')->getBundleInfo($entity_type)),
       $container->get('entity.manager'),
+      $container->get('plugin.manager.field.field_type'),
       $container->get('state'),
       $container->get('entity.query')
     );
@@ -110,32 +114,9 @@ public function import(Row $row, array $old_destination_id_values = array()) {
    */
   protected function processStubRow(Row $row) {
     parent::processStubRow($row);
-    $stub_commented_entity_type = $row->getDestinationProperty('entity_type');
-
-    // While parent::getEntity() fills the bundle property for stub entities
-    // if it's still empty, here we must also make sure entity_id/entity_type
-    // are filled (so $comment->getCommentedEntity() always returns a value).
-    if (empty($this->stubCommentedEntityIds[$stub_commented_entity_type])) {
-      // Fill stub entity id. Any id will do, as long as it exists.
-      $entity_type = $this->entityManager->getDefinition($stub_commented_entity_type);
-      $id_key = $entity_type->getKey('id');
-      $result = $this->entityQuery
-        ->get($stub_commented_entity_type)
-        ->range(0, 1)
-        ->execute();
-      if ($result) {
-        $this->stubCommentedEntityIds[$stub_commented_entity_type] = array_pop($result);
-        $row->setSourceProperty($id_key, $this->stubCommentedEntityIds[$stub_commented_entity_type]);
-      }
-      else {
-        throw new MigrateException(t('Could not find parent entity to use for comment %id', ['%id' => implode(':', $row->getSourceIdValues())]), MigrationInterface::MESSAGE_ERROR);
-      }
-    }
-
-    $row->setDestinationProperty('entity_id', $this->stubCommentedEntityIds[$stub_commented_entity_type]);
-    $row->setDestinationProperty('entity_type', $stub_commented_entity_type);
-    $row->setDestinationProperty('created', REQUEST_TIME);
-    $row->setDestinationProperty('changed', REQUEST_TIME);
+    // Neither uid nor name is required in itself, but it is required to set one
+    // of them.
+    $row->setDestinationProperty('name', 'anonymous_stub');
   }
 
 }
diff --git a/core/modules/comment/src/Tests/Migrate/MigrateCommentStubTest.php b/core/modules/comment/src/Tests/Migrate/MigrateCommentStubTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..22818f585a99f88212da8e719d2e6b57414e93a5
--- /dev/null
+++ b/core/modules/comment/src/Tests/Migrate/MigrateCommentStubTest.php
@@ -0,0 +1,78 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\comment\Tests\Migrate\MigrateCommentStubTest.
+ */
+
+namespace Drupal\comment\Tests\Migrate;
+
+use Drupal\comment\Entity\CommentType;
+use Drupal\migrate\MigrateException;
+use Drupal\migrate_drupal\Tests\MigrateDrupalTestBase;
+use Drupal\migrate_drupal\Tests\StubTestTrait;
+use Drupal\node\Entity\NodeType;
+
+/**
+ * Test stub creation for comment entities.
+ *
+ * @group comment
+ */
+class MigrateCommentStubTest extends MigrateDrupalTestBase {
+
+  use StubTestTrait;
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = ['comment', 'node'];
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+    $this->installEntitySchema('comment');
+    $this->installEntitySchema('node');
+    // Make sure uid 0 is created (default uid for comments is 0).
+    $storage = \Drupal::entityManager()->getStorage('user');
+    // Insert a row for the anonymous user.
+    $storage
+      ->create(array(
+        'uid' => 0,
+        'status' => 0,
+        'name' => '',
+      ))
+      ->save();
+    // Need at least one node type and comment type present.
+    NodeType::create([
+      'type' => 'testnodetype',
+      'name' => 'Test node type',
+    ])->save();
+    CommentType::create([
+      'id' => 'testcommenttype',
+      'label' => 'Test comment type',
+      'target_entity_type_id' => 'node',
+    ])->save();
+  }
+
+  /**
+   * Tests creation of comment stubs.
+   */
+  public function testStub() {
+    try {
+      // We expect an exception, because there's no node to reference.
+      $this->performStubTest('comment');
+      $this->fail('Expected exception has not been thrown.');
+    }
+    catch (MigrateException $e) {
+      $this->assertIdentical($e->getMessage(),
+        'Stubbing failed, unable to generate value for field entity_id');
+    }
+
+    // The stub should pass when there's a node to point to.
+    $this->createStub('node');
+    $this->performStubTest('comment');
+  }
+
+}
diff --git a/core/modules/comment/src/Tests/Migrate/d6/MigrateCommentTest.php b/core/modules/comment/src/Tests/Migrate/d6/MigrateCommentTest.php
index 4545e4626886dff4d7fe62e2d25a0cac850c3191..7219d9548890fcdc7261edcbfcca51c170e3dc03 100644
--- a/core/modules/comment/src/Tests/Migrate/d6/MigrateCommentTest.php
+++ b/core/modules/comment/src/Tests/Migrate/d6/MigrateCommentTest.php
@@ -22,13 +22,7 @@ class MigrateCommentTest extends MigrateDrupal6TestBase {
   /**
    * {@inheritdoc}
    */
-  public static $modules = [
-    'comment',
-    // Directly testing that a stub comment's entity_id is populated upon
-    // importing is not straightforward, but RDF module serves as an implicit
-    // test - its hook_comment_storage_load() references a stubbed comment.
-    'rdf',
-  ];
+  public static $modules = ['comment'];
 
   /**
    * {@inheritdoc}
diff --git a/core/modules/file/src/Plugin/migrate/destination/EntityFile.php b/core/modules/file/src/Plugin/migrate/destination/EntityFile.php
index c5e898948bca0a0b208d5f5d7b7175b5c8399bca..576b38b2643e75c19a430278bb7b09e1fda512d4 100644
--- a/core/modules/file/src/Plugin/migrate/destination/EntityFile.php
+++ b/core/modules/file/src/Plugin/migrate/destination/EntityFile.php
@@ -9,6 +9,8 @@
 
 use Drupal\Core\Entity\EntityManagerInterface;
 use Drupal\Core\Entity\EntityStorageInterface;
+use Drupal\Core\Field\FieldTypePluginManagerInterface;
+use Drupal\Core\Field\Plugin\Field\FieldType\UriItem;
 use Drupal\Core\File\FileSystemInterface;
 use Drupal\Core\StreamWrapper\LocalStream;
 use Drupal\Core\StreamWrapper\StreamWrapperManagerInterface;
@@ -41,7 +43,7 @@ class EntityFile extends EntityContentBase {
   /**
    * {@inheritdoc}
    */
-  public function __construct(array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration, EntityStorageInterface $storage, array $bundles, EntityManagerInterface $entity_manager, StreamWrapperManagerInterface $stream_wrappers, FileSystemInterface $file_system) {
+  public function __construct(array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration, EntityStorageInterface $storage, array $bundles, EntityManagerInterface $entity_manager, FieldTypePluginManagerInterface $field_type_manager, StreamWrapperManagerInterface $stream_wrappers, FileSystemInterface $file_system) {
     $configuration += array(
       'source_base_path' => '',
       'source_path_property' => 'filepath',
@@ -49,7 +51,7 @@ public function __construct(array $configuration, $plugin_id, $plugin_definition
       'move' => FALSE,
       'urlencode' => FALSE,
     );
-    parent::__construct($configuration, $plugin_id, $plugin_definition, $migration, $storage, $bundles, $entity_manager);
+    parent::__construct($configuration, $plugin_id, $plugin_definition, $migration, $storage, $bundles, $entity_manager, $field_type_manager);
 
     $this->streamWrapperManager = $stream_wrappers;
     $this->fileSystem = $file_system;
@@ -68,6 +70,7 @@ public static function create(ContainerInterface $container, array $configuratio
       $container->get('entity.manager')->getStorage($entity_type),
       array_keys($container->get('entity.manager')->getBundleInfo($entity_type)),
       $container->get('entity.manager'),
+      $container->get('plugin.manager.field.field_type'),
       $container->get('stream_wrapper_manager'),
       $container->get('file_system')
     );
@@ -77,6 +80,12 @@ public static function create(ContainerInterface $container, array $configuratio
    * {@inheritdoc}
    */
   protected function getEntity(Row $row, array $old_destination_id_values) {
+    // For stub rows, there is no real file to deal with, let the stubbing
+    // process take its default path.
+    if ($row->isStub()) {
+      return parent::getEntity($row, $old_destination_id_values);
+    }
+
     $destination = $row->getDestinationProperty($this->configuration['destination_path_property']);
     $entity = $this->storage->loadByProperties(['uri' => $destination]);
     if ($entity) {
@@ -91,6 +100,12 @@ protected function getEntity(Row $row, array $old_destination_id_values) {
    * {@inheritdoc}
    */
   public function import(Row $row, array $old_destination_id_values = array()) {
+    // For stub rows, there is no real file to deal with, let the stubbing
+    // process create the stub entity.
+    if ($row->isStub()) {
+      return parent::import($row, $old_destination_id_values);
+    }
+
     $file = $row->getSourceProperty($this->configuration['source_path_property']);
     $destination = $row->getDestinationProperty($this->configuration['destination_path_property']);
     $source = $this->configuration['source_base_path'] . $file;
@@ -256,4 +271,29 @@ protected function urlencode($filename) {
     return $filename;
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  protected function processStubRow(Row $row) {
+    // We stub the uri value ourselves so we can create a real stub file for it.
+    if (!$row->getDestinationProperty('uri')) {
+      $field_definitions = $this->entityManager
+        ->getFieldDefinitions($this->storage->getEntityTypeId(),
+          $this->getKey('bundle'));
+      $value = UriItem::generateSampleValue($field_definitions['uri']);
+      if (empty($value)) {
+        throw new MigrateException('Stubbing failed, unable to generate value for field uri');
+      }
+      // generateSampleValue() wraps the value in an array.
+      $value = reset($value);
+      // Make it into a proper public file uri, stripping off the existing
+      // scheme if present.
+      $value = 'public://' . preg_replace('|^[a-z]+://|i', '', $value);
+      // Create a real file, so File::preSave() can do filesize() on it.
+      touch($value);
+      $row->setDestinationProperty('uri', $value);
+    }
+    parent::processStubRow($row);
+  }
+
 }
diff --git a/core/modules/file/src/Plugin/migrate/process/d6/FileUri.php b/core/modules/file/src/Plugin/migrate/process/d6/FileUri.php
index b464ee00425316abb00ea087fdf33ec0b7ad45d0..912371c25edd0a43333d5c3a01f2401ff866e338 100644
--- a/core/modules/file/src/Plugin/migrate/process/d6/FileUri.php
+++ b/core/modules/file/src/Plugin/migrate/process/d6/FileUri.php
@@ -24,6 +24,11 @@ class FileUri extends ProcessPluginBase {
    * {@inheritdoc}
    */
   public function transform($value, MigrateExecutableInterface $migrate_executable, Row $row, $destination_property) {
+    // If we're stubbing a file entity, return a uri of NULL so it will get
+    // stubbed by the general process.
+    if ($row->isStub()) {
+      return NULL;
+    }
     list($filepath, $file_directory_path, $temp_directory_path, $is_public) = $value;
 
     // Specific handling using $temp_directory_path for temporary files.
diff --git a/core/modules/file/src/Tests/Migrate/MigrateFileStubTest.php b/core/modules/file/src/Tests/Migrate/MigrateFileStubTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..ae7446c877a22aa5c53b5d0d519ebbe422f03b01
--- /dev/null
+++ b/core/modules/file/src/Tests/Migrate/MigrateFileStubTest.php
@@ -0,0 +1,42 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\file\Tests\Migrate\MigrateFileStubTest.
+ */
+
+namespace Drupal\file\Tests\Migrate;
+
+use Drupal\migrate_drupal\Tests\MigrateDrupalTestBase;
+use Drupal\migrate_drupal\Tests\StubTestTrait;
+
+/**
+ * Test stub creation for file entities.
+ *
+ * @group file
+ */
+class MigrateFileStubTest extends MigrateDrupalTestBase {
+
+  use StubTestTrait;
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = ['file'];
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+    $this->installEntitySchema('file');
+  }
+
+  /**
+   * Tests creation of file stubs.
+   */
+  public function testStub() {
+    $this->performStubTest('file');
+  }
+
+}
diff --git a/core/modules/menu_link_content/src/Tests/Migrate/MigrateMenuLinkContentStubTest.php b/core/modules/menu_link_content/src/Tests/Migrate/MigrateMenuLinkContentStubTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..47e94d13409ad58141bb439d5827d82297c3162c
--- /dev/null
+++ b/core/modules/menu_link_content/src/Tests/Migrate/MigrateMenuLinkContentStubTest.php
@@ -0,0 +1,42 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\menu_link_content\Tests\Migrate\MigrateMenuLinkContentStubTest.
+ */
+
+namespace Drupal\menu_link_content\Tests\Migrate;
+
+use Drupal\migrate_drupal\Tests\MigrateDrupalTestBase;
+use Drupal\migrate_drupal\Tests\StubTestTrait;
+
+/**
+ * Test stub creation for menu link content entities.
+ *
+ * @group menu_link_content
+ */
+class MigrateMenuLinkContentStubTest extends MigrateDrupalTestBase {
+
+  use StubTestTrait;
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = ['menu_link_content', 'link'];
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+    $this->installEntitySchema('menu_link_content');
+  }
+
+  /**
+   * Tests creation of menu link content stubs.
+   */
+  public function testStub() {
+    $this->performStubTest('menu_link_content');
+  }
+
+}
diff --git a/core/modules/migrate/src/MigrateExecutable.php b/core/modules/migrate/src/MigrateExecutable.php
index 2749313d32542f3b56602eee96a75efbd02f4d1f..a3706239e757e80fccd4a3fe2681f8b34b54b59c 100644
--- a/core/modules/migrate/src/MigrateExecutable.php
+++ b/core/modules/migrate/src/MigrateExecutable.php
@@ -401,8 +401,8 @@ public function processRow(Row $row, array $process = NULL, $value = NULL) {
           $multiple = $multiple || $plugin->multiple();
         }
       }
-      // No plugins means do not set.
-      if ($plugins) {
+      // No plugins or no value means do not set.
+      if ($plugins && !is_null($value)) {
         $row->setDestinationProperty($destination, $value);
       }
       // Reset the value.
diff --git a/core/modules/migrate/src/Plugin/migrate/destination/Entity.php b/core/modules/migrate/src/Plugin/migrate/destination/Entity.php
index 64123d6d448b9be6b14003a093647dd665cec67c..f7a6a1e93ff3e776e5ab56ed9afb4a4f5b38fa2f 100644
--- a/core/modules/migrate/src/Plugin/migrate/destination/Entity.php
+++ b/core/modules/migrate/src/Plugin/migrate/destination/Entity.php
@@ -136,19 +136,6 @@ protected function getEntityId(Row $row) {
     return $row->getDestinationProperty($this->getKey('id'));
   }
 
-  /**
-   * Process the stub values.
-   *
-   * @param \Drupal\migrate\Row $row
-   *   The row of data.
-   */
-  protected function processStubRow(Row $row) {
-    $bundle_key = $this->getKey('bundle');
-    if ($bundle_key && empty($row->getDestinationProperty($bundle_key))) {
-      $row->setDestinationProperty($bundle_key, reset($this->bundles));
-    }
-  }
-
   /**
    * Returns a specific entity key.
    *
diff --git a/core/modules/migrate/src/Plugin/migrate/destination/EntityContentBase.php b/core/modules/migrate/src/Plugin/migrate/destination/EntityContentBase.php
index ae3030a11b2cf616dfcb1649be96d8ff579c0bca..4dfd6c6e0ab37a9929698879ff6a50877d21b0db 100644
--- a/core/modules/migrate/src/Plugin/migrate/destination/EntityContentBase.php
+++ b/core/modules/migrate/src/Plugin/migrate/destination/EntityContentBase.php
@@ -7,12 +7,17 @@
 
 namespace Drupal\migrate\Plugin\migrate\destination;
 
+use Drupal\Component\Utility\Random;
+use Drupal\Component\Utility\Unicode;
 use Drupal\Core\Entity\ContentEntityInterface;
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Entity\EntityManagerInterface;
 use Drupal\Core\Entity\EntityStorageInterface;
+use Drupal\Core\Field\FieldTypePluginManagerInterface;
 use Drupal\Core\TypedData\TypedDataInterface;
+use Drupal\link\LinkItemInterface;
 use Drupal\migrate\Entity\MigrationInterface;
+use Drupal\migrate\MigrateException;
 use Drupal\migrate\Plugin\MigrateIdMapInterface;
 use Drupal\migrate\Row;
 use Symfony\Component\DependencyInjection\ContainerInterface;
@@ -29,6 +34,13 @@ class EntityContentBase extends Entity {
    */
   protected $entityManager;
 
+  /**
+   * Field type plugin manager.
+   *
+   * @var \Drupal\Core\Field\FieldTypePluginManagerInterface
+   */
+  protected $fieldTypeManager;
+
   /**
    * Constructs a content entity.
    *
@@ -46,10 +58,13 @@ class EntityContentBase extends Entity {
    *   The list of bundles this entity type has.
    * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
    *   The entity manager service.
+   * @param \Drupal\Core\Field\FieldTypePluginManagerInterface $field_type_manager
+   *   The field type plugin manager service.
    */
-  public function __construct(array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration, EntityStorageInterface $storage, array $bundles, EntityManagerInterface $entity_manager) {
+  public function __construct(array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration, EntityStorageInterface $storage, array $bundles, EntityManagerInterface $entity_manager, FieldTypePluginManagerInterface $field_type_manager) {
     parent::__construct($configuration, $plugin_id, $plugin_definition, $migration, $storage, $bundles);
     $this->entityManager = $entity_manager;
+    $this->fieldTypeManager = $field_type_manager;
   }
 
   /**
@@ -64,7 +79,8 @@ public static function create(ContainerInterface $container, array $configuratio
       $migration,
       $container->get('entity.manager')->getStorage($entity_type),
       array_keys($container->get('entity.manager')->getBundleInfo($entity_type)),
-      $container->get('entity.manager')
+      $container->get('entity.manager'),
+      $container->get('plugin.manager.field.field_type')
     );
   }
 
@@ -132,4 +148,44 @@ protected function updateEntity(EntityInterface $entity, Row $row) {
     $this->setRollbackAction($row->getIdMap());
   }
 
+  /**
+   * Do as much population of the stub row as we can.
+   *
+   * @param \Drupal\migrate\Row $row
+   *   The row of data.
+   */
+  protected function processStubRow(Row $row) {
+    $bundle_key = $this->getKey('bundle');
+    if ($bundle_key && empty($row->getDestinationProperty($bundle_key))) {
+      $row->setDestinationProperty($bundle_key, reset($this->bundles));
+    }
+
+    // Populate any required fields not already populated.
+    $fields = $this->entityManager
+      ->getFieldDefinitions($this->storage->getEntityTypeId(), $bundle_key);
+    foreach ($fields as $field_name => $field_definition) {
+      if ($field_definition->isRequired() && is_null($row->getDestinationProperty($field_name))) {
+        // Use the configured default value for this specific field, if any.
+        if ($default_value = $field_definition->getDefaultValueLiteral()) {
+          $values[] = $default_value;
+        }
+        else {
+          // Otherwise, ask the field type to generate a sample value.
+          $field_type = $field_definition->getType();
+          /** @var \Drupal\Core\Field\FieldItemInterface $field_type_class */
+          $field_type_class = $this->fieldTypeManager
+            ->getPluginClass($field_definition->getType());
+          $values = $field_type_class::generateSampleValue($field_definition);
+          if (is_null($values)) {
+            // Handle failure to generate a sample value.
+            throw new MigrateException('Stubbing failed, unable to generate value for field ' . $field_name);
+            break;
+          }
+        }
+
+        $row->setDestinationProperty($field_name, $values);
+      }
+    }
+  }
+
 }
diff --git a/core/modules/migrate/src/Row.php b/core/modules/migrate/src/Row.php
index a509da479317498f50b4544f1742cbddcf42eeed..4e788afecccfbdf1f9ffb2073426f2e1d7d0a04d 100644
--- a/core/modules/migrate/src/Row.php
+++ b/core/modules/migrate/src/Row.php
@@ -73,7 +73,7 @@ class Row {
    *
    * @see getRawDestination()
    */
-  protected $rawDestination;
+  protected $rawDestination = [];
 
   /**
    * TRUE when this row is a stub.
diff --git a/core/modules/migrate/tests/src/Unit/destination/EntityRevisionTest.php b/core/modules/migrate/tests/src/Unit/destination/EntityRevisionTest.php
index d5f71a072c376cadb4f1c95b20f4e5789dee70df..2db5d9e8e3aa06b1c07238fdf8e13529cb584f34 100644
--- a/core/modules/migrate/tests/src/Unit/destination/EntityRevisionTest.php
+++ b/core/modules/migrate/tests/src/Unit/destination/EntityRevisionTest.php
@@ -36,6 +36,11 @@ class EntityRevisionTest extends UnitTestCase {
    */
   protected $entityManager;
 
+  /**
+   * @var \Drupal\Core\Field\FieldTypePluginManagerInterface
+   */
+  protected $fieldTypeManager;
+
   public function setUp() {
     parent::setUp();
 
@@ -43,6 +48,7 @@ public function setUp() {
     $this->migration = $this->prophesize('\Drupal\migrate\Entity\MigrationInterface');
     $this->storage = $this->prophesize('\Drupal\Core\Entity\EntityStorageInterface');
     $this->entityManager = $this->prophesize('\Drupal\Core\Entity\EntityManagerInterface');
+    $this->fieldTypeManager = $this->prophesize('\Drupal\Core\Field\FieldTypePluginManagerInterface');
   }
 
   /**
@@ -183,7 +189,9 @@ protected function getEntityRevisionDestination(array $configuration = [], $plug
       $this->migration->reveal(),
       $this->storage->reveal(),
       [],
-      $this->entityManager->reveal());
+      $this->entityManager->reveal(),
+      $this->fieldTypeManager->reveal()
+    );
   }
 
 }
diff --git a/core/modules/migrate_drupal/src/Tests/StubTestTrait.php b/core/modules/migrate_drupal/src/Tests/StubTestTrait.php
new file mode 100644
index 0000000000000000000000000000000000000000..1e028bfff542251800d94ef12aa7986b466e6f38
--- /dev/null
+++ b/core/modules/migrate_drupal/src/Tests/StubTestTrait.php
@@ -0,0 +1,80 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\migrate_drupal\Tests\StubTestTrait.
+ */
+
+namespace Drupal\migrate_drupal\Tests;
+use Drupal\migrate\Entity\Migration;
+use Drupal\migrate\Row;
+
+/**
+ * Provides common functionality for testing stubbing.
+ */
+trait StubTestTrait {
+
+  /**
+   * Test that creating a stub of the given entity type results in a valid
+   * entity.
+   *
+   * @param string $entity_type_id
+   *   The entity type we are stubbing.
+   */
+  protected function performStubTest($entity_type_id) {
+    $entity_id = $this->createStub($entity_type_id);
+    $this->assertTrue($entity_id, 'Stub successfully created');
+    if ($entity_id) {
+      $violations = $this->validateStub($entity_type_id, $entity_id);
+      if (!$this->assertIdentical(count($violations), 0, 'Stub is a valid entity')) {
+        foreach ($violations as $violation) {
+          $this->fail((string) $violation->getMessage());
+        }
+      }
+    }
+  }
+
+  /**
+   * Create a stub of the given entity type.
+   *
+   * @param string $entity_type_id
+   *   The entity type we are stubbing.
+   *
+   * @return int
+   *   ID of the created entity.
+   */
+  protected function createStub($entity_type_id) {
+    // Create a dummy migration to pass to the destination plugin.
+    $config = [
+      'id' => 'dummy',
+      'migration_tags' => ['Stub test'],
+      'source' => ['plugin' => 'empty'],
+      'process' => [],
+      'destination' => ['plugin' => 'entity:' . $entity_type_id],
+    ];
+    $migration = Migration::create($config);
+    $destination_plugin = $migration->getDestinationPlugin(TRUE);
+    $stub_row = new Row([], [], TRUE);
+    $destination_ids = $destination_plugin->import($stub_row);
+    return reset($destination_ids);
+  }
+
+  /**
+   * Perform validation on a stub entity.
+   *
+   * @param string $entity_type_id
+   *   The entity type we are stubbing.
+   * @param string $entity_id
+   *   ID of the stubbed entity to validate.
+   *
+   * @return \Drupal\Core\Entity\EntityConstraintViolationListInterface
+   *   List of constraint violations identified.
+   */
+  protected function validateStub($entity_type_id, $entity_id) {
+    $controller = \Drupal::entityManager()->getStorage($entity_type_id);
+    /** @var \Drupal\Core\Entity\ContentEntityInterface $stub_entity */
+    $stub_entity = $controller->load($entity_id);
+    return $stub_entity->validate();
+  }
+
+}
diff --git a/core/modules/node/src/Tests/Migrate/MigrateNodeStubTest.php b/core/modules/node/src/Tests/Migrate/MigrateNodeStubTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..22d140bf3e568ce064147af5abd5552e008819b6
--- /dev/null
+++ b/core/modules/node/src/Tests/Migrate/MigrateNodeStubTest.php
@@ -0,0 +1,48 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\node\Tests\Migrate\MigrateNodeStubTest.
+ */
+
+namespace Drupal\node\Tests\Migrate;
+
+use Drupal\migrate_drupal\Tests\MigrateDrupalTestBase;
+use Drupal\migrate_drupal\Tests\StubTestTrait;
+use Drupal\node\Entity\NodeType;
+
+/**
+ * Test stub creation for nodes.
+ *
+ * @group node
+ */
+class MigrateNodeStubTest extends MigrateDrupalTestBase {
+
+  use StubTestTrait;
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = ['node'];
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+    $this->installEntitySchema('node');
+    // Need at least one node type present.
+    NodeType::create([
+      'type' => 'testnodetype',
+      'name' => 'Test node type',
+    ])->save();
+  }
+
+  /**
+   * Tests creation of node stubs.
+   */
+  public function testStub() {
+    $this->performStubTest('node');
+  }
+
+}
diff --git a/core/modules/shortcut/src/Tests/Migrate/MigrateShortcutStubTest.php b/core/modules/shortcut/src/Tests/Migrate/MigrateShortcutStubTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..ba9a0002e56f01bd63e7d0bfa4686388741525d5
--- /dev/null
+++ b/core/modules/shortcut/src/Tests/Migrate/MigrateShortcutStubTest.php
@@ -0,0 +1,44 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\shortcut\Tests\Migrate\MigrateShortcutStubTest.
+ */
+
+namespace Drupal\shortcut\Tests\Migrate;
+
+use Drupal\migrate_drupal\Tests\MigrateDrupalTestBase;
+use Drupal\migrate_drupal\Tests\StubTestTrait;
+
+/**
+ * Test stub creation for shortcut entities.
+ *
+ * @group shortcut
+ */
+class MigrateShortcutStubTest extends MigrateDrupalTestBase {
+
+  use StubTestTrait;
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = ['shortcut', 'link'];
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+    $this->installEntitySchema('shortcut');
+    // Make sure the 'default' shortcut_set is installed.
+    $this->installConfig(['shortcut']);
+  }
+
+  /**
+   * Tests creation of shortcut stubs.
+   */
+  public function testStub() {
+    $this->performStubTest('shortcut');
+  }
+
+}
diff --git a/core/modules/taxonomy/src/Plugin/migrate/destination/EntityTaxonomyTerm.php b/core/modules/taxonomy/src/Plugin/migrate/destination/EntityTaxonomyTerm.php
deleted file mode 100644
index 0a28f5ba7118d7e7c6c6b5846d8b291d48650295..0000000000000000000000000000000000000000
--- a/core/modules/taxonomy/src/Plugin/migrate/destination/EntityTaxonomyTerm.php
+++ /dev/null
@@ -1,30 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\taxonomy\Plugin\migrate\destination\EntityTaxonomyTerm.
- */
-
-namespace Drupal\taxonomy\Plugin\migrate\destination;
-
-use Drupal\migrate\Row;
-use Drupal\migrate\Plugin\migrate\destination\EntityContentBase;
-
-/**
- * @MigrateDestination(
- *   id = "entity:taxonomy_term"
- * )
- */
-class EntityTaxonomyTerm extends EntityContentBase {
-
-  /**
-   * {@inheritdoc}
-   */
-  protected function getEntity(Row $row, array $old_destination_id_values) {
-    if ($row->isStub()) {
-      $row->setDestinationProperty('name', $this->t('Stub name for source tid:') . $row->getSourceProperty('tid'));
-    }
-    return parent::getEntity($row, $old_destination_id_values);
-  }
-
-}
diff --git a/core/modules/taxonomy/src/Tests/Migrate/MigrateTaxonomyTermStubTest.php b/core/modules/taxonomy/src/Tests/Migrate/MigrateTaxonomyTermStubTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..a7cd9ca7fcb8e07ac03cc678028427418cdb4159
--- /dev/null
+++ b/core/modules/taxonomy/src/Tests/Migrate/MigrateTaxonomyTermStubTest.php
@@ -0,0 +1,118 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\taxonomy\Tests\Migrate\MigrateTaxonomyTermStubTest.
+ */
+
+namespace Drupal\taxonomy\Tests\Migrate;
+
+use Drupal\migrate\Entity\Migration;
+use Drupal\migrate\MigrateExecutable;
+use Drupal\migrate_drupal\Tests\MigrateDrupalTestBase;
+use Drupal\migrate_drupal\Tests\StubTestTrait;
+use Drupal\taxonomy\Entity\Term;
+use Drupal\taxonomy\Entity\Vocabulary;
+
+/**
+ * Test stub creation for taxonomy terms.
+ *
+ * @group taxonomy
+ */
+class MigrateTaxonomyTermStubTest extends MigrateDrupalTestBase {
+
+  use StubTestTrait;
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = ['taxonomy', 'text'];
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+    $this->installEntitySchema('taxonomy_term');
+  }
+
+  /**
+   * Tests creation of taxonomy term stubs.
+   */
+  public function testStub() {
+    Vocabulary::create([
+      'vid' => 'test_vocabulary',
+      'name' => 'Test vocabulary',
+    ])->save();
+    $this->performStubTest('taxonomy_term');
+  }
+
+  /**
+   * Tests creation of stubs when weight is mapped.
+   */
+  public function testStubWithWeightMapping() {
+    // Create a vocabulary via migration for the terms to reference.
+    $vocabulary_data_rows = [
+      ['id' => '1', 'name' => 'tags'],
+    ];
+    $ids = ['id' => ['type' => 'integer']];
+    $config = [
+      'id' => 'vocabularies',
+      'migration_tags' => ['Stub test'],
+      'source' => [
+        'plugin' => 'embedded_data',
+        'data_rows' => $vocabulary_data_rows,
+        'ids' => $ids,
+      ],
+      'process' => [
+        'vid' => 'id',
+        'name' => 'name',
+      ],
+      'destination' => ['plugin' => 'entity:taxonomy_vocabulary'],
+    ];
+    $vocabulary_migration = Migration::create($config);
+    $vocabulary_executable = new MigrateExecutable($vocabulary_migration, $this);
+    $vocabulary_executable->import();
+
+    // We have a term referencing an unmigrated parent, forcing a stub to be
+    // created.
+    $term_data_rows = [
+      ['id' => '1', 'vocab' => '1', 'name' => 'music', 'parent' => '2'],
+    ];
+    $ids = ['id' => ['type' => 'integer']];
+    $config = [
+      'id' => 'terms',
+      'migration_tags' => ['Import and rollback test'],
+      'source' => [
+        'plugin' => 'embedded_data',
+        'data_rows' => $term_data_rows,
+        'ids' => $ids,
+      ],
+      'process' => [
+        'tid' => 'id',
+        'vid' => 'vocab',
+        'name' => 'name',
+        'weight' => 'weight',
+        'parent' => [
+          'plugin' => 'migration',
+          'migration' => 'terms',
+          'source' => 'parent',
+        ],
+      ],
+      'destination' => ['plugin' => 'entity:taxonomy_term'],
+      'migration_dependencies' => ['required' => ['vocabularies']],
+    ];
+
+    $term_migration = Migration::create($config);
+    $term_migration->save();
+    $term_executable = new MigrateExecutable($term_migration, $this);
+    $term_executable->import();
+    // Load the referenced term, which should exist as a stub.
+    /** @var \Drupal\Core\Entity\ContentEntityBase $stub_entity */
+    $stub_entity = Term::load(2);
+    $this->assertTrue($stub_entity, 'Stub successfully created');
+    if ($stub_entity) {
+      $this->assertIdentical(count($stub_entity->validate()), 0, 'Stub is a valid entity');
+    }
+  }
+}
diff --git a/core/modules/user/src/Plugin/migrate/destination/EntityUser.php b/core/modules/user/src/Plugin/migrate/destination/EntityUser.php
index 785a3b53044ac2824cae72d4d865532387bcb7be..beda7c806f08e66150a37cb3c017bb9e1bc49524 100644
--- a/core/modules/user/src/Plugin/migrate/destination/EntityUser.php
+++ b/core/modules/user/src/Plugin/migrate/destination/EntityUser.php
@@ -7,8 +7,11 @@
 
 namespace Drupal\user\Plugin\migrate\destination;
 
+use Drupal\Component\Utility\Unicode;
 use Drupal\Core\Entity\EntityManagerInterface;
 use Drupal\Core\Entity\EntityStorageInterface;
+use Drupal\Core\Field\FieldTypePluginManagerInterface;
+use Drupal\Core\Field\Plugin\Field\FieldType\EmailItem;
 use Drupal\Core\Password\PasswordInterface;
 use Drupal\migrate\Entity\MigrationInterface;
 use Drupal\migrate\MigrateException;
@@ -50,11 +53,13 @@ class EntityUser extends EntityContentBase {
    *   The migrate plugin manager.
    * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
    *   The entity manager service.
+   * @param \Drupal\Core\Field\FieldTypePluginManagerInterface $field_type_manager
+   *   The field type plugin manager service.
    * @param \Drupal\Core\Password\PasswordInterface $password
    *   The password service.
    */
-  public function __construct(array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration, EntityStorageInterface $storage, array $bundles, EntityManagerInterface $entity_manager, PasswordInterface $password) {
-    parent::__construct($configuration, $plugin_id, $plugin_definition, $migration, $storage, $bundles, $entity_manager);
+  public function __construct(array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration, EntityStorageInterface $storage, array $bundles, EntityManagerInterface $entity_manager, FieldTypePluginManagerInterface $field_type_manager, PasswordInterface $password) {
+    parent::__construct($configuration, $plugin_id, $plugin_definition, $migration, $storage, $bundles, $entity_manager, $field_type_manager);
     if (isset($configuration['md5_passwords'])) {
       $this->password = $password;
     }
@@ -73,6 +78,7 @@ public static function create(ContainerInterface $container, array $configuratio
       $container->get('entity.manager')->getStorage($entity_type),
       array_keys($container->get('entity.manager')->getBundleInfo($entity_type)),
       $container->get('entity.manager'),
+      $container->get('plugin.manager.field.field_type'),
       $container->get('password')
     );
   }
@@ -102,4 +108,28 @@ public function import(Row $row, array $old_destination_id_values = array()) {
     return $ids;
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  protected function processStubRow(Row $row) {
+    parent::processStubRow($row);
+    // Email address is not defined as required in the base field definition but
+    // is effectively required by the UserMailRequired constraint. This means
+    // that Entity::processStubRow() did not populate it - we do it here.
+    $field_definitions = $this->entityManager
+      ->getFieldDefinitions($this->storage->getEntityTypeId(),
+        $this->getKey('bundle'));
+    $mail = EmailItem::generateSampleValue($field_definitions['mail']);
+    $row->setDestinationProperty('mail', reset($mail));
+
+    // @todo Work-around for https://www.drupal.org/node/2602066.
+    $name = $row->getDestinationProperty('name');
+    if (is_array($name)) {
+      $name = reset($name);
+    }
+    if (Unicode::strlen($name) > USERNAME_MAX_LENGTH) {
+      $row->setDestinationProperty('name', Unicode::substr($name, 0, USERNAME_MAX_LENGTH));
+    }
+  }
+
 }
diff --git a/core/modules/user/src/Tests/Migrate/MigrateUserStubTest.php b/core/modules/user/src/Tests/Migrate/MigrateUserStubTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..0a65ddf2d2b1a2265dce991642f00a9a362e3e0a
--- /dev/null
+++ b/core/modules/user/src/Tests/Migrate/MigrateUserStubTest.php
@@ -0,0 +1,43 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\user\Tests\Migrate\MigrateUserStubTest.
+ */
+
+namespace Drupal\user\Tests\Migrate;
+
+use Drupal\migrate_drupal\Tests\MigrateDrupalTestBase;
+use Drupal\migrate_drupal\Tests\StubTestTrait;
+
+/**
+ * Test stub creation for user entities.
+ *
+ * @group user
+ */
+class MigrateUserStubTest extends MigrateDrupalTestBase {
+
+  use StubTestTrait;
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = ['user'];
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+    $this->installEntitySchema('user');
+    $this->installSchema('system', ['sequences']);
+  }
+
+  /**
+   * Tests creation of user stubs.
+   */
+  public function testStub() {
+    $this->performStubTest('user');
+  }
+
+}