diff --git a/src/Tests/GALoginTestBase.php b/src/Tests/GALoginTestBase.php
index 30112f1..8b77b20 100644
--- a/src/Tests/GALoginTestBase.php
+++ b/src/Tests/GALoginTestBase.php
@@ -7,11 +7,9 @@ use Otp\GoogleAuthenticator;
 use Otp\Otp;
 
 /**
- * Class GALoginTestBase.
- *
- * @package Drupal\ga_login\Tests
+ * Defines a base class for tests.
  */
-class GALoginTestBase extends WebTestBase {
+abstract class GALoginTestBase extends WebTestBase {
   /**
    * Object containing the external validation library.
    *
@@ -87,4 +85,13 @@ class GALoginTestBase extends WebTestBase {
       ->save();
   }
 
+  /**
+   * Get the user's login_hash from the current test url.
+   */
+  public function getLoginHash() {
+    $url_parts = explode('/', explode('?', $this->url)[0]);
+    $login_hash = array_pop($url_parts);
+    return $login_hash;
+  }
+
 }
diff --git a/src/Tests/GALoginValidationTest.php b/src/Tests/GALoginValidationTest.php
index dd98ec9..1f8e395 100644
--- a/src/Tests/GALoginValidationTest.php
+++ b/src/Tests/GALoginValidationTest.php
@@ -59,16 +59,12 @@ class GALoginValidationTest extends GALoginTestBase {
     // Do not use drupalLogin as it does actual login.
     $this->drupalPostForm('user/login', $edit, $this->t('Log in'));
     $this->assertText($this->uiStrings('app-desc'));
-    // Get login hash. Could user tfa_login_hash() but would require reloading
-    // account.
-    $url_parts = explode('/', $this->url);
-    $login_hash = array_pop($url_parts);
 
     // Try invalid code.
     $edit = [
       'code' => 112233,
     ];
-    $this->drupalPostForm('tfa/' . $account->id() . '/' . $login_hash, $edit, $this->t('Verify'));
+    $this->drupalPostForm('tfa/' . $account->id() . '/' . $this->getLoginHash(), $edit, $this->t('Verify'));
     $this->assertText($this->uiStrings('invalid-code-retry'));
 
     // Try valid code.
@@ -78,7 +74,7 @@ class GALoginValidationTest extends GALoginTestBase {
       'code' => $code,
     ];
 
-    $this->drupalPostForm('tfa/' . $account->id() . '/' . $login_hash, $edit, $this->t('Verify'));
+    $this->drupalPostForm('tfa/' . $account->id() . '/' . $this->getLoginHash(), $edit, $this->t('Verify'));
     $this->assertResponse(200);
     $this->assertText($account->getUsername());
 
@@ -91,14 +87,12 @@ class GALoginValidationTest extends GALoginTestBase {
 
     // Do not use drupalLogin as it does actual login.
     $this->drupalPostForm('user/login', $edit, $this->t('Log in'));
-    $url_parts = explode('/', $this->url);
-    $login_hash = array_pop($url_parts);
 
     $edit = [
       'code' => $code,
     ];
 
-    $this->drupalPostForm('tfa/' . $account->id() . '/' . $login_hash, $edit, $this->t('Verify'));
+    $this->drupalPostForm('tfa/' . $account->id() . '/' . $this->getLoginHash(), $edit, $this->t('Verify'));
     $this->assertText($this->uiStrings('code-already-used'));
   }
 
@@ -122,19 +116,16 @@ class GALoginValidationTest extends GALoginTestBase {
       'name' => $account->getUsername(),
       'pass' => $account->pass_raw,
     ];
+
     // Do not use drupalLogin as it does actual login.
     $this->drupalPostForm('user/login', $edit, $this->t('Log in'));
     $this->assertText($this->uiStrings('app-desc'));
-    // Get login hash. Could user tfa_login_hash() but would require reloading
-    // account.
-    $url_parts = explode('/', $this->url);
-    $login_hash = array_pop($url_parts);
-    //
+
     // Try invalid code.
     $edit = [
       'code' => 112233,
     ];
-    $this->drupalPostForm('tfa/' . $account->id() . '/' . $login_hash, $edit, $this->t('Verify'));
+    $this->drupalPostForm('tfa/' . $account->id() . '/' . $this->getLoginHash(), $edit, $this->t('Verify'));
     $this->assertText($this->uiStrings('invalid-code-retry'));
 
     // Try valid code.
@@ -144,7 +135,7 @@ class GALoginValidationTest extends GALoginTestBase {
       'code' => $code,
     ];
 
-    $this->drupalPostForm('tfa/' . $account->id() . '/' . $login_hash, $edit, $this->t('Verify'));
+    $this->drupalPostForm('tfa/' . $account->id() . '/' . $this->getLoginHash(), $edit, $this->t('Verify'));
     $this->assertResponse(200);
     $this->assertText($account->getUsername());
 
@@ -157,14 +148,12 @@ class GALoginValidationTest extends GALoginTestBase {
 
     // Do not use drupalLogin as it does actual login.
     $this->drupalPostForm('user/login', $edit, $this->t('Log in'));
-    $url_parts = explode('/', $this->url);
-    $login_hash = array_pop($url_parts);
 
     $edit = [
       'code' => $code,
     ];
 
-    $this->drupalPostForm('tfa/' . $account->id() . '/' . $login_hash, $edit, $this->t('Verify'));
+    $this->drupalPostForm('tfa/' . $account->id() . '/' . $this->getLoginHash(), $edit, $this->t('Verify'));
     $this->assertText($this->uiStrings('code-already-used'));
 
   }
@@ -209,23 +198,19 @@ class GALoginValidationTest extends GALoginTestBase {
     // Do not use drupalLogin as it does actual login.
     $this->drupalPostForm('user/login', $edit, $this->t('Log in'));
     $this->assertText($this->uiStrings('app-desc'));
-    // Get login hash. Could user tfa_login_hash() but would require reloading
-    // account.
-    $url_parts = explode('/', $this->url);
-    $login_hash = array_pop($url_parts);
 
     // Try invalid recovery code.
     $edit = [
       'code' => '111 222 333',
     ];
-    $this->drupalPostForm('tfa/' . $account->id() . '/' . $login_hash, $edit, $this->t('Verify'));
+    $this->drupalPostForm('tfa/' . $account->id() . '/' . $this->getLoginHash(), $edit, $this->t('Verify'));
     $this->assertText($this->uiStrings('invalid-recovery-code'));
 
     // Try valid recovery code.
     $edit = [
       'code' => '222 333 444',
     ];
-    $this->drupalPostForm('tfa/' . $account->id() . '/' . $login_hash, $edit, $this->t('Verify'));
+    $this->drupalPostForm('tfa/' . $account->id() . '/' . $this->getLoginHash(), $edit, $this->t('Verify'));
     $this->assertResponse(200);
     $this->assertText($account->getUsername());
   }
