diff --git a/src/GroupMembership.php b/src/GroupMembership.php
index cbb6493..de560ba 100644
--- a/src/GroupMembership.php
+++ b/src/GroupMembership.php
@@ -78,6 +78,23 @@ class GroupMembership implements CacheableDependencyInterface {
     return $group_role_storage->loadByUserAndGroup($this->getUser(), $this->getGroup());
   }

+  /**
+   * Gets the group type roles..
+   *
+   * @return \Drupal\group\Entity\GroupRoleInterface[]
+   *   An array of group role objects.
+   */
+  protected function getGroupTypeRoles() {
+    $properties = [
+      'group_type' => $this->groupContent->getGroup()->bundle(),
+      'internal' => FALSE,
+    ];
+
+    return \Drupal::entityTypeManager()
+      ->getStorage('group_role')
+      ->loadByProperties($properties);
+  }
+
   /**
    * Checks whether the member has a permission.
    *
@@ -121,4 +138,69 @@ class GroupMembership implements CacheableDependencyInterface {
     return \Drupal::service('group_permission.checker');
   }

+  /**
+   * Add a role to a member.
+   *
+   * @param string $role
+   *   The role id for the role to add.
+   */
+  public function addRole($role) {
+    // Check that the role exists for a given group type.
+    $group_type_roles = $this->getGroupTypeRoles();
+    $role_exists = FALSE;
+
+    foreach (array_keys($group_type_roles) as $group_type_role) {
+      if ($group_type_role === $role) {
+        $role_exists = TRUE;
+        break;
+      }
+    }
+
+    if ($role_exists) {
+      // Check if a member already has the role.
+      foreach ($this->groupContent->group_roles as $key => $role_item) {
+        if ($role_item->target_id === $role) {
+          return;
+        }
+      }
+
+      $this->groupContent->group_roles[] = $role;
+      $this->groupContent->save();
+    }
+  }
+
+  /**
+   * Remove a role from a member.
+   *
+   * @param string $role
+   *   The role id for the role to remove.
+   */
+  public function removeRole($role) {
+    $roles = $this->groupContent->group_roles;
+    foreach ($roles as $key => $role_item) {
+      if ($role_item->target_id == $role) {
+        $roles->removeItem($key);
+      }
+    }
+
+    $this->groupContent->group_roles = $roles;
+    $this->groupContent->save();
+  }
+
+  /**
+   * Check if a member has a role.
+   *
+   * @param string $role
+   *   The role id to be checked.
+   *
+   * @return bool
+   *  True if the member has role.
+   */
+  public function hasRole($role) {
+    $roles_values = $this->groupContent->group_roles->getValue();
+    $roles = array_column($roles_values, 'target_id');
+
+    return in_array($role, $roles);
+ }
+
 }
diff --git a/tests/src/Kernel/ChainGroupPermissionCalculatorTest.php b/tests/src/Kernel/ChainGroupPermissionCalculatorTest.php
index 87fd2aa..ee66b15 100644
--- a/tests/src/Kernel/ChainGroupPermissionCalculatorTest.php
+++ b/tests/src/Kernel/ChainGroupPermissionCalculatorTest.php
@@ -192,10 +192,7 @@ class ChainGroupPermissionCalculatorTest extends GroupKernelTestBase {
     $this->assertSame(-1, $calculated_permissions->getCacheMaxAge(), 'Member permissions have the right max cache age after joining a group.');
     $this->assertSame($cache_tags, $calculated_permissions->getCacheTags(), 'Member permissions have the right cache tags after joining a group.');

-    // @todo This displays a desperate need for addRole() and removeRole().
-    $membership = $member->getGroupContent();
-    $membership->group_roles[] = 'default-custom';
-    $membership->save();
+    $member->addRole('default-custom');
     $permissions[$group->id()][] = 'join group';
     $cache_tags[] = 'config:group.role.default-custom';
     sort($cache_tags);
diff --git a/tests/src/Kernel/GroupMembershipTest.php b/tests/src/Kernel/GroupMembershipTest.php
new file mode 100644
index 0000000..78c42c6
--- /dev/null
+++ b/tests/src/Kernel/GroupMembershipTest.php
@@ -0,0 +1,94 @@
+<?php
+
+namespace Drupal\Tests\group\Kernel;
+
+/**
+ * Tests the general behavior of group membership.
+ *
+ * @coversDefaultClass \Drupal\group\GroupMembership
+ * @group group
+ */
+class GroupMembershipTest extends GroupKernelTestBase {
+
+  /**
+   * The group we will use to test methods on.
+   *
+   * @var \Drupal\group\Entity\Group
+   */
+  protected $group;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+    $this->group = $this->createGroup();
+  }
+
+  /**
+   * Tests the addition of a role to a member of a group.
+   *
+   * @covers ::addRole
+   */
+  public function testAddRole() {
+    $account = $this->createUser();
+    $this->group->addMember($account);
+    $member = $this->group->getMember($account);
+    $this->assertArrayNotHasKey('default-custom', $member->getRoles());
+
+    // Verify adding a non-existing role on a member that didn't have that role
+    // previously, doesn't add the role.
+    $user_role = 'default-non-existing-role';
+    $member->addRole($user_role);
+    $this->assertArrayNotHasKey($user_role, $member->getRoles());
+
+    // Verify adding an existing role on a member that has that role already,
+    // doesn't set the role twice.
+    $user_role = 'default-member';
+    $member->addRole($user_role);
+    $this->assertArrayHasKey($user_role, $member->getRoles());
+
+    // Verify adding an existing role on a member that didn't have that role
+    // previously, adds the role.
+    $user_role = 'default-custom';
+    $member->addRole($user_role);
+    $this->assertArrayHasKey($user_role, $member->getRoles());
+  }
+
+  /**
+   * Tests the removing of a role on a member of a group.
+   *
+   * @covers ::removeRole
+   */
+  public function testRemoveRole() {
+    $account = $this->createUser();
+    $this->group->addMember($account);
+    $member = $this->group->getMember($account);
+    $role_name = 'default-custom';
+    $this->assertArrayNotHasKey($role_name, $member->getRoles());
+
+    // First add a role we can remove afterwards.
+    $member->addRole($role_name);
+    $this->assertArrayHasKey($role_name, $member->getRoles());
+
+    // Now remove that role again.
+    $member->removeRole($role_name);
+    $this->assertArrayNotHasKey($role_name, $member->getRoles());
+  }
+
+  /**
+   * Tests a user has a role.
+   *
+   * @covers ::hasRole
+   */
+  public function testHasRole() {
+    $account = $this->createUser();
+    $role_name = 'default-custom';
+    $this->group->addMember($account, ['group_roles' => $role_name]);
+    $member = $this->group->getMember($account);
+
+    // Validate the user has role.
+    $this->assertTrue($member->hasRole($role_name));
+  }
+
+}
diff --git a/tests/src/Kernel/GroupRoleStorageTest.php b/tests/src/Kernel/GroupRoleStorageTest.php
index 786c921..5cd5957 100644
--- a/tests/src/Kernel/GroupRoleStorageTest.php
+++ b/tests/src/Kernel/GroupRoleStorageTest.php
@@ -82,10 +82,9 @@ class GroupRoleStorageTest extends GroupKernelTestBase {
       'weight' => 0,
       'group_type' => 'default',
     ])->save();
-    // @todo This displays a desperate need for addRole() and removeRole().
-    $membership = $this->group->getMember($this->account)->getGroupContent();
-    $membership->group_roles[] = 'default-editor';
-    $membership->save();
+
+    $membership = $this->group->getMember($this->account);
+    $membership->addRole('default-editor');
     $this->compareMemberRoles(['default-editor'], FALSE, 'User has the editor group role.');
     $this->compareMemberRoles(['default-editor', 'default-member'], TRUE, 'User also has implicit member role.');
   }
