diff --git a/core/modules/migrate_drupal/src/Plugin/migrate/source/d8/ContentEntity.php b/core/modules/migrate_drupal/src/Plugin/migrate/source/d8/ContentEntity.php
new file mode 100644
index 0000000..ec7d95b
--- /dev/null
+++ b/core/modules/migrate_drupal/src/Plugin/migrate/source/d8/ContentEntity.php
@@ -0,0 +1,309 @@
+<?php
+
+namespace Drupal\migrate_drupal\Plugin\migrate\source\d8;
+
+use Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException;
+use Drupal\Core\Entity\EntityFieldManagerInterface;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\Core\State\StateInterface;
+use Drupal\migrate\MigrateException;
+use Drupal\migrate\Plugin\migrate\source\SqlBase;
+use Drupal\migrate\Plugin\MigrationInterface;
+use Drupal\migrate\Row;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Drupal contenty entity source from database.
+ *
+ * @MigrateSource(
+ *   id = "d8_content_entity",
+ *   source_provider = "migrate_drupal"
+ * )
+ */
+class ContentEntity extends SqlBase {
+
+  /**
+   * The entity type manager.
+   *
+   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
+   */
+  protected $entityTypeManager;
+
+  /**
+   * The entity field manager.
+   *
+   * @var \Drupal\Core\Entity\EntityFieldManagerInterface
+   */
+  protected $entityFieldManager;
+
+  /**
+   * Static cache for bundle fields.
+   *
+   * @var array
+   */
+  protected $bundleFields = [];
+
+  /**
+   * {@inheritdoc}
+   */
+  public function __construct(array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration, StateInterface $state, EntityTypeManagerInterface $entity_type_manager, EntityFieldManagerInterface $entity_field_manager) {
+    if (empty($configuration['entity_type'])) {
+      throw new InvalidPluginDefinitionException($plugin_id, 'Missing required entity_type definition');
+    }
+    $this->entityTypeManager = $entity_type_manager;
+    $this->entityFieldManager = $entity_field_manager;
+    parent::__construct($configuration + ['bundle' => NULL], $plugin_id, $plugin_definition, $migration, $state);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration = NULL) {
+    return new static(
+      $configuration,
+      $plugin_id,
+      $plugin_definition,
+      $migration,
+      $container->get('state'),
+      $container->get('entity_type.manager'),
+      $container->get('entity_field.manager')
+    );
+  }
+
+  /**
+   * Returns all non-deleted field instances attached to a specific entity type.
+   *
+   * @param string $entity_type
+   *   The entity type ID.
+   * @param string|null $bundle
+   *   (optional) The bundle.
+   *
+   * @return array[]
+   *   The field instances, keyed by field name.
+   */
+  protected function getFields($entity_type, $bundle = NULL) {
+    $fieldConfig = $this->select('config', 'c')
+      ->fields('c')
+      ->condition('name', 'field.field.' . $entity_type . '.%', 'LIKE')
+      ->execute()
+      ->fetchAllAssoc('name');
+
+    $fields = [];
+    foreach ($fieldConfig as $config) {
+      $data = unserialize($config['data']);
+      // Status of false signifies the field is deleted. We do not return
+      // deleted data.
+      if ($data['status']) {
+        // If requested by configuration, filter by a bundle. Don't filter
+        // if it isn't configured.
+        if ($bundle && $data['bundle'] == $bundle) {
+          $fields[$data['field_name']] = $data;
+        }
+        else {
+          $fields[$data['field_name']] = $data;
+        }
+      }
+    }
+
+    return $fields;
+  }
+
+  /**
+   * Retrieves field values for a single field of a single entity.
+   *
+   * @param string $entity_type
+   *   Entity type.
+   * @param string $field_name
+   *   The field name.
+   * @param int $entity_id
+   *   The entity ID.
+   * @param int|null $revision_id
+   *   (optional) The entity revision ID.
+   *
+   * @throws \Drupal\migrate\MigrateException
+   *
+   * @return array
+   *   The raw field values, keyed by delta.
+   *
+   * @todo Support multilingual field values.
+   */
+  protected function getFieldValues($entity_type, $field_name, $entity_id, $revision_id = NULL) {
+    $table = $this->getDedicatedDataTableName($entity_type, $field_name);
+
+    $query = $this->select($table, 't')
+      ->fields('t')
+      ->condition('entity_id', $entity_id)
+      ->condition('deleted', 0);
+
+    if ($revision_id) {
+      $query->condition('revision_id', $revision_id);
+    }
+
+    $values = [];
+    foreach ($query->execute() as $row) {
+      foreach ($row as $key => $value) {
+        $delta = $row['delta'];
+        if (strpos($key, $field_name) === 0) {
+          $column = substr($key, strlen($field_name) + 1);
+          $values[$delta][$column] = $value;
+        }
+      }
+    }
+    return $values;
+  }
+
+  /**
+   * Get the table name keeping in mind the hashing logic for long names.
+   *
+   * @param string $entityType
+   *   Entity type id.
+   * @param string $field_name
+   *   Field name.
+   * @param bool $revision
+   *   If revision table or not.
+   *
+   * @see \Drupal\Core\Entity\Sql\DefaultTableMapping::generateFieldTableName
+   *
+   * @throws \Drupal\migrate\MigrateException
+   *
+   * @return string
+   *   The table name string.
+   */
+  protected function getDedicatedDataTableName($entityType, $field_name, $revision = FALSE) {
+    $separator = $revision ? '_revision__' : '__';
+    $tableName = $entityType . $separator . $field_name;
+
+    // This matches \Drupal\Core\Entity\Sql\DefaultTableMapping where longer
+    // table revision names get shortened by the Entity API.
+    if (strlen($tableName) > 48) {
+      $separator = $revision ? '_r__' : '__';
+
+      $query = $this->select('config', 'c')
+        ->fields('c', ['data'])
+        ->condition('name', "field.storage.{$entityType}.{$field_name}");
+      $fieldDefinitionData = $query->execute()->fetchField();
+
+      if ($fieldDefinitionData) {
+        $data = unserialize($fieldDefinitionData);
+        $uuid = $data['uuid'];
+      }
+      else {
+        throw new MigrateException(sprintf('Missing field storage config for field %s', $field_name));
+      }
+
+      $entityType = substr($entityType, 0, 34);
+      $fieldHash = substr(hash('sha256', $uuid), 0, 10);
+      $tableName = $entityType . $separator . $fieldHash;
+    }
+    return $tableName;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function query() {
+    $entityDefinition = $this->entityTypeManager->getDefinition($this->configuration['entity_type']);
+    $idKey = $entityDefinition->getKey('id');
+    $bundleKey = $entityDefinition->getKey('bundle');
+    $baseTable = $entityDefinition->getBaseTable();
+    $dataTable = $entityDefinition->getDataTable();
+
+    if ($dataTable) {
+      $query = $this->select($dataTable, 'd')
+        ->fields('d');
+      $alias = $query->innerJoin($baseTable, 'b', "b.{$idKey} = d.{$idKey}");
+      $query->fields($alias);
+      if (!empty($this->configuration['bundle'])) {
+        $query->condition("d.{$bundleKey}", $this->configuration['bundle']);
+      }
+    }
+    else {
+      $query = $this->select($baseTable, 'b')
+        ->fields('b');
+      if (!empty($this->configuration['bundle'])) {
+        $query->condition("b.{$bundleKey}", $this->configuration['bundle']);
+      }
+    }
+
+    return $query;
+
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function fields() {
+    $fieldDefinitions = $this->entityFieldManager->getBaseFieldDefinitions($this->configuration['entity_type']);
+    $fields = [];
+    foreach ($fieldDefinitions as $fieldName => $definition) {
+      $fields[$fieldName] = (string) $definition->getLabel();
+    }
+    return $fields;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function prepareRow(Row $row) {
+    $entityType = $this->configuration['entity_type'];
+    // Get Field API field values.
+    if (!$this->bundleFields) {
+      $this->bundleFields = $this->getFields($entityType, $this->configuration['bundle']);
+    }
+
+    $entityDefinition = $this->entityTypeManager->getDefinition($this->configuration['entity_type']);
+    $idKey = $entityDefinition->getKey('id');
+    foreach (array_keys($this->bundleFields) as $field) {
+      $entityId = $row->getSourceProperty($idKey);
+      $revisionId = NULL;
+      if ($entityDefinition->isRevisionable()) {
+        $revisionKey = $entityDefinition->getKey('revision');
+        $revisionId = $row->getSourceProperty($revisionKey);
+      }
+      $row->setSourceProperty($field, $this->getFieldValues($entityType, $field, $entityId, $revisionId));
+    }
+
+    return parent::prepareRow($row);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getIds() {
+    $entityDefinition = $this->entityTypeManager->getDefinition($this->configuration['entity_type']);
+    $idKey = $entityDefinition->getKey('id');
+    $ids[$idKey] = $this->getDefinitionFromEntity($idKey);
+
+    if ($entityDefinition->isTranslatable()) {
+      $langcodeKey = $entityDefinition->getKey('langcode');
+      $ids[$langcodeKey] = $this->getDefinitionFromEntity($langcodeKey);
+    }
+
+    return $ids;
+  }
+
+  /**
+   * Gets the field definition from a specific entity base field.
+   *
+   * @param string $key
+   *   The field ID key.
+   *
+   * @return array
+   *   An associative array with a structure that contains the field type, keyed
+   *   as 'type', together with field storage settings as they are returned by
+   *   FieldStorageDefinitionInterface::getSettings().
+   *
+   * @see \Drupal\migrate\Plugin\migrate\destination\EntityContentBase::getDefinitionFromEntity()
+   */
+  protected function getDefinitionFromEntity($key) {
+    /** @var \Drupal\Core\Field\FieldStorageDefinitionInterface[] $fieldDefinitions */
+    $fieldDefinitions = $this->entityFieldManager->getBaseFieldDefinitions($this->configuration['entity_type']);
+    $fieldDefinition = $fieldDefinitions[$key];
+
+    return [
+      'alias' => 'b',
+      'type' => $fieldDefinition->getType(),
+    ] + $fieldDefinition->getSettings();
+  }
+
+}
diff --git a/core/modules/migrate_drupal/tests/src/Kernel/Plugin/migrate/source/d8/ContentEntityTest.php b/core/modules/migrate_drupal/tests/src/Kernel/Plugin/migrate/source/d8/ContentEntityTest.php
new file mode 100755
index 0000000..c183eaf
--- /dev/null
+++ b/core/modules/migrate_drupal/tests/src/Kernel/Plugin/migrate/source/d8/ContentEntityTest.php
@@ -0,0 +1,98 @@
+<?php
+
+namespace Drupal\Tests\migrate_drupal\Kernel\Plugin\migrate\source\d8;
+
+/**
+ * Tests the entity content source plugin.
+ *
+ * @group migrate_drupal
+ */
+class ContentEntityTest extends ContentEntityTestBase {
+
+  /**
+   * Tests table destination.
+   */
+  public function testEntitySource() {
+    /** @var \Drupal\migrate\Plugin\MigrationPluginManagerInterface $migrationPluginManager */
+    $migrationPluginManager = \Drupal::service('plugin.manager.migration');
+    $definition = $this->migrationDefinition();
+
+    // User tests.
+    $definition['source']['entity_type'] = 'user';
+    $source = $migrationPluginManager->createStubMigration($definition)->getSourcePlugin();
+    $ids = $source->getIds();
+    $this->assertArrayHasKey('langcode', $ids);
+    $this->assertArrayHasKey('uid', $ids);
+    $fields = $source->fields();
+    $this->assertArrayHasKey('name', $fields);
+    $this->assertArrayHasKey('pass', $fields);
+    $this->assertArrayHasKey('mail', $fields);
+    $this->assertArrayHasKey('uid', $fields);
+    $this->assertArrayHasKey('roles', $fields);
+    $source->rewind();
+    $values = $source->current()->getSource();
+    $this->assertEquals('example@example.com', $values['mail']);
+    $this->assertEquals('user123', $values['name']);
+    $this->assertEquals(1, $values['uid']);
+
+    // File testing.
+    $definition['source']['entity_type'] = 'file';
+    $source = $migrationPluginManager->createStubMigration($definition)->getSourcePlugin();
+    $ids = $source->getIds();
+    $this->assertArrayHasKey('fid', $ids);
+    $fields = $source->fields();
+    $this->assertArrayHasKey('fid', $fields);
+    $this->assertArrayHasKey('filemime', $fields);
+    $this->assertArrayHasKey('filename', $fields);
+    $this->assertArrayHasKey('uid', $fields);
+    $this->assertArrayHasKey('uri', $fields);
+    $source->rewind();
+    $values = $source->current()->getSource();
+    $this->assertEquals('text/plain', $values['filemime']);
+    $this->assertEquals('public://foo.txt', $values['uri']);
+    $this->assertEquals('foo.txt', $values['filename']);
+    $this->assertEquals(1, $values['fid']);
+
+    // Node tests.
+    $definition['source']['entity_type'] = 'node';
+    $definition['source']['bundle'] = $this->bundle;
+    $source = $migrationPluginManager->createStubMigration($definition)->getSourcePlugin();
+    $ids = $source->getIds();
+    $this->assertArrayHasKey('langcode', $ids);
+    $this->assertArrayHasKey('nid', $ids);
+    $fields = $source->fields();
+    $this->assertArrayHasKey('nid', $fields);
+    $this->assertArrayHasKey('vid', $fields);
+    $this->assertArrayHasKey('title', $fields);
+    $this->assertArrayHasKey('uid', $fields);
+    $this->assertArrayHasKey('sticky', $fields);
+    $source->rewind();
+    $values = $source->current()->getSource();
+    $this->assertEquals($this->bundle, $values['type']);
+    $this->assertEquals('node', $values['entity_type']);
+    $this->assertEquals(1, $values['nid']);
+    $this->assertEquals(1, $values['status']);
+    $this->assertEquals('Apples', $values['title']);
+
+    // Media testing.
+    $definition['source']['entity_type'] = 'media';
+    $definition['source']['bundle'] = 'image';
+    $source = $migrationPluginManager->createStubMigration($definition)->getSourcePlugin();
+    $ids = $source->getIds();
+    $this->assertArrayHasKey('langcode', $ids);
+    $this->assertArrayHasKey('mid', $ids);
+    $fields = $source->fields();
+    $this->assertArrayHasKey('bundle', $fields);
+    $this->assertArrayHasKey('mid', $fields);
+    $this->assertArrayHasKey('name', $fields);
+    $this->assertArrayHasKey('status', $fields);
+    $source->rewind();
+    $values = $source->current()->getSource();
+    $this->assertEquals(1, $values['mid']);
+    $this->assertEquals('Foo media', $values['name']);
+    $this->assertEquals('Foo media', $values['thumbnail__title']);
+    $this->assertEquals(1, $values['uid']);
+    $this->assertEquals('image', $values['bundle']);
+  }
+
+}
diff --git a/core/modules/migrate_drupal/tests/src/Kernel/Plugin/migrate/source/d8/ContentEntityTestBase.php b/core/modules/migrate_drupal/tests/src/Kernel/Plugin/migrate/source/d8/ContentEntityTestBase.php
new file mode 100755
index 0000000..f08e0d6
--- /dev/null
+++ b/core/modules/migrate_drupal/tests/src/Kernel/Plugin/migrate/source/d8/ContentEntityTestBase.php
@@ -0,0 +1,166 @@
+<?php
+
+namespace Drupal\Tests\migrate_drupal\Kernel\Plugin\migrate\source\d8;
+
+use Drupal\field\Tests\EntityReference\EntityReferenceTestTrait;
+use Drupal\file\Entity\File;
+use Drupal\media\Entity\Media;
+use Drupal\media\Entity\MediaType;
+use Drupal\node\Entity\Node;
+use Drupal\node\Entity\NodeType;
+use Drupal\Tests\migrate\Kernel\MigrateTestBase;
+use Drupal\user\Entity\User;
+
+/**
+ * Tests the entity content source plugin.
+ *
+ * @group migrate_drupal
+ */
+abstract class ContentEntityTestBase extends MigrateTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = [
+    'migrate_drupal',
+    'migrate',
+    'user',
+    'system',
+    'node',
+    'field',
+    'file',
+    'image',
+    'media',
+    'media_test_source',
+    'text',
+    'filter',
+  ];
+
+  /**
+   * The bundle used in this test.
+   *
+   * @var string
+   */
+  protected $bundle = 'article';
+
+  /**
+   * The test user.
+   *
+   * @var \Drupal\user\UserInterface
+   */
+  protected $user;
+
+  /**
+   *
+   * @var \Drupal\node\NodeInterface
+   */
+  protected $node;
+
+  /**
+   * The migration plugin.
+   *
+   * @var \Drupal\migrate\Plugin\MigrationInterface
+   */
+  protected $migration;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    // Create article content type.
+    $nodeType = NodeType::create(['type' => $this->bundle, 'name' => 'Article']);
+    $nodeType->save();
+
+    $this->installEntitySchema('node');
+    $this->installEntitySchema('file');
+    $this->installEntitySchema('media');
+    $this->installEntitySchema('user');
+    $this->installSchema('system', ['sequences']);
+    $this->installSchema('user', 'users_data');
+    $this->installSchema('file', 'file_usage');
+
+    $this->installConfig($this->modules);
+
+    // Create a media type.
+    $mediaType = $this->createMediaType('test');
+
+    // Create some data.
+    $this->user = User::create([
+      'name' => 'user123',
+      'uid' => 1,
+      'mail' => 'example@example.com',
+    ]);
+    $this->user->save();
+    $this->node = Node::create([
+      'type' => $this->bundle,
+      'title' => 'Apples',
+      'uid' => $this->user->id(),
+    ]);
+    $this->node->save();
+    $file = File::create([
+      'filename' => 'foo.txt',
+      'uid' => $this->user->id(),
+      'uri' => 'public://foo.txt',
+    ]);
+    $file->save();
+    $media = Media::create([
+      'name' => 'Foo media',
+      'uid' => $this->user->id(),
+      'bundle' => $mediaType->id(),
+    ]);
+    $media->save();
+  }
+
+  /**
+   * Get a migration definition.
+   *
+   * @return array
+   *   The definition.
+   */
+  protected function migrationDefinition() {
+    return [
+      'source' => [
+        'plugin' => 'd8_content_entity',
+        'key' => 'default',
+      ],
+      'process' => [],
+      'destination' => [
+        'plugin' => 'null',
+      ],
+    ];
+  }
+
+  /**
+   * Create a media type for a source plugin.
+   *
+   * @param string $media_source_name
+   *   The name of the media source.
+   *
+   * @return \Drupal\media\MediaTypeInterface
+   *   A media type.
+   */
+  protected function createMediaType($media_source_name) {
+    $id = strtolower($this->randomMachineName());
+    $media_type = MediaType::create([
+      'id' => 'image',
+      'label' => 'Image',
+      'source' => $media_source_name,
+      'new_revision' => FALSE,
+    ]);
+    $media_type->save();
+    $source_field = $media_type->getSource()->createSourceField($media_type);
+    // The media type form creates a source field if it does not exist yet. The
+    // same must be done in a kernel test, since it does not use that form.
+    // @see \Drupal\media\MediaTypeForm::save()
+    $source_field->getFieldStorageDefinition()->save();
+    // The source field storage has been created, now the field can be saved.
+    $source_field->save();
+    $media_type->set('source_configuration', [
+      'source_field' => $source_field->getName(),
+    ])->save();
+    return $media_type;
+  }
+
+}
diff --git a/core/modules/taxonomy/src/Plugin/migrate/source/d8/Term.php b/core/modules/taxonomy/src/Plugin/migrate/source/d8/Term.php
new file mode 100644
index 0000000..1f64c72
--- /dev/null
+++ b/core/modules/taxonomy/src/Plugin/migrate/source/d8/Term.php
@@ -0,0 +1,59 @@
+<?php
+
+namespace Drupal\taxonomy\Plugin\migrate\source\d8;
+
+use Drupal\Core\Entity\EntityFieldManagerInterface;
+use Drupal\Core\Entity\EntityTypeManagerInterface;
+use Drupal\Core\State\StateInterface;
+use Drupal\migrate\Plugin\MigrationInterface;
+use Drupal\migrate\Row;
+use Drupal\migrate_drupal\Plugin\migrate\source\d8\ContentEntity;
+
+/**
+ * Drupal 8 taxonomy_term source from database.
+ *
+ * @MigrateSource(
+ *   id = "d8_term",
+ *   source_provider = "taxonomy"
+ * )
+ */
+class Term extends ContentEntity {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function __construct(array $configuration, $plugin_id, $plugin_definition, MigrationInterface $migration, StateInterface $state, EntityTypeManagerInterface $entity_type_manager, EntityFieldManagerInterface $entity_field_manager) {
+    $configuration['entity_type'] = 'taxonomy_term';
+    parent::__construct($configuration, $plugin_id, $plugin_definition, $migration, $state, $entity_type_manager, $entity_field_manager);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function prepareRow(Row $row) {
+    $tid = $row->getSourceProperty('tid');
+    $parent = $this->taxonomyTermGetParent($tid);
+    if ($parent !== FALSE) {
+      $row->setSourceProperty('parent', $parent);
+    }
+    return parent::prepareRow($row);
+  }
+
+  /**
+   * The parent value has custom storage, retrieve it directly.
+   *
+   * @param int $tid
+   *   The term id.
+   *
+   * @return bool|int
+   *   The parent term id or FALSE if there is none.
+   */
+  protected function taxonomyTermGetParent($tid) {
+    /** @var \Drupal\Core\Database\Query\SelectInterface $query */
+    $query = $this->select('taxonomy_term_hierarchy', 'h')
+      ->fields('h', ['parent'])
+      ->condition('tid', $tid);
+    return $query->execute()->fetchField();
+  }
+
+}
diff --git a/core/modules/taxonomy/tests/src/Kernel/Plugin/migrate/source/d8/TermTest.php b/core/modules/taxonomy/tests/src/Kernel/Plugin/migrate/source/d8/TermTest.php
new file mode 100755
index 0000000..471eaf3
--- /dev/null
+++ b/core/modules/taxonomy/tests/src/Kernel/Plugin/migrate/source/d8/TermTest.php
@@ -0,0 +1,152 @@
+<?php
+
+namespace Drupal\Tests\taxonomy\Kernel\Plugin\migrate\source\d8;
+
+use Drupal\Core\Field\FieldStorageDefinitionInterface;
+use Drupal\Core\Language\LanguageInterface;
+use Drupal\field\Tests\EntityReference\EntityReferenceTestTrait;
+use Drupal\taxonomy\Entity\Term;
+use Drupal\taxonomy\Entity\Vocabulary;
+use Drupal\Tests\migrate_drupal\Kernel\Plugin\migrate\source\d8\ContentEntityTestBase;
+
+/**
+ * Tests the term source plugin.
+ *
+ * @group migrate_drupal
+ */
+class TermTest extends ContentEntityTestBase {
+
+  use EntityReferenceTestTrait;
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = [
+    'taxonomy',
+  ];
+
+  /**
+   * The name of the field used in this test.
+   *
+   * @var string
+   */
+  protected $fieldName = 'field_entity_reference';
+
+  /**
+   * The vocabulary id.
+   *
+   * @var string
+   */
+  protected $vocabulary = 'fruit';
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+    $this->installConfig(['taxonomy']);
+
+    $this->installEntitySchema('taxonomy_term');
+    $this->installEntitySchema('taxonomy_vocabulary');
+
+    // Create a vocabulary.
+    $vocabulary = Vocabulary::create([
+      'name' => $this->vocabulary,
+      'description' => $this->vocabulary,
+      'vid' => $this->vocabulary,
+      'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
+    ]);
+    $vocabulary->save();
+
+    // Create a term reference field on node.
+    $this->createEntityReferenceField(
+      'node',
+      $this->bundle,
+      $this->fieldName,
+      'Term reference',
+      'taxonomy_term',
+      'default',
+      ['target_bundles' => [$this->vocabulary]],
+      FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED
+    );
+    // Create a term reference field on user.
+    $this->createEntityReferenceField(
+      'user',
+      'user',
+      $this->fieldName,
+      'Term reference',
+      'taxonomy_term',
+      'default',
+      ['target_bundles' => [$this->vocabulary]],
+      FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED
+    );
+
+    // Create some terms.
+    $term = Term::create([
+      'vid' => $this->vocabulary,
+      'name' => 'Apples',
+      'uid' => $this->user->id(),
+    ]);
+    $term->save();
+    $term2 = Term::create([
+      'vid' => $this->vocabulary,
+      'name' => 'Granny Smith',
+      'uid' => $this->user->id(),
+      'parent' => $term->id(),
+    ]);
+    $term2->save();
+    $this->user->set($this->fieldName, $term->id());
+    $this->user->save();
+    $this->node->set($this->fieldName, $term->id());
+    $this->node->save();
+  }
+
+  /**
+   * Tests table destination.
+   */
+  public function testEntitySource() {
+    /** @var \Drupal\migrate\Plugin\MigrationPluginManagerInterface $migrationPluginManager */
+    $migrationPluginManager = \Drupal::service('plugin.manager.migration');
+    $definition = $this->migrationDefinition();
+
+    // User tests.
+    $definition['source']['entity_type'] = 'user';
+    $source = $migrationPluginManager->createStubMigration($definition)->getSourcePlugin();
+    $source->rewind();
+    $values = $source->current()->getSource();
+    $this->assertEquals(1, $values[$this->fieldName][0]['target_id']);
+
+    // Node tests.
+    $definition['source']['entity_type'] = 'node';
+    $definition['source']['bundle'] = $this->bundle;
+    $source = $migrationPluginManager->createStubMigration($definition)->getSourcePlugin();
+    $source->rewind();
+    $values = $source->current()->getSource();
+    $this->assertEquals(1, $values[$this->fieldName][0]['target_id']);
+
+    // Term testing.
+    $definition['source']['plugin'] = 'd8_term';
+    $definition['source']['bundle'] = $this->vocabulary;
+    $source = $migrationPluginManager->createStubMigration($definition)->getSourcePlugin();
+    $ids = $source->getIds();
+    $this->assertArrayHasKey('langcode', $ids);
+    $this->assertArrayHasKey('tid', $ids);
+    $fields = $source->fields();
+    $this->assertArrayHasKey('vid', $fields);
+    $this->assertArrayHasKey('tid', $fields);
+    $this->assertArrayHasKey('name', $fields);
+    $source->rewind();
+    $values = $source->current()->getSource();
+    $this->assertEquals($this->vocabulary, $values['vid']);
+    $this->assertEquals(1, $values['tid']);
+    $this->assertEquals(0, $values['parent']);
+    $this->assertEquals('Apples', $values['name']);
+    $source->next();
+    $values = $source->current()->getSource();
+    $this->assertEquals($this->vocabulary, $values['vid']);
+    $this->assertEquals(2, $values['tid']);
+    $this->assertEquals(1, $values['parent']);
+    $this->assertEquals('Granny Smith', $values['name']);
+  }
+
+}
