diff --git a/core/modules/simpletest/src/WebTestBase.php b/core/modules/simpletest/src/WebTestBase.php
index 98f68fc..e685180 100644
--- a/core/modules/simpletest/src/WebTestBase.php
+++ b/core/modules/simpletest/src/WebTestBase.php
@@ -27,6 +27,7 @@
 use Drupal\Core\StreamWrapper\PublicStream;
 use Drupal\Core\Datetime\DrupalDateTime;
 use Drupal\block\Entity\Block;
+use Drupal\user\Plugin\Validation\Constraint\UserNameConstraintValidator;
 use Symfony\Component\HttpFoundation\Request;
 use Drupal\user\Entity\Role;
 
@@ -527,8 +528,14 @@ protected function drupalCreateUser(array $permissions = array(), $name = NULL)
 
     // Create a user assigned to that role.
     $edit = array();
-    $edit['name']   = !empty($name) ? $name : $this->randomMachineName();
+    $edit['name']   = !empty($name) ? $name : $this->getRandomGenerator()->string(8, TRUE, array($this, 'randomUsernameValidate'));
     $edit['mail']   = $edit['name'] . '@example.com';
+    // It is possible that name + @example.com is not a valid email address
+    // since user names can contain whitespace and @ characters and start with a
+    // dot.
+    if (!valid_email_address($edit['mail'])) {
+      $edit['mail'] = $this->getRandomGenerator()->name() . '@example.com';
+    }
     $edit['pass']   = user_password();
     $edit['status'] = 1;
     if ($rid) {
@@ -2560,6 +2567,10 @@ protected function assertNoResponse($code, $message = '', $group = 'Browser') {
    *   TRUE on pass, FALSE on fail.
    */
   protected function assertMail($name, $value = '', $message = '', $group = 'Email') {
+    // The mail subject is mime encoded.
+    if ($name == 'subject') {
+      $value = mime_header_encode($value);
+    }
     $captured_emails = \Drupal::state()->get('system.test_mail_collector') ?: array();
     $email = end($captured_emails);
     return $this->assertTrue($email && isset($email[$name]) && $email[$name] == $value, $message, $group);
@@ -2701,4 +2712,19 @@ protected function prepareRequestForGenerator($clean_urls = TRUE, $override_serv
 
     return $request;
   }
+
+  /**
+   * Callback for random username validation.
+   *
+   * @see \Drupal\Component\Utility\Random::string()
+   *
+   * @param string $string
+   *   The random string to validate.
+   *
+   * @return bool
+   *   TRUE if the random string is valid, FALSE if not.
+   */
+  public function randomUsernameValidate($string) {
+    return $this->randomStringValidate($string) && !UserNameConstraintValidator::hasIllegalCharacters($string);
+  }
 }
diff --git a/core/modules/system/src/Tests/Path/UrlAlterFunctionalTest.php b/core/modules/system/src/Tests/Path/UrlAlterFunctionalTest.php
index 7f64ddb..baca1da 100644
--- a/core/modules/system/src/Tests/Path/UrlAlterFunctionalTest.php
+++ b/core/modules/system/src/Tests/Path/UrlAlterFunctionalTest.php
@@ -36,7 +36,7 @@ function testUrlAlter() {
     // Test a single altered path.
     $this->drupalGet("user/$name");
     $this->assertResponse('200', 'The user/username path gets resolved correctly');
-    $this->assertUrlOutboundAlter("user/$uid", "user/$name");
+    $this->assertUrlOutboundAlter("user/$uid", "user/" . urlencode($name));
 
     // Test that a path always uses its alias.
     $path = array('source' => "user/$uid/test1", 'alias' => 'alias/test1');
diff --git a/core/modules/user/src/Plugin/Validation/Constraint/UserNameConstraintValidator.php b/core/modules/user/src/Plugin/Validation/Constraint/UserNameConstraintValidator.php
index 5b788ca..38f0bbf 100644
--- a/core/modules/user/src/Plugin/Validation/Constraint/UserNameConstraintValidator.php
+++ b/core/modules/user/src/Plugin/Validation/Constraint/UserNameConstraintValidator.php
@@ -33,23 +33,36 @@ public function validate($items, Constraint $constraint) {
     if (strpos($name, '  ') !== FALSE) {
       $this->context->addViolation($constraint->multipleSpacesMessage);
     }
-    if (preg_match('/[^\x{80}-\x{F7} a-z0-9@_.\'-]/i', $name)
-      || preg_match(
-        '/[\x{80}-\x{A0}' .       // Non-printable ISO-8859-1 + NBSP
-        '\x{AD}' .                // Soft-hyphen
-        '\x{2000}-\x{200F}' .     // Various space characters
-        '\x{2028}-\x{202F}' .     // Bidirectional text overrides
-        '\x{205F}-\x{206F}' .     // Various text hinting characters
-        '\x{FEFF}' .              // Byte order mark
-        '\x{FF01}-\x{FF60}' .     // Full-width latin
-        '\x{FFF9}-\x{FFFD}' .     // Replacement characters
-        '\x{0}-\x{1F}]/u',        // NULL byte and control characters
-        $name)
-    ) {
+    if (static::hasIllegalCharacters($name)) {
       $this->context->addViolation($constraint->illegalMessage);
     }
     if (drupal_strlen($name) > USERNAME_MAX_LENGTH) {
       $this->context->addViolation($constraint->tooLongMessage, array('%name' => $name, '%max' => USERNAME_MAX_LENGTH));
     }
   }
+
+  /**
+   * Checks if the username has illegal characters.
+   *
+   * @param string $name
+   *   The username to check.
+   *
+   * @return bool
+   *   TRUE if the username has illegal characters, FALSE if not.
+   */
+  public static function hasIllegalCharacters($name) {
+    return preg_match('/[^\x{80}-\x{F7} a-z0-9@+_.\'-]/i', $name)
+    || preg_match(
+      '/[\x{80}-\x{A0}' .       // Non-printable ISO-8859-1 + NBSP
+      '\x{AD}' .                // Soft-hyphen
+      '\x{2000}-\x{200F}' .     // Various space characters
+      '\x{2028}-\x{202F}' .     // Bidirectional text overrides
+      '\x{205F}-\x{206F}' .     // Various text hinting characters
+      '\x{FEFF}' .              // Byte order mark
+      '\x{FF01}-\x{FF60}' .     // Full-width latin
+      '\x{FFF9}-\x{FFFD}' .     // Replacement characters
+      '\x{0}-\x{1F}]/u',        // NULL byte and control characters
+      $name);
+  }
+
 }
diff --git a/core/modules/user/src/Tests/UserValidationTest.php b/core/modules/user/src/Tests/UserValidationTest.php
index 81a9e48..9d68578 100644
--- a/core/modules/user/src/Tests/UserValidationTest.php
+++ b/core/modules/user/src/Tests/UserValidationTest.php
@@ -53,6 +53,7 @@ function testUsernames() {
       'foo@example.com'        => array('Valid username', 'assertNull'),
       'foo@-example.com'       => array('Valid username', 'assertNull'), // invalid domains are allowed in usernames
       'þòøÇßªř€'               => array('Valid username', 'assertNull'),
+      'foo+bar'                => array('Valid username', 'assertNull'), // '+' symbol is allowed
       'ᚠᛇᚻ᛫ᛒᛦᚦ'                => array('Valid UTF8 username', 'assertNull'), // runes
       ' foo'                   => array('Invalid username that starts with a space', 'assertNotNull'),
       'foo '                   => array('Invalid username that ends with a space', 'assertNotNull'),
