diff --git a/src/Tests/FlagEnableDisableTest.php b/src/Tests/FlagEnableDisableTest.php
index cc90a27..5b46a90 100644
--- a/src/Tests/FlagEnableDisableTest.php
+++ b/src/Tests/FlagEnableDisableTest.php
@@ -6,6 +6,7 @@
 
 namespace Drupal\flag\Tests;
 
+use Drupal\flag\Entity\Flag;
 use Drupal\simpletest\WebTestBase;
 use Drupal\user\RoleInterface;
 use Drupal\user\Entity\Role;
@@ -15,16 +16,7 @@ use Drupal\user\Entity\Role;
  *
  * @group flag
  */
-class FlagEnableDisableTest extends WebTestBase {
-
-  /**
-   * Set to TRUE to strict check all configuration saved.
-   *
-   * @see \Drupal\Core\Config\Testing\ConfigSchemaChecker
-   *
-   * @var bool
-   */
-  protected $strictConfigSchema = FALSE;
+class FlagEnableDisableTest extends FlagTestBase {
 
   /**
    * The label of the flag to create for the test.
@@ -67,11 +59,12 @@ class FlagEnableDisableTest extends WebTestBase {
   protected $adminUser;
 
   /**
-   * Modules to enable.
+   * The flag.
    *
-   * @var array
+   * @var \Drupal\flag\Flaginterface
    */
-  public static $modules = array('views', 'flag', 'node', 'field_ui');
+  protected $flag;
+
 
   /**
    * Test the enabling and disabling of a flag from the Admin UI.
@@ -99,12 +92,6 @@ class FlagEnableDisableTest extends WebTestBase {
     // Create content type.
     $this->drupalCreateContentType(['type' => $this->nodeType]);
 
-    // Test with minimal value requirement.
-    $edit = [
-      'flag_entity_type' => 'flagtype_node',
-    ];
-    $this->drupalPostForm('admin/structure/flags/add', $edit, t('Continue'));
-
     $edit = [
       'label' => $this->label,
       'id' => $this->id,
@@ -112,7 +99,7 @@ class FlagEnableDisableTest extends WebTestBase {
       'flag_short' => $this->flagShortText,
       'unflag_short' => $this->unflagShortText,
     ];
-    $this->drupalPostForm(NULL, $edit, t('Create Flag'));
+    $this->flag = $this->createFlagForm('node', $edit);
   }
 
   /**
@@ -124,10 +111,7 @@ class FlagEnableDisableTest extends WebTestBase {
 
     // Grant the flag permissions to the authenticated role, so that both
     // users have the same roles and share the render cache.
-    $role = Role::load(RoleInterface::AUTHENTICATED_ID);
-    $role->grantPermission('flag ' . $this->id);
-    $role->grantPermission('unflag ' . $this->id);
-    $role->save();
+    $this->grantFlagPermissions($this->flag);
 
     // Click the flag link.
     $this->drupalGet('node/' . $this->node_id);
diff --git a/src/Tests/FlagTestBase.php b/src/Tests/FlagTestBase.php
new file mode 100644
index 0000000..275272e
--- /dev/null
+++ b/src/Tests/FlagTestBase.php
@@ -0,0 +1,244 @@
+<?php
+/**
+ * @file
+ * Contains \Drupal\flag\Tests\FlagTestBase.
+ */
+
+namespace Drupal\flag\Tests;
+
+use Drupal\Core\StringTranslation\StringTranslationTrait;
+use Drupal\flag\FlagInterface;
+use Drupal\simpletest\WebTestBase;
+use Drupal\flag\Entity\Flag;
+use Drupal\user\RoleInterface;
+use Drupal\user\Entity\Role;
+
+/**
+ * Provides common methods for Flag tests.
+ */
+abstract class FlagTestBase extends WebTestBase {
+
+  use StringTranslationTrait;
+
+  /**
+   * Set to TRUE to strict check all configuration saved.
+   *
+   * @see \Drupal\Core\Config\Testing\ConfigSchemaChecker
+   *
+   * @var bool
+   */
+  protected $strictConfigSchema = FALSE;
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = array(
+    'views',
+    'node',
+    'user',
+    'flag',
+    'node',
+    'field_ui',
+    'text',
+    'block',
+  );
+
+  /**
+   * Create a basic flag programmatically.
+   *
+   * Creates a flag with the given entity type, subtype, and link type without
+   * using the admin UI. The flag's ID, label, flag and unflag text will be
+   * random strings.
+   *
+   * @param string|null $entity_type
+   *   (optional) The entity type of the flag to create. If NULL, assumes 'node'.
+   * @param array $subtypes
+   *   (optional) An array of entity subtypes to which the flag applies. If NULL,
+   *   all subtypes are assumed.
+   * @param string|null $link_type
+   *   (optional) The ID of the link type to use. If NULL, assumes 'reload'.
+   *
+   * @return \Drupal\flag\FlagInterface
+   *   A new flag entity with the given criteria.
+   */
+  protected function createFlag($entity_type = NULL, $subtypes = [], $link_type = NULL) {
+
+    // If we didn't get an entity type, assume 'node'.
+    if (empty($entity_type)) {
+      $entity_type = 'node';
+    }
+
+    // If we didn't get a subtype, assume all subtypes for the entity.
+    if (empty($subtypes)) {
+      $subtypes = $this->getBundleIds($entity_type);
+    }
+
+    // If we didn't get a link type, assume 'reload'.
+    if (empty($link_type)) {
+      $link_type = 'reload';
+    }
+
+    // Create the flag programmatically.
+    $flag = Flag::create([
+      'id' => strtolower($this->randomMachineName()),
+      'label' => $this->randomString(),
+      'entity_type' => $entity_type,
+      'types' => $subtypes,
+      'flag_short' => $this->randomString(16),
+      'flag_long' => $this->randomString(16),
+      'flag_type' => $this->getFlagType($entity_type),
+      'link_type' => $link_type,
+      'flagTypeConfig' => [],
+      'linkTypeConfig' => [],
+    ]);
+
+    // Save the flag.
+    $flag->save();
+
+    // Make sure that we actually did get a flag entity.
+    $this->assertTrue($flag instanceof Flag);
+
+    return $flag;
+  }
+
+  /**
+   * Creates a flag entity using the admin UI.
+   *
+   * If you do not provide any bundles in $edit, all bundles for $entity_type
+   * is assumed.
+   *
+   * @param string|null $entity_type
+   *   (optional) A string containing the flaggable entity type.
+   * @param array $edit
+   *   (optional) An array of form field names and values. If omitted, random
+   *   strings will be used for the flag ID, label, short and long text.
+   * @param string|null $link_type
+   *   (optional) A string containing the link type ID. Is omitted, assumes
+   *   'reload'.
+   *
+   * @return null|\Drupal\flag\FlagInterface
+   *   The created flag entity.
+   */
+  protected function createFlagForm($entity_type = 'node', $edit = [], $link_type = 'reload') {
+    // Submit the flag add page.
+    $this->drupalPostForm('admin/structure/flags/add', [
+      'flag_entity_type' => $this->getFlagType($entity_type),
+    ], $this->t('Continue'));
+
+    // Set the link type.
+    $this->drupalPostAjaxForm(NULL, ['link_type' => $link_type], 'link_type');
+
+    // Create an array of defaults.
+    $default_edit = [
+      'id' => strtolower($this->randomMachineName()),
+      'label' => $this->randomString(),
+      'flag_short' => $this->randomString(16),
+      'flag_long' => $this->randomString(16),
+    ];
+
+    // Merge the default values with the edit array.
+    $final_edit = array_merge($default_edit, $edit);
+
+    // Check if any of the bundles have been set.
+    $bundles = $this->getBundleIds($entity_type);
+    $has_specified_bundle = FALSE;
+    foreach ($bundles as $bundle_id) {
+      if (!empty($final_edit['types[' . $bundle_id . ']'])) {
+        $has_specified_bundle = TRUE;
+        break;
+      }
+    }
+
+    // If not, assume all by default.
+    if (!$has_specified_bundle) {
+      foreach ($bundles as $bundle_id) {
+        $final_edit['types[' . $bundle_id . ']'] = $bundle_id;
+      }
+    }
+
+    // Submit the flag details form.
+    $this->drupalPostForm(NULL, $final_edit, $this->t('Create Flag'));
+
+    // Load the new flag we created.
+    $flag = Flag::load($final_edit['id']);
+
+    // Make sure that we actually did get a flag entity.
+    $this->assertTrue($flag instanceof Flag);
+
+    // Return the flag.
+    return $flag;
+  }
+
+  /**
+   * Grants flag and unflag permission to the given flag.
+   *
+   * @param \Drupal\flag\FlagInterface $flag
+   *   The flag on which to grant permissions.
+   * @param array|string $role_id
+   *   (optional) The ID of the role to grant permissions. If omitted, the
+   *   authenticated role is assumed.
+   * @param bool $can_flag
+   *   (optional) TRUE to grant the role flagging permission, FALSE to not grant
+   *   flagging permission to the role. If omitted, TRUE is assumed.
+   * @param bool $can_unflag
+   *   Optional TRUE to grant the role unflagging permission, FALSE to not grant
+   *   unflagging permission to the role. If omitted, TRUE is assumed.
+   */
+  protected function grantFlagPermissions(FlagInterface $flag,
+                                      $role_id = RoleInterface::AUTHENTICATED_ID,
+                                      $can_flag = TRUE, $can_unflag = TRUE) {
+
+    // Grant the flag permissions to the authenticated role, so that both
+    // users have the same roles and share the render cache.
+    $role = Role::load($role_id);
+    if ($can_flag) {
+      $role->grantPermission('flag ' . $flag->id());
+    }
+
+    if ($can_unflag) {
+      $role->grantPermission('unflag ' . $flag->id());
+    }
+
+    $role->save();
+  }
+
+  /**
+   * Get a flag type plugin ID for the given entity.
+   *
+   * @param string $entity_type
+   *   The entity type of the flag type plugin to get.
+   *
+   * @return string
+   *   A string containing the flag type ID.
+   */
+  protected function getFlagType($entity_type) {
+    $all_flag_types = \Drupal::service('plugin.manager.flag.flagtype')->getDefinitions();
+
+    // Search and return the flag type ID that matches our entity.
+    foreach ($all_flag_types as $plugin_id => $plugin_def) {
+      if ($plugin_def['entity_type'] == $entity_type) {
+        return $plugin_id;
+      }
+    }
+
+    return 'flagtype_entity';
+  }
+
+  /**
+   * Get the bundle IDs for the given entity type.
+   *
+   * @param string $entity_type
+   *   A string containing the entity type.
+   *
+   * @return array
+   *   An array of bundle IDs.
+   */
+  protected function getBundleIds($entity_type) {
+    $types = entity_get_bundles($entity_type);
+
+    return array_keys($types);
+  }
+
+}
