diff --git a/core/modules/field/lib/Drupal/field/Tests/FieldAttachOtherTest.php b/core/modules/field/lib/Drupal/field/Tests/FieldAttachOtherTest.php
index 6322522..b8632a4 100644
--- a/core/modules/field/lib/Drupal/field/Tests/FieldAttachOtherTest.php
+++ b/core/modules/field/lib/Drupal/field/Tests/FieldAttachOtherTest.php
@@ -282,7 +282,7 @@ function testFieldAttachCache() {
     $this->assertEqual($cache->data[$this->field_name_2][$langcode], $values, 'Cached: correct cache entry on load');
 
     // Update with different values, and check that the cache entry is wiped.
-    $values = $this->_generateTestFieldValues($this->field_name_2['cardinality']);
+    $values = $this->_generateTestFieldValues($this->field_2->getFieldCardinality());
     $entity = entity_create($entity_type, array(
       'type' => $entity_type,
       'id' => $entity->id(),
@@ -302,8 +302,8 @@ function testFieldAttachCache() {
       'type' => $entity_type,
       'id' => $entity->id(),
     ));
-    $values = $this->_generateTestFieldValues($this->field_name_2['cardinality']);
-    $entity->{$this->field_name} = $values;
+    $values = $this->_generateTestFieldValues($this->field_2->getFieldCardinality());
+    $entity->{$this->field_name_2} = $values;
     $entity->setNewRevision();
     $entity->save();
     $this->assertFalse(cache('field')->get($cid), 'Cached: no cache entry on new revision creation');
diff --git a/core/modules/field/lib/Drupal/field/Tests/FieldInfoTest.php b/core/modules/field/lib/Drupal/field/Tests/FieldInfoTest.php
index 9c433aa..dfbfcf9 100644
--- a/core/modules/field/lib/Drupal/field/Tests/FieldInfoTest.php
+++ b/core/modules/field/lib/Drupal/field/Tests/FieldInfoTest.php
@@ -23,7 +23,7 @@ public static function getInfo() {
   function testFieldInfo() {
     // Test that field_test module's fields, widgets, and formatters show up.
 
-    $field_test_info = field_test_field_info();
+    $field_test_info = $this->field_test_field_info();
     $info = \Drupal::service('plugin.manager.entity.field.field_type')->getDefinitions();
     foreach ($field_test_info as $t_key => $field_type) {
       foreach ($field_type as $key => $val) {
@@ -283,7 +283,7 @@ function testFieldMap() {
    * Test that the field_info settings convenience functions work.
    */
   function testSettingsInfo() {
-    $info = field_test_field_info();
+    $info = $this->field_test_field_info();
     foreach ($info as $type => $data) {
       $field_type_manager = \Drupal::service('plugin.manager.entity.field.field_type');
       $this->assertIdentical($field_type_manager->getDefaultSettings($type), $data['settings'], format_string("field settings service returns %type's field settings", array('%type' => $type)));
@@ -327,4 +327,50 @@ function testWidgetDefinition() {
     $this->assertTrue(in_array('test_field', $widget_definition['field_types']), "The 'test_field_widget_multiple' widget is enabled for the 'test_field' field type in field_test_field_widget_info_alter().");
   }
 
+  /**
+   * Returns field info definition.
+   */
+  protected function field_test_field_info() {
+    return array(
+      'test_field' => array(
+        'label' => t('Test field'),
+        'description' => t('Dummy field type used for tests.'),
+        'settings' => array(
+          'test_field_setting' => 'dummy test string',
+          'changeable' => 'a changeable field setting',
+          'unchangeable' => 'an unchangeable field setting',
+        ),
+        'instance_settings' => array(
+          'test_instance_setting' => 'dummy test string',
+          'test_hook_field_load' => FALSE,
+        ),
+        'default_widget' => 'test_field_widget',
+        'default_formatter' => 'field_test_default',
+        'class' => 'Drupal\field_test\Plugin\field\field_type\TestItem',
+      ),
+      'shape' => array(
+        'label' => t('Shape'),
+        'description' => t('Another dummy field type.'),
+        'settings' => array(
+          'foreign_key_name' => 'shape',
+        ),
+        'instance_settings' => array(),
+        'default_widget' => 'test_field_widget',
+        'default_formatter' => 'field_test_default',
+        'class' => 'Drupal\field_test\Plugin\field\field_type\ShapeItem',
+      ),
+      'hidden_test_field' => array(
+        'no_ui' => TRUE,
+        'label' => t('Hidden from UI test field'),
+        'description' => t('Dummy hidden field type used for tests.'),
+        'settings' => array(),
+        'instance_settings' => array(),
+        'default_widget' => 'test_field_widget',
+        'default_formatter' => 'field_test_default',
+        'class' => 'Drupal\field_test\Plugin\field\field_type\HiddenTestItem',
+      ),
+    );
+  }
+
+
 }
diff --git a/core/modules/field/tests/modules/field_test/field_test.field.inc b/core/modules/field/tests/modules/field_test/field_test.field.inc
index 3ac75fa..3e10d6a 100644
--- a/core/modules/field/tests/modules/field_test/field_test.field.inc
+++ b/core/modules/field/tests/modules/field_test/field_test.field.inc
@@ -9,50 +9,6 @@
 use Drupal\field\FieldException;
 use Drupal\field\FieldInterface;
 
-/**
- * Implements hook_field_info().
- */
-function field_test_field_info() {
-  return array(
-    'test_field' => array(
-      'label' => t('Test field'),
-      'description' => t('Dummy field type used for tests.'),
-      'settings' => array(
-        'test_field_setting' => 'dummy test string',
-        'changeable' => 'a changeable field setting',
-        'unchangeable' => 'an unchangeable field setting',
-      ),
-      'instance_settings' => array(
-        'test_instance_setting' => 'dummy test string',
-        'test_hook_field_load' => FALSE,
-      ),
-      'default_widget' => 'test_field_widget',
-      'default_formatter' => 'field_test_default',
-      'class' => 'Drupal\field_test\Type\TestItem',
-    ),
-    'shape' => array(
-      'label' => t('Shape'),
-      'description' => t('Another dummy field type.'),
-      'settings' => array(
-        'foreign_key_name' => 'shape',
-      ),
-      'instance_settings' => array(),
-      'default_widget' => 'test_field_widget',
-      'default_formatter' => 'field_test_default',
-      'class' => 'Drupal\field_test\Type\ShapeItem',
-    ),
-    'hidden_test_field' => array(
-      'no_ui' => TRUE,
-      'label' => t('Hidden from UI test field'),
-      'description' => t('Dummy hidden field type used for tests.'),
-      'settings' => array(),
-      'instance_settings' => array(),
-      'default_widget' => 'test_field_widget',
-      'default_formatter' => 'field_test_default',
-      'class' => 'Drupal\field_test\Type\HiddenTestItem',
-    ),
-  );
-}
 
 /**
  * Implements hook_field_widget_info_alter().
@@ -71,127 +27,6 @@ function field_test_field_update_forbid($field, $prior_field) {
 }
 
 /**
- * Implements hook_field_load().
- */
-function field_test_field_load($entity_type, $entities, $field, $instances, $langcode, &$items, $age) {
-  $args = func_get_args();
-  field_test_memorize(__FUNCTION__, $args);
-
-  foreach ($items as $id => $item) {
-    // To keep the test non-intrusive, only act for instances with the
-    // test_hook_field_load setting explicitly set to TRUE.
-    if (!empty($instances[$id]['settings']['test_hook_field_load'])) {
-      foreach ($item as $delta => $value) {
-        // Don't add anything on empty values.
-        if ($value) {
-          $items[$id][$delta]['additional_key'] = 'additional_value';
-        }
-      }
-    }
-  }
-}
-
-/**
- * Implements hook_field_insert().
- */
-function field_test_field_insert(EntityInterface $entity, $field, $instance, $items) {
-  $args = func_get_args();
-  field_test_memorize(__FUNCTION__, $args);
-}
-
-/**
- * Implements hook_field_update().
- */
-function field_test_field_update(EntityInterface $entity, $field, $instance, $items) {
-  $args = func_get_args();
-  field_test_memorize(__FUNCTION__, $args);
-}
-
-/**
- * Implements hook_field_delete().
- */
-function field_test_field_delete(EntityInterface $entity, $field, $instance, $items) {
-  $args = func_get_args();
-  field_test_memorize(__FUNCTION__, $args);
-}
-
-/**
- * Implements hook_field_validate().
- *
- * Possible error codes:
- * - 'field_test_invalid': The value is invalid.
- */
-function field_test_field_validate(EntityInterface $entity = NULL, $field, $instance, $langcode, $items, &$errors) {
-  $args = func_get_args();
-  field_test_memorize(__FUNCTION__, $args);
-
-  foreach ($items as $delta => $item) {
-    if ($item['value'] == -1) {
-      $errors[$field['field_name']][$langcode][$delta][] = array(
-        'error' => 'field_test_invalid',
-        'message' => t('%name does not accept the value -1.', array('%name' => $instance['label'])),
-      );
-    }
-  }
-}
-
-/**
- * Implements hook_field_is_empty().
- */
-function field_test_field_is_empty($item, $field_type) {
-  if ($field_type == 'test_field') {
-    return empty($item['value']);
-  }
-  return empty($item['shape']) && empty($item['color']);
-}
-
-/**
- * Implements hook_field_settings_form().
- */
-function field_test_field_settings_form($field, $instance) {
-  $settings = $field['settings'];
-
-  $form['test_field_setting'] = array(
-    '#type' => 'textfield',
-    '#title' => t('Field test field setting'),
-    '#default_value' => $settings['test_field_setting'],
-    '#required' => FALSE,
-    '#description' => t('A dummy form element to simulate field setting.'),
-  );
-
-  return $form;
-}
-
-/**
- * Implements hook_field_instance_settings_form().
- */
-function field_test_field_instance_settings_form($field, $instance) {
-  $settings = $instance['settings'];
-
-  $form['test_instance_setting'] = array(
-    '#type' => 'textfield',
-    '#title' => t('Field test field instance setting'),
-    '#default_value' => $settings['test_instance_setting'],
-    '#required' => FALSE,
-    '#description' => t('A dummy form element to simulate field instance setting.'),
-  );
-
-  return $form;
-}
-
-/**
- * Form element validation handler for 'test_field_widget_multiple' widget.
- */
-function field_test_widget_multiple_validate($element, &$form_state) {
-  $values = array_map('trim', explode(',', $element['#value']));
-  $items = array();
-  foreach ($values as $value) {
-    $items[] = array('value' => $value);
-  }
-  form_set_value($element, $items, $form_state);
-}
-
-/**
  * Sample 'default value' callback.
  */
 function field_test_default_value(EntityInterface $entity, $field, $instance) {
diff --git a/core/modules/field/tests/modules/field_test/field_test.install b/core/modules/field/tests/modules/field_test/field_test.install
deleted file mode 100644
index a079829..0000000
--- a/core/modules/field/tests/modules/field_test/field_test.install
+++ /dev/null
@@ -1,62 +0,0 @@
-<?php
-
-/**
- * @file
- * Install, update and uninstall functions for the field_test module.
- */
-
-/**
- * Implements hook_install().
- */
-function field_test_install() {
-  // hook_entity_info_alter() needs to be executed as last.
-  module_set_weight('field_test', 1);
-}
-
-/**
- * Implements hook_field_schema().
- */
-function field_test_field_schema($field) {
-  if ($field['type'] == 'test_field') {
-    return array(
-      'columns' => array(
-        'value' => array(
-          'type' => 'int',
-          'size' => 'medium',
-          'not null' => FALSE,
-        ),
-      ),
-      'indexes' => array(
-        'value' => array('value'),
-      ),
-    );
-  }
-  else {
-    $foreign_keys = array();
-    // The 'foreign keys' key is not always used in tests.
-    if (!empty($field['settings']['foreign_key_name'])) {
-      $foreign_keys['foreign keys'] = array(
-        // This is a dummy foreign key definition, references a table that
-        // doesn't exist, but that's not a problem.
-        $field['settings']['foreign_key_name'] => array(
-          'table' => $field['settings']['foreign_key_name'],
-          'columns' => array($field['settings']['foreign_key_name'] => 'id'),
-        ),
-      );
-    }
-    return array(
-      'columns' => array(
-        'shape' => array(
-          'type' => 'varchar',
-          'length' => 32,
-          'not null' => FALSE,
-        ),
-        'color' => array(
-          'type' => 'varchar',
-          'length' => 32,
-          'not null' => FALSE,
-        ),
-      ),
-    ) + $foreign_keys;
-  }
-}
diff --git a/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/Plugin/Validation/Constraint/TestFieldConstraint.php b/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/Plugin/Validation/Constraint/TestFieldConstraint.php
new file mode 100644
index 0000000..fb258a6
--- /dev/null
+++ b/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/Plugin/Validation/Constraint/TestFieldConstraint.php
@@ -0,0 +1,61 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\field_test\Plugin\Validation\Constraint\TestField.
+ */
+
+namespace Drupal\field_test\Plugin\Validation\Constraint;
+
+use Symfony\Component\Validator\Constraint;
+use Drupal\Component\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+
+
+/**
+ * Checks if a value is a valid.
+ *
+ * @Plugin(
+ *   id = "TestField",
+ *   label = @Translation("Test Field", context = "Validation"),
+ *   type = { "integer" }
+ * )
+ */
+class TestFieldConstraint extends Constraint {
+
+  /**
+   * The default violation message.
+   *
+   * @var string
+   */
+  public $message = '%name does not accept the value @compare.';
+
+  /**
+   * The compare value.
+   *
+   * @var int
+   */
+  public $compare;
+
+  /**
+   * The compare name.
+   *
+   * @var string
+   */
+  public $name;
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getDefaultOption() {
+    return 'compare';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getRequiredOptions() {
+    return array('compare', 'name');
+  }
+
+}
diff --git a/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/Plugin/Validation/Constraint/TestFieldConstraintValidator.php b/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/Plugin/Validation/Constraint/TestFieldConstraintValidator.php
new file mode 100644
index 0000000..25e85de
--- /dev/null
+++ b/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/Plugin/Validation/Constraint/TestFieldConstraintValidator.php
@@ -0,0 +1,28 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\field_test\Plugin\Validation\Constraint\TestFieldConstraintValidator.
+ */
+
+namespace Drupal\field_test\Plugin\Validation\Constraint;
+
+use Symfony\Component\Validator\Constraint;
+use Symfony\Component\Validator\ConstraintValidator;
+
+/**
+ * Validates the TestField constraint.
+ */
+class TestFieldConstraintValidator extends ConstraintValidator {
+
+  /**
+   * Implements \Symfony\Component\Validator\ConstraintValidatorInterface::validate().
+   */
+  public function validate($value, Constraint $constraint) {
+
+    if ($value == $constraint->compare) {
+      $this->context->addViolation($constraint->message, array('%name' => $constraint->name, '@compare' => $constraint->compare));
+    }
+  }
+
+}
diff --git a/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/Type/HiddenTestItem.php b/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/Plugin/field/field_type/HiddenTestItem.php
similarity index 53%
rename from core/modules/field/tests/modules/field_test/lib/Drupal/field_test/Type/HiddenTestItem.php
rename to core/modules/field/tests/modules/field_test/lib/Drupal/field_test/Plugin/field/field_type/HiddenTestItem.php
index 7a9dd30..5d909e4 100644
--- a/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/Type/HiddenTestItem.php
+++ b/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/Plugin/field/field_type/HiddenTestItem.php
@@ -2,13 +2,25 @@
 
 /**
  * @file
- * Contains \Drupal\field_test\Type\HiddenTestItem.
+ * Contains \Drupal\field_test\Plugin\field\field_type\HiddenTestItem.
  */
 
-namespace Drupal\field_test\Type;
+namespace Drupal\field_test\Plugin\field\field_type;
+
+use Drupal\Core\Entity\Annotation\FieldType;
+use Drupal\Core\Annotation\Translation;
 
 /**
- * Defines the 'test_field' entity field item.
+ * Defines the 'hidden_test' entity field item.
+ *
+ * @FieldType(
+ *   id = "hidden_test_field",
+ *   label = @Translation("Hidden from UI test field"),
+ *   description = @Translation("Dummy hidden field type used for tests."),
+ *   no_ui = TRUE,
+ *   default_widget = "test_field_widget",
+ *   default_formatter = "field_test_default"
+ * )
  */
 class HiddenTestItem extends TestItem {
 
diff --git a/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/Plugin/field/field_type/ShapeItem.php b/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/Plugin/field/field_type/ShapeItem.php
new file mode 100644
index 0000000..e7d8c7a
--- /dev/null
+++ b/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/Plugin/field/field_type/ShapeItem.php
@@ -0,0 +1,98 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\field_test\Plugin\field\field_type\ShapeItem.
+ */
+
+namespace Drupal\field_test\Plugin\field\field_type;
+
+use Drupal\Core\Entity\Annotation\FieldType;
+use Drupal\Core\Annotation\Translation;
+use Drupal\field\FieldInterface;
+use Drupal\field\Plugin\Type\FieldType\ConfigFieldItemBase;
+
+/**
+ * Defines the 'shape_field' entity field item.
+ *
+ * @FieldType(
+ *   id = "shape",
+ *   label = @Translation("Shape"),
+ *   description = @Translation("Another dummy field type."),
+ *   settings = {
+ *     "foreign_key_name" = "shape"
+ *   },
+ *   default_widget = "test_field_widget",
+ *   default_formatter = "field_test_default"
+ * )
+ */
+class ShapeItem extends ConfigFieldItemBase {
+
+  /**
+   * Property definitions of the contained properties.
+   *
+   * @see ShapeItem::getPropertyDefinitions()
+   *
+   * @var array
+   */
+  static $propertyDefinitions;
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getPropertyDefinitions() {
+
+    if (!isset(static::$propertyDefinitions)) {
+      static::$propertyDefinitions['shape'] = array(
+        'type' => 'string',
+        'label' => t('Shape'),
+      );
+      static::$propertyDefinitions['color'] = array(
+        'type' => 'string',
+        'label' => t('Color'),
+      );
+    }
+    return static::$propertyDefinitions;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function schema(FieldInterface $field) {
+    $foreign_keys = array();
+    // The 'foreign keys' key is not always used in tests.
+    if ($field->getFieldSetting('foreign_key_name')) {
+      $foreign_keys['foreign keys'] = array(
+        // This is a dummy foreign key definition, references a table that
+        // doesn't exist, but that's not a problem.
+        $field['settings']['foreign_key_name'] => array(
+          'table' => $field->getFieldSetting('foreign_key_name'),
+          'columns' => array($field->getFieldSetting('foreign_key_name') => 'id'),
+        ),
+      );
+    }
+    return array(
+      'columns' => array(
+        'shape' => array(
+          'type' => 'varchar',
+          'length' => 32,
+          'not null' => FALSE,
+        ),
+        'color' => array(
+          'type' => 'varchar',
+          'length' => 32,
+          'not null' => FALSE,
+        ),
+      ),
+    ) + $foreign_keys;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function isEmpty() {
+    $item = $this->getValue();
+    return empty($item['shape']) && empty($item['color']);
+  }
+
+}
diff --git a/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/Plugin/field/field_type/TestItem.php b/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/Plugin/field/field_type/TestItem.php
new file mode 100644
index 0000000..316eadd
--- /dev/null
+++ b/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/Plugin/field/field_type/TestItem.php
@@ -0,0 +1,153 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\field_test\Plugin\field\field_type\TestItem.
+ */
+
+namespace Drupal\field_test\Plugin\field\field_type;
+
+use Drupal\Core\Entity\Annotation\FieldType;
+use Drupal\Core\Annotation\Translation;
+use Drupal\Core\Entity\Field\PrepareCacheInterface;
+use Drupal\field\FieldInterface;
+use Drupal\field\Plugin\Type\FieldType\ConfigFieldItemBase;
+
+/**
+ * Defines the 'test_field' entity field item.
+ *
+ * @FieldType(
+ *   id = "test_field",
+ *   label = @Translation("Test field"),
+ *   description = @Translation("Dummy field type used for tests."),
+ *   settings = {
+ *     "test_field_setting" = "dummy test string",
+ *     "changeable" =  "a changeable field setting",
+ *     "unchangeable" = "an unchangeable field setting"
+ *   },
+ *   instance_settings = {
+ *     "test_instance_setting" = "dummy test string",
+ *     "test_hook_field_load" = FALSE
+ *   },
+ *   default_widget = "test_field_widget",
+ *   default_formatter = "field_test_default"
+ * )
+ */
+class TestItem extends ConfigFieldItemBase implements PrepareCacheInterface {
+
+  /**
+   * Property definitions of the contained properties.
+   *
+   * @see TestItem::getPropertyDefinitions()
+   *
+   * @var array
+   */
+  static $propertyDefinitions;
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getPropertyDefinitions() {
+
+    if (!isset(static::$propertyDefinitions)) {
+      static::$propertyDefinitions['value'] = array(
+        'type' => 'integer',
+        'label' => t('Test integer value'),
+      );
+    }
+    return static::$propertyDefinitions;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function schema(FieldInterface $field) {
+    return array(
+      'columns' => array(
+        'value' => array(
+          'type' => 'int',
+          'size' => 'medium',
+          'not null' => FALSE,
+        ),
+      ),
+      'indexes' => array(
+        'value' => array('value'),
+      ),
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function settingsForm(array $form, array &$form_state, $has_data) {
+    $form['test_field_setting'] = array(
+      '#type' => 'textfield',
+      '#title' => t('Field test field setting'),
+      '#default_value' => $this->getFieldSetting('test_field_setting'),
+      '#required' => FALSE,
+      '#description' => t('A dummy form element to simulate field setting.'),
+    );
+
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function instanceSettingsForm(array $form, array &$form_state) {
+    $form['test_instance_setting'] = array(
+      '#type' => 'textfield',
+      '#title' => t('Field test field instance setting'),
+      '#default_value' => $this->getFieldSetting('test_instance_setting'),
+      '#required' => FALSE,
+      '#description' => t('A dummy form element to simulate field instance setting.'),
+    );
+
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function prepareCache() {
+    if ($this->getFieldSetting('test_hook_field_load')) {
+      $this->additional_key = 'additional_value';
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function delete() {
+    // In D7 it was hook_field_delete().
+    field_test_memorize('field_test_field_delete', array($this->getEntity()));
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getConstraints() {
+    $constraint_manager = \Drupal::typedData()->getValidationConstraintManager();
+    $constraints = parent::getConstraints();
+
+    $constraints[] = $constraint_manager->create('ComplexData', array(
+      'value' => array(
+        'TestField' => array(
+          'compare' => -1,
+          'name' => $this->getFieldDefinition()->getFieldLabel(),
+        )
+      ),
+    ));
+
+    return $constraints;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function isEmpty() {
+    $value = $this->get('value')->getValue();
+    return empty($value);
+  }
+
+}
diff --git a/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/Plugin/field/widget/TestFieldWidgetMultiple.php b/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/Plugin/field/widget/TestFieldWidgetMultiple.php
index 86d7dde..eca862f 100644
--- a/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/Plugin/field/widget/TestFieldWidgetMultiple.php
+++ b/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/Plugin/field/widget/TestFieldWidgetMultiple.php
@@ -66,7 +66,7 @@ public function formElement(FieldInterface $items, $delta, array $element, $lang
     $element += array(
       '#type' => 'textfield',
       '#default_value' => implode(', ', $values),
-      '#element_validate' => array('field_test_widget_multiple_validate'),
+      '#element_validate' => array(array(get_class($this), 'multipleValidate')),
     );
     return $element;
   }
@@ -78,4 +78,16 @@ public function errorElement(array $element, ConstraintViolationInterface $error
     return $element;
   }
 
+  /**
+   * Element validation helper.
+   */
+  public static function multipleValidate($element, &$form_state) {
+    $values = array_map('trim', explode(',', $element['#value']));
+    $items = array();
+    foreach ($values as $value) {
+      $items[] = array('value' => $value);
+    }
+    form_set_value($element, $items, $form_state);
+  }
+
 }
diff --git a/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/Type/ShapeItem.php b/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/Type/ShapeItem.php
deleted file mode 100644
index 4c0d4c9..0000000
--- a/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/Type/ShapeItem.php
+++ /dev/null
@@ -1,44 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\field_test\Type\ShapeItem.
- */
-
-namespace Drupal\field_test\Type;
-
-use Drupal\field\Plugin\field\field_type\LegacyConfigFieldItem;
-
-/**
- * Defines the 'shape_field' entity field item.
- */
-class ShapeItem extends LegacyConfigFieldItem {
-
-  /**
-   * Property definitions of the contained properties.
-   *
-   * @see ShapeItem::getPropertyDefinitions()
-   *
-   * @var array
-   */
-  static $propertyDefinitions;
-
-  /**
-   * Implements \Drupal\Core\TypedData\ComplexDataInterface::getPropertyDefinitions().
-   */
-  public function getPropertyDefinitions() {
-
-    if (!isset(static::$propertyDefinitions)) {
-      static::$propertyDefinitions['shape'] = array(
-        'type' => 'string',
-        'label' => t('Shape'),
-      );
-      static::$propertyDefinitions['color'] = array(
-        'type' => 'string',
-        'label' => t('Color'),
-      );
-    }
-    return static::$propertyDefinitions;
-  }
-
-}
diff --git a/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/Type/TestItem.php b/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/Type/TestItem.php
deleted file mode 100644
index 20c34e3..0000000
--- a/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/Type/TestItem.php
+++ /dev/null
@@ -1,40 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\field_test\Type\TestItem.
- */
-
-namespace Drupal\field_test\Type;
-
-use Drupal\field\Plugin\field\field_type\LegacyConfigFieldItem;
-
-/**
- * Defines the 'test_field' entity field item.
- */
-class TestItem extends LegacyConfigFieldItem {
-
-  /**
-   * Property definitions of the contained properties.
-   *
-   * @see TestItem::getPropertyDefinitions()
-   *
-   * @var array
-   */
-  static $propertyDefinitions;
-
-  /**
-   * Implements \Drupal\Core\TypedData\ComplexDataInterface::getPropertyDefinitions().
-   */
-  public function getPropertyDefinitions() {
-
-    if (!isset(static::$propertyDefinitions)) {
-      static::$propertyDefinitions['value'] = array(
-        'type' => 'integer',
-        'label' => t('Test integer value'),
-      );
-    }
-    return static::$propertyDefinitions;
-  }
-
-}
