diff --git a/modules/ingredient/migrations/d7_ingredient.yml b/modules/ingredient/migrations/d7_ingredient.yml
new file mode 100644
index 0000000..4f83bbc
--- /dev/null
+++ b/modules/ingredient/migrations/d7_ingredient.yml
@@ -0,0 +1,12 @@
+id: d7_ingredient
+label: Ingredients
+migration_tags:
+  - Drupal 7
+source:
+  plugin: d7_ingredient
+process:
+  id: id
+  name: name
+destination:
+  plugin: entity:ingredient
+migration_dependencies: {  }
diff --git a/modules/ingredient/src/Plugin/migrate/destination/EntityIngredient.php b/modules/ingredient/src/Plugin/migrate/destination/EntityIngredient.php
new file mode 100644
index 0000000..68548c5
--- /dev/null
+++ b/modules/ingredient/src/Plugin/migrate/destination/EntityIngredient.php
@@ -0,0 +1,12 @@
+<?php
+
+namespace Drupal\ingredient\Plugin\migrate\destination;
+
+use Drupal\migrate\Plugin\migrate\destination\EntityContentBase;
+
+/**
+ * @MigrateDestination(
+ *   id = "entity:ingredient"
+ * )
+ */
+class EntityIngredient extends EntityContentBase {}
diff --git a/modules/ingredient/src/Plugin/migrate/source/d7/Ingredient.php b/modules/ingredient/src/Plugin/migrate/source/d7/Ingredient.php
new file mode 100644
index 0000000..495efeb
--- /dev/null
+++ b/modules/ingredient/src/Plugin/migrate/source/d7/Ingredient.php
@@ -0,0 +1,43 @@
+<?php
+
+namespace Drupal\ingredient\Plugin\migrate\source\d7;
+
+use Drupal\migrate_drupal\Plugin\migrate\source\DrupalSqlBase;
+
+/**
+ * Drupal 7 ingredient source from database.
+ *
+ * @MigrateSource(
+ *   id = "d7_ingredient",
+ *   source_module = "recipe"
+ * )
+ */
+class Ingredient extends DrupalSqlBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function query() {
+    return $this->select('recipe_ingredient', 'i')
+      ->fields('i', ['id', 'name'])
+      ->orderBy('i.id');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function fields() {
+    return [
+      'id' => $this->t('Ingredient ID'),
+      'name' => $this->t('Ingredient name'),
+    ];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getIds() {
+    return ['id' => ['type' => 'integer']];
+  }
+
+}
diff --git a/modules/ingredient/tests/fixtures/ingredient713.php b/modules/ingredient/tests/fixtures/ingredient713.php
new file mode 100644
index 0000000..8bc8239
--- /dev/null
+++ b/modules/ingredient/tests/fixtures/ingredient713.php
@@ -0,0 +1,263 @@
+<?php
+// @codingStandardsIgnoreFile
+/**
+ * @file
+ * Filled installation of Recipe 7.x-1.3, for test purposes.
+ *
+ * This file was originally generated by the dump-database-d7.sh tool, from an
+ * installation of Drupal 7:
+ */
+
+use Drupal\Core\Database\Database;
+
+$connection = Database::getConnection();
+
+$connection->schema()->createTable('recipe_ingredient', array(
+  'fields' => array(
+    'id' => array(
+      'type' => 'serial',
+      'unsigned' => TRUE,
+      'not null' => TRUE,
+    ),
+    'name' => array(
+      'type' => 'varchar',
+      'length' => 255,
+    ),
+    'link' => array(
+      'type' => 'int',
+      'unsigned' => TRUE,
+      'not null' => TRUE,
+    ),
+  ),
+  'primary key' => array(
+    'id',
+  ),
+  'module' => 'recipe',
+  'name' => 'recipe_ingredient',
+));
+$connection->insert('recipe_ingredient')->fields(array(
+  'id',
+  'name',
+  'link',
+))
+  ->values(array(
+    'id' => '1',
+    'name' => 'water',
+    'link' => '0',
+  ))
+  ->values(array(
+    'id' => '2',
+    'name' => 'salt',
+    'link' => '0',
+  ))
+  ->execute();
+
+$connection->schema()->createTable('recipe_node_ingredient', array(
+  'fields' => array(
+    'id' => array(
+      'type' => 'serial',
+      'unsigned' => TRUE,
+      'not null' => TRUE,
+    ),
+    'nid' => array(
+      'type' => 'int',
+      'unsigned' => TRUE,
+      'not null' => TRUE,
+    ),
+    'unit_key' => array(
+      'type' => 'varchar',
+      'length' => 255,
+      'not null' => TRUE,
+      'default' => '',
+    ),
+    'quantity' => array(
+      'type' => 'float',
+      'not null' => FALSE,
+    ),
+    'ingredient_id' => array(
+      'type' => 'int',
+      'unsigned' => TRUE,
+      'not null' => TRUE,
+    ),
+    'weight' => array(
+      'type' => 'int',
+      'not null' => TRUE,
+      'default' => 0,
+    ),
+    'note' => array(
+      'type' => 'varchar',
+      'length' => 255,
+      'not null' => TRUE,
+      'default' => '',
+    ),
+  ),
+  'primary key' => array(
+    'id',
+  ),
+  'module' => 'recipe',
+  'name' => 'recipe_node_ingredient',
+));
+$connection->insert('recipe_node_ingredient')->fields(array(
+  'id',
+  'nid',
+  'unit_key',
+  'quantity',
+  'ingredient_id',
+  'weight',
+  'note',
+))
+  ->values(array(
+    'id' => '1',
+    'nid' => '1',
+    'unit_key' => 'cup',
+    'quantity' => '2',
+    'ingredient_id' => '1',
+    'weight' => '0',
+    'note' => 'cold',
+  ))
+  ->values(array(
+    'id' => '2',
+    'nid' => '1',
+    'unit_key' => 'tablespoon',
+    'quantity' => '1',
+    'ingredient_id' => '2',
+    'weight' => '1',
+    'note' => '',
+  ))
+  ->execute();
+
+$connection->schema()->createTable('system', array(
+  'fields' => array(
+    'filename' => array(
+      'type' => 'varchar',
+      'not null' => TRUE,
+      'length' => '255',
+      'default' => '',
+    ),
+    'name' => array(
+      'type' => 'varchar',
+      'not null' => TRUE,
+      'length' => '255',
+      'default' => '',
+    ),
+    'type' => array(
+      'type' => 'varchar',
+      'not null' => TRUE,
+      'length' => '12',
+      'default' => '',
+    ),
+    'owner' => array(
+      'type' => 'varchar',
+      'not null' => TRUE,
+      'length' => '255',
+      'default' => '',
+    ),
+    'status' => array(
+      'type' => 'int',
+      'not null' => TRUE,
+      'size' => 'normal',
+      'default' => '0',
+    ),
+    'bootstrap' => array(
+      'type' => 'int',
+      'not null' => TRUE,
+      'size' => 'normal',
+      'default' => '0',
+    ),
+    'schema_version' => array(
+      'type' => 'int',
+      'not null' => TRUE,
+      'size' => 'normal',
+      'default' => '-1',
+    ),
+    'weight' => array(
+      'type' => 'int',
+      'not null' => TRUE,
+      'size' => 'normal',
+      'default' => '0',
+    ),
+    'info' => array(
+      'type' => 'blob',
+      'not null' => FALSE,
+      'size' => 'normal',
+    ),
+  ),
+  'primary key' => array(
+    'filename',
+  ),
+  'mysql_character_set' => 'utf8',
+));
+
+$connection->insert('system')->fields(array(
+  'filename',
+  'name',
+  'type',
+  'owner',
+  'status',
+  'bootstrap',
+  'schema_version',
+  'weight',
+  'info',
+))
+  ->values(array(
+    'filename' => 'sites/default/modules/recipe/recipe.module',
+    'name' => 'recipe',
+    'type' => 'module',
+    'owner' => '',
+    'status' => '1',
+    'bootstrap' => '0',
+    'schema_version' => '7004',
+    'weight' => '0',
+    'info' => 'a:11:{s:4:"name";s:6:"Recipe";s:11:"description";s:28:"Collect and display recipes.";s:7:"package";s:6:"Recipe";s:4:"core";s:3:"7.x";s:7:"version";s:7:"7.x-1.3";s:7:"project";s:6:"recipe";s:9:"datestamp";s:10:"1335415286";s:12:"dependencies";a:0:{}s:3:"php";s:5:"5.2.4";s:5:"files";a:0:{}s:9:"bootstrap";i:0;}',
+  ))
+  ->execute();
+
+$connection->schema()->createTable('variable', array(
+  'fields' => array(
+    'name' => array(
+      'type' => 'varchar',
+      'not null' => TRUE,
+      'length' => '128',
+      'default' => '',
+    ),
+    'value' => array(
+      'type' => 'blob',
+      'not null' => TRUE,
+      'size' => 'normal',
+    ),
+  ),
+  'primary key' => array(
+    'name',
+  ),
+  'mysql_character_set' => 'utf8',
+));
+
+$connection->insert('variable')->fields(array(
+  'name',
+  'value',
+))
+  ->values(array(
+    'name' => 'recipe_default_unit',
+    'value' => 's:0:"";',
+  ))
+  ->values(array(
+    'name' => 'recipe_fraction_display',
+    'value' => 's:16:"{%d }%d&frasl;%d";',
+  ))
+  ->values(array(
+    'name' => 'recipe_ingredient_name_normalize',
+    'value' => 's:1:"0";',
+  ))
+  ->values(array(
+    'name' => 'recipe_preferred_system_of_measure',
+    'value' => 's:1:"0";',
+  ))
+  ->values(array(
+    'name' => 'recipe_preferred_system_of_measure_limit',
+    'value' => 'i:0;',
+  ))
+  ->values(array(
+    'name' => 'recipe_unit_display',
+    'value' => 's:1:"0";',
+  ))
+  ->execute();
diff --git a/modules/ingredient/tests/src/Kernel/Migrate/d7/MigrateIngredientTest.php b/modules/ingredient/tests/src/Kernel/Migrate/d7/MigrateIngredientTest.php
new file mode 100644
index 0000000..fbc994f
--- /dev/null
+++ b/modules/ingredient/tests/src/Kernel/Migrate/d7/MigrateIngredientTest.php
@@ -0,0 +1,72 @@
+<?php
+
+namespace Drupal\Tests\ingredient\Kernel\Migrate\d7;
+
+use Drupal\Core\Database\Database;
+use Drupal\ingredient\Entity\Ingredient;
+use Drupal\ingredient\IngredientInterface;
+use Drupal\Tests\migrate_drupal\Kernel\d7\MigrateDrupal7TestBase;
+
+/**
+ * Ingredients migration.
+ *
+ * @group recipe
+ */
+class MigrateIngredientTest extends MigrateDrupal7TestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = [
+    'ingredient',
+  ];
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+    $this->installEntitySchema('ingredient');
+    $this->executeMigrations([
+      'd7_ingredient',
+    ]);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getFixtureFilePath() {
+    return __DIR__ . '/../../../../fixtures/ingredient713.php';
+  }
+
+  /**
+   * Asserts various aspects of an ingredient.
+   *
+   * @param string $id
+   *   The ingredient ID.
+   * @param string $label
+   *   The ingredient name.
+   */
+  protected function assertEntity($id, $label) {
+    /** @var \Drupal\ingredient\IngredientInterface $ingredient */
+    $ingredient = Ingredient::load($id);
+    $this->assertTrue($ingredient instanceof IngredientInterface);
+    $this->assertSame($label, $ingredient->label());
+  }
+
+  /**
+   * Tests the Drupal 7 ingredient to Drupal 8 migration.
+   */
+  public function testIngredient() {
+    $ingredients = Database::getConnection('default', 'migrate')
+      ->select('recipe_ingredient', 'i')
+      ->fields('i')
+      ->execute()
+      ->fetchAll();
+
+    foreach ($ingredients as $source) {
+      $this->assertEntity($source->id, $source->name);
+    }
+  }
+
+}
diff --git a/tests/upgrade/drupal-7.recipe.database.php b/tests/fixtures/recipe713.php
similarity index 99%
rename from tests/upgrade/drupal-7.recipe.database.php
rename to tests/fixtures/recipe713.php
index 8622e25..63223cd 100644
--- a/tests/upgrade/drupal-7.recipe.database.php
+++ b/tests/fixtures/recipe713.php
@@ -1,5 +1,5 @@
 <?php
-
+// @codingStandardsIgnoreFile
 /**
  * @file
  * Filled installation of Recipe 7.x-1.3, for test purposes.
