diff --git a/core/modules/simpletest/src/AssertContentTrait.php b/core/modules/simpletest/src/AssertContentTrait.php
index 2c015ca..87e8887 100644
--- a/core/modules/simpletest/src/AssertContentTrait.php
+++ b/core/modules/simpletest/src/AssertContentTrait.php
@@ -178,6 +178,10 @@ protected function getUrl() {
   protected function buildXPathQuery($xpath, array $args = array()) {
     // Replace placeholders.
     foreach ($args as $placeholder => $value) {
+      // Cast SafeStringInterface objects to string.
+      if (is_object($value)) {
+        $value = (string) $value;
+      }
       // XPath 1.0 doesn't support a way to escape single or double quotes in a
       // string literal. We split double quotes out of the string, and encode
       // them separately.
@@ -281,7 +285,7 @@ protected function getAllOptions(\SimpleXMLElement $element) {
    *
    * An optional link index may be passed.
    *
-   * @param string $label
+   * @param string|\Drupal\Component\Utility\SafeStringInterface $label
    *   Text between the anchor tags.
    * @param int $index
    *   Link position counting from zero.
@@ -299,6 +303,8 @@ protected function getAllOptions(\SimpleXMLElement $element) {
    *   TRUE if the assertion succeeded, FALSE otherwise.
    */
   protected function assertLink($label, $index = 0, $message = '', $group = 'Other') {
+    // Cast SafeStringInterface objects to string.
+    $label = (string) $label;
     $links = $this->xpath('//a[normalize-space(text())=:label]', array(':label' => $label));
     $message = ($message ? $message : strtr('Link with label %label found.', array('%label' => $label)));
     return $this->assert(isset($links[$index]), $message, $group);
@@ -307,7 +313,7 @@ protected function assertLink($label, $index = 0, $message = '', $group = 'Other
   /**
    * Passes if a link with the specified label is not found.
    *
-   * @param string $label
+   * @param string|\Drupal\Component\Utility\SafeStringInterface $label
    *   Text between the anchor tags.
    * @param string $message
    *   (optional) A message to display with the assertion. Do not translate
@@ -324,6 +330,8 @@ protected function assertLink($label, $index = 0, $message = '', $group = 'Other
    *   TRUE if the assertion succeeded, FALSE otherwise.
    */
   protected function assertNoLink($label, $message = '', $group = 'Other') {
+    // Cast SafeStringInterface objects to string.
+    $label = (string) $label;
     $links = $this->xpath('//a[normalize-space(text())=:label]', array(':label' => $label));
     $message = ($message ? $message : SafeMarkup::format('Link with label %label not found.', array('%label' => $label)));
     return $this->assert(empty($links), $message, $group);
@@ -613,7 +621,7 @@ protected function assertTextHelper($text, $message = '', $group = 'Other', $not
    * through a web browser. In other words the HTML has been filtered out of
    * the contents.
    *
-   * @param string $text
+   * @param string|\Drupal\Component\Utility\SafeStringInterface $text
    *   Plain text to look for.
    * @param string $message
    *   (optional) A message to display with the assertion. Do not translate
@@ -640,7 +648,7 @@ protected function assertUniqueText($text, $message = '', $group = 'Other') {
    * through a web browser. In other words the HTML has been filtered out of
    * the contents.
    *
-   * @param string $text
+   * @param string|\Drupal\Component\Utility\SafeStringInterface $text
    *   Plain text to look for.
    * @param string $message
    *   (optional) A message to display with the assertion. Do not translate
@@ -665,7 +673,7 @@ protected function assertNoUniqueText($text, $message = '', $group = 'Other') {
    *
    * It is not recommended to call this function directly.
    *
-   * @param string $text
+   * @param string|\Drupal\Component\Utility\SafeStringInterface $text
    *   Plain text to look for.
    * @param string $message
    *   (optional) A message to display with the assertion. Do not translate
@@ -685,6 +693,8 @@ protected function assertNoUniqueText($text, $message = '', $group = 'Other') {
    *   TRUE on pass, FALSE on fail.
    */
   protected function assertUniqueTextHelper($text, $message = '', $group = 'Other', $be_unique = FALSE) {
+    // Cast SafeStringInterface objects to string.
+    $text = (string) $text;
     if (!$message) {
       $message = '"' . $text . '"' . ($be_unique ? ' found only once' : ' found more than once');
     }
@@ -1104,12 +1114,12 @@ protected function assertNoFieldByName($name, $value = '', $message = '', $group
    *
    * @param string $id
    *   ID of field to assert.
-   * @param string $value
+   * @param string|\Drupal\Component\Utility\SafeStringInterface $value
    *   (optional) Value for the field to assert. You may pass in NULL to skip
    *   checking the value, while still checking that the field exists.
    *   However, the default value ('') asserts that the field value is an empty
    *   string.
-   * @param string $message
+   * @param string|\Drupal\Component\Utility\SafeStringInterface $message
    *   (optional) A message to display with the assertion. Do not translate
    *   messages: use \Drupal\Component\Utility\SafeMarkup::format() to embed
    *   variables in the message text, not t(). If left blank, a default message
@@ -1124,6 +1134,11 @@ protected function assertNoFieldByName($name, $value = '', $message = '', $group
    *   TRUE on pass, FALSE on fail.
    */
   protected function assertFieldById($id, $value = '', $message = '', $group = 'Browser') {
+    // Cast SafeStringInterface objects to string.
+    if (isset($value)) {
+      $value = (string) $value;
+    }
+    $message = (string) $message;
     return $this->assertFieldByXPath($this->constructFieldXpath('id', $id), $value, $message ? $message : SafeMarkup::format('Found field by id @id', array('@id' => $id)), $group);
   }
 
diff --git a/core/modules/simpletest/src/TestBase.php b/core/modules/simpletest/src/TestBase.php
index 1e59712..556ae9f 100644
--- a/core/modules/simpletest/src/TestBase.php
+++ b/core/modules/simpletest/src/TestBase.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\simpletest;
 
+use Drupal\Component\Utility\SafeStringInterface;
 use Drupal\Component\Utility\Crypt;
 use Drupal\Component\Utility\Random;
 use Drupal\Component\Utility\SafeMarkup;
@@ -359,7 +360,7 @@ protected function storeAssertion(array $assertion) {
    * @param $status
    *   Can be 'pass', 'fail', 'exception', 'debug'.
    *   TRUE is a synonym for 'pass', FALSE for 'fail'.
-   * @param $message
+   * @param string|\Drupal\Component\Utility\SafeStringInterface $message
    *   (optional) A message to display with the assertion. Do not translate
    *   messages: use \Drupal\Component\Utility\SafeMarkup::format() to embed
    *   variables in the message text, not t(). If left blank, a default message
@@ -377,6 +378,9 @@ protected function storeAssertion(array $assertion) {
    *   is the caller function itself.
    */
   protected function assert($status, $message = '', $group = 'Other', array $caller = NULL) {
+    if ($message instanceof SafeStringInterface) {
+      $message = (string) $message;
+    }
     // Convert boolean status to string status.
     if (is_bool($status)) {
       $status = $status ? 'pass' : 'fail';
@@ -654,6 +658,10 @@ protected function assertNotNull($value, $message = '', $group = 'Other') {
    *   TRUE if the assertion succeeded, FALSE otherwise.
    */
   protected function assertEqual($first, $second, $message = '', $group = 'Other') {
+    // We cast objects implementing SafeStringInterface to string ourself so as
+    // to not rely on PHP casting them to string depending on what we're
+    // comparing with.
+    $this->castSafeStrings($first, $second);
     return $this->assert($first == $second, $message ? $message : SafeMarkup::format('Value @first is equal to value @second.', array('@first' => var_export($first, TRUE), '@second' => var_export($second, TRUE))), $group);
   }
 
@@ -683,8 +691,45 @@ protected function assertNotEqual($first, $second, $message = '', $group = 'Othe
   }
 
   /**
+   * Casts SafeStringInterface objects into string in 2 compared values.
+   *
+   * @param string|array &$first
+   *   The first value to act on.
+   * @param string|array &$second
+   *   The second value to act on (optional).
+   *
+   * @return list
+   *   The input values, with SafeStringInterface objects casted to string.
+   */
+  protected function castSafeStrings(&$first, &$second = NULL) {
+    if ($first instanceof SafeStringInterface) {
+      $first = (string) $first;
+    }
+    if ($second instanceof SafeStringInterface) {
+      $second = (string) $second;
+    }
+    if (is_array($first)) {
+      array_walk_recursive($first, function (&$first) {
+        if ($first instanceof SafeStringInterface) {
+          $first = (string) $first;
+        }
+      });
+    }
+    if (is_array($second)) {
+      array_walk_recursive($second, function (&$second) {
+        if ($second instanceof SafeStringInterface) {
+          $second = (string) $second;
+        }
+      });
+    }
+  }
+
+  /**
    * Check to see if two values are identical.
    *
+   * Objects implementing SafeStringInterface in the values will be casted to
+   * string before the assertion is performed.
+   *
    * @param $first
    *   The first value to check.
    * @param $second
@@ -704,6 +749,11 @@ protected function assertNotEqual($first, $second, $message = '', $group = 'Othe
    *   TRUE if the assertion succeeded, FALSE otherwise.
    */
   protected function assertIdentical($first, $second, $message = '', $group = 'Other') {
+    // As there are very few use cases where we need a strict equality check
+    // between a string and the same string in a TranslationWrapper to fail,
+    // we convert objects implementing SafeStringInterface to string as a
+    // development aid.
+    $this->castSafeStrings($first, $second);
     return $this->assert($first === $second, $message ? $message : SafeMarkup::format('Value @first is identical to value @second.', array('@first' => var_export($first, TRUE), '@second' => var_export($second, TRUE))), $group);
   }
 
diff --git a/core/modules/simpletest/src/WebTestBase.php b/core/modules/simpletest/src/WebTestBase.php
index 8e149f8..f029887 100644
--- a/core/modules/simpletest/src/WebTestBase.php
+++ b/core/modules/simpletest/src/WebTestBase.php
@@ -233,7 +233,7 @@ function __construct($test_id = NULL) {
   /**
    * Get a node from the database based on its title.
    *
-   * @param $title
+   * @param string|\Drupal\Component\Utility\SafeStringInterface $title
    *   A node title, usually generated by $this->randomMachineName().
    * @param $reset
    *   (optional) Whether to reset the entity cache.
@@ -245,6 +245,8 @@ function drupalGetNodeByTitle($title, $reset = FALSE) {
     if ($reset) {
       \Drupal::entityManager()->getStorage('node')->resetCache();
     }
+    // Cast SafeStringInterface objects to string.
+    $title = (string) $title;
     $nodes = entity_load_multiple_by_properties('node', array('title' => $title));
     // Load the first node returned from the database.
     $returned_node = reset($nodes);
@@ -1690,6 +1692,14 @@ protected function drupalGetXHR($path, array $options = array(), array $headers
    *   (e.g., "&extra_var1=hello+world&extra_var2=you%26me").
    */
   protected function drupalPostForm($path, $edit, $submit, array $options = array(), array $headers = array(), $form_html_id = NULL, $extra_post = NULL) {
+    if (!is_array($submit)) {
+      // Cast SafeStringInterface objects to string.
+      $submit = (string) $submit;
+    }
+    if (is_array($edit)) {
+      array_walk_recursive($edit, [$this, 'castSafeStrings']);
+    }
+
     $submit_matches = FALSE;
     $ajax = is_array($submit);
     if (isset($path)) {
@@ -2407,7 +2417,7 @@ protected function handleForm(&$post, &$edit, &$upload, $submit, $form) {
    *
    * If the link is discovered and clicked, the test passes. Fail otherwise.
    *
-   * @param string $label
+   * @param string|\Drupal\Component\Utility\SafeStringInterface $label
    *   Text between the anchor tags.
    * @param int $index
    *   Link position counting from zero.
@@ -2425,7 +2435,7 @@ protected function clickLink($label, $index = 0) {
    *
    * If the link is discovered and clicked, the test passes. Fail otherwise.
    *
-   * @param string $label
+   * @param string|\Drupal\Component\Utility\SafeStringInterface $label
    *   Text between the anchor tags, uses starts-with().
    * @param int $index
    *   Link position counting from zero.
@@ -2442,7 +2452,7 @@ protected function clickLinkPartialName($label, $index = 0) {
   /**
    * Provides a helper for ::clickLink() and ::clickLinkPartialName().
    *
-   * @param string $label
+   * @param string|\Drupal\Component\Utility\SafeStringInterface $label
    *   Text between the anchor tags, uses starts-with().
    * @param int $index
    *   Link position counting from zero.
@@ -2453,6 +2463,8 @@ protected function clickLinkPartialName($label, $index = 0) {
    *   Page contents on success, or FALSE on failure.
    */
   protected function clickLinkHelper($label, $index, $pattern) {
+    // Cast SafeStringInterface objects to string.
+    $label = (string) $label;
     $url_before = $this->getUrl();
     $urls = $this->xpath($pattern, array(':label' => $label));
     if (isset($urls[$index])) {
@@ -2803,6 +2815,8 @@ protected function assertMail($name, $value = '', $message = '', $group = 'Email
   protected function assertMailString($field_name, $string, $email_depth, $message = '', $group = 'Other') {
     $mails = $this->drupalGetMails();
     $string_found = FALSE;
+    // Cast SafeStringInterface objects to string.
+    $string = (string) $string;
     for ($i = count($mails) -1; $i >= count($mails) - $email_depth && $i >= 0; $i--) {
       $mail = $mails[$i];
       // Normalize whitespace, as we don't know what the mail system might have
