You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@juneau.apache.org by ja...@apache.org on 2021/07/12 18:59:59 UTC

[juneau] branch master updated: Unit tests.

This is an automated email from the ASF dual-hosted git repository.

jamesbognar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/juneau.git


The following commit(s) were added to refs/heads/master by this push:
     new 20131ef  Unit tests.
20131ef is described below

commit 20131ef56daa2abc9574cec942f7252666dbadb2
Author: JamesBognar <ja...@salesforce.com>
AuthorDate: Mon Jul 12 14:59:36 2021 -0400

    Unit tests.
---
 .../org/apache/juneau/assertions/Assertion.java    |  32 +---
 .../juneau/assertions/AssertionPredicate.java      |  81 +++++++-
 .../juneau/assertions/AssertionPredicates.java     | 213 ++++++++++++++++++---
 .../org/apache/juneau/assertions/Assertions.java   |   7 +-
 .../juneau/assertions/FluentArrayAssertion.java    |  24 ++-
 .../juneau/assertions/FluentBooleanAssertion.java  |  10 +-
 .../assertions/FluentCollectionAssertion.java      |  19 +-
 .../assertions/FluentComparableAssertion.java      |  30 ++-
 .../juneau/assertions/FluentDateAssertion.java     |  22 ++-
 .../juneau/assertions/FluentListAssertion.java     |   7 +-
 .../juneau/assertions/FluentMapAssertion.java      |  19 +-
 .../juneau/assertions/FluentObjectAssertion.java   |  42 ++--
 .../assertions/FluentPrimitiveArrayAssertion.java  |  25 ++-
 .../juneau/assertions/FluentStringAssertion.java   |  87 +++++----
 .../assertions/FluentThrowableAssertion.java       |  67 +++++--
 .../assertions/FluentZonedDateTimeAssertion.java   |  22 ++-
 .../java/org/apache/juneau/assertions/Verify.java  |  12 +-
 .../apache/juneau/assertions/Messages.properties   | 113 +++++++++++
 .../juneau/assertions/ArrayAssertion_Test.java     |  11 +-
 .../assertions/AssertionPredicates_Test.java       | 157 +++++++++++++++
 .../apache/juneau/assertions/Assertions_Test.java  |   1 +
 .../juneau/assertions/BeanAssertion_Test.java      |   1 +
 .../juneau/assertions/BooleanAssertion_Test.java   |   1 +
 .../juneau/assertions/ByteArrayAssertion_Test.java |  15 +-
 .../assertions/CollectionAssertion_Test.java       |   9 +-
 .../assertions/ComparableAssertion_Test.java       |   1 +
 .../juneau/assertions/DateAssertion_Test.java      |   9 +-
 .../juneau/assertions/IntegerAssertion_Test.java   |  49 ++---
 .../juneau/assertions/ListAssertion_Test.java      |   5 +-
 .../juneau/assertions/LongAssertion_Test.java      |  49 ++---
 .../juneau/assertions/MapAssertion_Test.java       |   9 +-
 .../juneau/assertions/ObjectAssertion_Test.java    |  27 +--
 .../juneau/assertions/StringAssertion_Test.java    |  71 +++----
 .../juneau/assertions/ThrowableAssertion_Test.java |  17 +-
 .../org/apache/juneau/assertions/Verify_Test.java  |  16 +-
 .../assertions/ZonedDateTimeAssertion_Test.java    |   9 +-
 .../java/org/apache/juneau/config/ConfigTest.java  |  18 +-
 .../org/apache/juneau/http/EntityTag_Test.java     |   2 +-
 .../apache/juneau/reflection/ClassInfoTest.java    |   2 +-
 39 files changed, 973 insertions(+), 338 deletions(-)

diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/assertions/Assertion.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/assertions/Assertion.java
index 35b052d..2c619d6 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/assertions/Assertion.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/assertions/Assertion.java
@@ -28,6 +28,11 @@ import org.apache.juneau.internal.*;
 @FluentSetters
 public class Assertion {
 
+	private static final Messages MESSAGES = Messages.of(Assertion.class, "Messages");
+	static final String
+		MSG_parameterCannotBeNull = MESSAGES.getString("parameterCannotBeNull"),
+		MSG_causedBy = MESSAGES.getString("causedBy");
+
 	String msg;
 	Object[] msgArgs;
 	PrintStream out = System.err;
@@ -129,7 +134,7 @@ public class Assertion {
 	protected BasicAssertionError error(Throwable cause, String msg, Object...args) {
 		msg = format(msg, args);
 		if (this.msg != null)
-			msg = format(this.msg, this.msgArgs).replace("<<<MSG>>>", msg).replace("<<<CAUSED-BY>>>", cause == null ? "" : "Caused by: " + cause.getMessage());
+			msg = format(this.msg, this.msgArgs).replace("<<<MSG>>>", msg).replace("<<<CAUSED-BY>>>", cause == null ? "" : MSG_causedBy + ": " + cause.getMessage());
 		if (out != null)
 			out.println(msg);
 		if (throwable != null) {
@@ -163,31 +168,6 @@ public class Assertion {
 		return (Class<E[]>)Array.newInstance(c,0).getClass();
 	}
 
-	/**
-	 * Asserts the specified value is not null.
-	 *
-	 * @param value The value to check.
-	 * @param msg The message.
-	 * @param args The message arguments.
-	 * @return The value.
-	 */
-	protected <T> T assertNotNull(T value, String msg, Object...args) {
-		if (value == null)
-			throw new BasicAssertionError(format(msg, args));
-		return value;
-	}
-
-	/**
-	 * Asserts the specified parameter is not null.
-	 *
-	 * @param parameter The parameter name.
-	 * @param value The value to check.
-	 * @return The value.
-	 */
-	protected <T> T assertNotNull(String parameter, T value) {
-		return assertNotNull(value, "Parameter ''{0}'' cannot be null.", parameter);
-	}
-
 	// <FluentSetters>
 
 	// </FluentSetters>
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/assertions/AssertionPredicate.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/assertions/AssertionPredicate.java
index 2678a99..5871a38 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/assertions/AssertionPredicate.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/assertions/AssertionPredicate.java
@@ -14,17 +14,58 @@ package org.apache.juneau.assertions;
 
 import static org.apache.juneau.internal.StringUtils.*;
 
+import java.text.*;
 import java.util.function.*;
 
+import org.apache.juneau.cp.*;
+
 /**
  * Wrapper around a {@link Predicate} that allows for an error message for when the predicate fails.
  *
+ * <p>
+ * Typically used wherever predicates are allowed for testing of {@link Assertion} objects such as...
+ * <ul>
+ * 	<li>{@link FluentObjectAssertion#passes(Predicate)}
+ * 	<li>{@link FluentArrayAssertion#passes(Predicate...)}
+ * 	<li>{@link FluentPrimitiveArrayAssertion#passes(Predicate...)}
+ * 	<li>{@link FluentListAssertion#passes(Predicate...)}
+ * </ul>
+ *
+ * <p>
+ * See {@link AssertionPredicates} for a set of predefined predicates for common use cases.
+ *
+ * <h5 class='section'>Example:</h5>
+ * <p class='bcode w800'>
+ * 	<jc>// Asserts that a bean passes a custom test.</jc>
+ * 	<jc>// AssertionError with specified message is thrown otherwise.</jc>
+ * 	Predicate<> <jv>p</jv> = <jk>new</jk> AssertionPredicate&lt;MyBean&gt;(
+ * 		<jv>x</jv> -&gt; <jv>x</jv>.getFoo().equals(<js>"bar"</js>),
+ * 		<js>"Foo did not equal bar.  Bean was=''{0}''"</js>,
+ * 		<jsf>VALUE</jsf>
+ * 	);
+ * 	<jsm>assertObject<jsm>(<jv>myBean</jv>).passes(<jv>p</jv>);
+ * </p>
+ *
  * @param <T> the type of input being tested.
  */
 public class AssertionPredicate<T> implements Predicate<T> {
 
+	/**
+	 * Argument placeholder for tested value.
+	 */
+	public static final Function<Object,String> VALUE = x -> stringify(x);
+
+	private static final Messages MESSAGES = Messages.of(AssertionPredicate.class, "Messages");
+	static final String
+		MSG_valueDidNotPassTest = MESSAGES.getString("valueDidNotPassTest"),
+		MSG_valueDidNotPassTestWithValue = MESSAGES.getString("valueDidNotPassTestWithValue"),
+		MSG_predicateTestFailed = MESSAGES.getString("predicateTestFailed"),
+		MSG_noPredicateTestsPassed = MESSAGES.getString("noPredicateTestsPassed"),
+		MSG_predicateTestsUnexpectedlyPassed = MESSAGES.getString("predicateTestsUnexpectedlyPassed");
+
 	final Predicate<T> inner;
 	final String message;
+	final Object[] args;
 	final ThreadLocal<String> failedMessage = new ThreadLocal<>();
 
 	/**
@@ -33,25 +74,48 @@ public class AssertionPredicate<T> implements Predicate<T> {
 	 * @param inner The predicate test.
 	 * @param message
 	 * 	The error message if predicate fails.
-	 * 	<br>Can contain <c>{VALUE}</c> variable.
-	 * @param args Optional message arguments.
+	 * 	<br>Supports {@link MessageFormat}-style arguments.
+	 * @param args
+	 * 	Optional message arguments.
+	 * 	<br>Can contain {@link #VALUE} to specify the value itself as an argument.
+	 * 	<br>Can contain {@link Function functions} to apply to the tested value.
 	 */
 	public AssertionPredicate(Predicate<T> inner, String message, Object...args) {
 		this.inner = inner;
-		this.message = format(message, args);
+		if (message != null) {
+			this.message = message;
+			this.args = args;
+		} else if (inner instanceof AssertionPredicate) {
+			this.message = MSG_valueDidNotPassTest;
+			this.args = new Object[]{};
+		} else {
+			this.message = MSG_valueDidNotPassTestWithValue;
+			this.args = new Object[]{VALUE};
+		}
 	}
 
 	AssertionPredicate() {
 		this.inner = null;
 		this.message = null;
+		this.args = null;
 	}
 
 	@Override /* Predicate */
+	@SuppressWarnings({"unchecked","rawtypes"})
 	public boolean test(T t) {
 		failedMessage.remove();
 		boolean b = inner.test(t);
 		if (! b) {
-			String m = message.replace("{VALUE}", stringify(t));
+			String m = message;
+			Object[] args = new Object[this.args.length];
+			for (int i = 0; i < args.length; i++) {
+				Object a = this.args[i];
+				if (a instanceof Function)
+					args[i] = ((Function)a).apply(t);
+				else
+					args[i] = a;
+			}
+			m = format(m, args);
 			if (inner instanceof AssertionPredicate)
 				m += "\n\t" + ((AssertionPredicate<?>)inner).getFailureMessage();
 			failedMessage.set(m);
@@ -99,7 +163,7 @@ public class AssertionPredicate<T> implements Predicate<T> {
 				if (p != null) {
 					boolean b = p.test(t);
 					if (! b) {
-						String m = format("Predicate test #{0} failed.", i);
+						String m = format(MSG_predicateTestFailed, i+1);
 						if (p instanceof AssertionPredicate)
 							m += "\n\t" + ((AssertionPredicate<?>)p).getFailureMessage();
 						failedMessage.set(m);
@@ -141,7 +205,7 @@ public class AssertionPredicate<T> implements Predicate<T> {
 				if (p != null)
 					if (p.test(t))
 						return true;
-			String m = format("No predicate tests passed.");
+			String m = format(MSG_noPredicateTestsPassed);
 			failedMessage.set(m);
 			return false;
 		}
@@ -175,10 +239,7 @@ public class AssertionPredicate<T> implements Predicate<T> {
 			if (p != null) {
 				boolean b = p.test(t);
 				if (b) {
-					String m = format("Predicate test unexpectedly passed.");
-					if (p instanceof AssertionPredicate)
-						m += "\n\t" + ((AssertionPredicate<?>)p).getFailureMessage();
-					failedMessage.set(m);
+					failedMessage.set(format(MSG_predicateTestsUnexpectedlyPassed));
 					return false;
 				}
 			}
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/assertions/AssertionPredicates.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/assertions/AssertionPredicates.java
index 3416ae6..4d550c0 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/assertions/AssertionPredicates.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/assertions/AssertionPredicates.java
@@ -13,15 +13,28 @@
 package org.apache.juneau.assertions;
 
 import static org.apache.juneau.internal.StringUtils.*;
+import static org.apache.juneau.assertions.AssertionPredicate.*;
+import static org.apache.juneau.assertions.Assertions.*;
 
+import java.text.*;
 import java.util.*;
 import java.util.function.*;
 import java.util.regex.*;
 
+import org.apache.juneau.cp.*;
 import org.apache.juneau.internal.*;
 
 /**
- * Generic predicates that can be run.
+ * Predefined {@link AssertionPredicate} objects.
+ *
+ * <p>
+ * Typically used wherever predicates are allowed for testing of {@link Assertion} objects such as...
+ * <ul>
+ * 	<li>{@link FluentObjectAssertion#passes(Predicate)}
+ * 	<li>{@link FluentArrayAssertion#passes(Predicate...)}
+ * 	<li>{@link FluentPrimitiveArrayAssertion#passes(Predicate...)}
+ * 	<li>{@link FluentListAssertion#passes(Predicate...)}
+ * </ul>
  *
  * <h5 class='section'>Example:</h5>
  * <p class='bcode w800'>
@@ -33,6 +46,18 @@ import org.apache.juneau.internal.*;
  */
 public class AssertionPredicates {
 
+	private static Function<Object,String> TYPENAME = x -> x == null ? null : x.getClass().getName();
+
+	private static final Messages MESSAGES = Messages.of(AssertionPredicates.class, "Messages");
+	static final String
+		MSG_valueWasNull = MESSAGES.getString("valueWasNull"),
+		MSG_valueWasNotNull = MESSAGES.getString("valueWasNotNull"),
+		MSG_valueDidNotMatchExpected = MESSAGES.getString("valueDidNotMatchExpected"),
+		MSG_valueUnexpectedlyMatched = MESSAGES.getString("valueUnexpectedlyMatched"),
+		MSG_valueDidNotMatchValue = MESSAGES.getString("valueDidNotMatchValue"),
+		MSG_valueWasNotExpectedType = MESSAGES.getString("valueWasNotExpectedType"),
+		MSG_valueDidNotMatchPattern = MESSAGES.getString("valueDidNotMatchPattern");
+
 	/**
 	 * Predicate that always returns <jk>true</jk>.
 	 *
@@ -42,28 +67,34 @@ public class AssertionPredicates {
 	 * @param <T> The object type being tested.
 	 * @return A new predicate.
 	 */
-	public static final <T> Predicate<T> any() {
-		return new AssertionPredicate<>(x -> true, null);
+	public static final <T> AssertionPredicate<T> any() {
+		return test(x -> true, null);
 	}
 
 	/**
 	 * Predicate that returns <jk>true</jk> if the tested value is not null.
 	 *
+	 * <p>
+	 * Assertion error message is <js>"Value was null."</js>.
+	 *
 	 * @param <T> The object type being tested.
 	 * @return A new predicate.
 	 */
-	public static final <T> Predicate<T> notNull() {
-		return new AssertionPredicate<>(x -> x != null, "Value was null.");
+	public static final <T> AssertionPredicate<T> notNull() {
+		return test(x -> x != null, MSG_valueWasNull);
 	}
 
 	/**
 	 * Predicate that returns <jk>true</jk> if the tested value is null.
 	 *
+	 * <p>
+	 * Assertion error message is <js>"Value was not null."</js>.
+	 *
 	 * @param <T> The object type being tested.
 	 * @return A new predicate.
 	 */
-	public static final <T> Predicate<T> isNull() {
-		return new AssertionPredicate<>(x -> x == null, "Value was not null.");
+	public static final <T> AssertionPredicate<T> isNull() {
+		return test(x -> x == null, MSG_valueWasNotNull);
 	}
 
 	/**
@@ -72,67 +103,101 @@ public class AssertionPredicates {
 	 * <p>
 	 * Uses standard Java equality for testing.
 	 *
+	 * <p>
+	 * Assertion error message is <js>"Value did not match expected.  Expected='{0}', Actual='{1}'."</js>.
+	 *
 	 * @param <T> The object type being tested.
 	 * @param value The specified value.
 	 * @return A new predicate.
 	 */
-	public static final <T> Predicate<T> eq(Object value) {
-		return new AssertionPredicate<>(x -> Objects.equals(x, value), "Value did not match expected.  Expected=''{0}'', Actual='{VALUE}'.", value);
+	public static final <T> AssertionPredicate<T> eq(Object value) {
+		return test(x -> Objects.equals(x, value), MSG_valueDidNotMatchExpected, value, VALUE);
 	}
 
 	/**
 	 * Predicate that returns <jk>true</jk> if the tested value converted to a string matches the specified value.
 	 *
+	 * <p>
+	 * Assertion error message is <js>"Value did not match expected.  Expected='{0}', Actual='{1}'."</js>.
+	 *
 	 * @param <T> The object type being tested.
 	 * @param value The specified value.
 	 * @return A new predicate.
 	 */
-	public static final <T> Predicate<T> eq(String value) {
-		return new AssertionPredicate<>(x -> Objects.equals(stringify(x), value), "Value did not match expected.  Expected=''{0}'', Actual='{VALUE}'.", value);
+	public static final <T> AssertionPredicate<T> eq(String value) {
+		return test(x -> Objects.equals(stringify(x), value), MSG_valueDidNotMatchExpected, value, VALUE);
 	}
 
 	/**
 	 * Predicate that returns <jk>true</jk> if the tested value does not match the specified value.
 	 *
+	 * <p>
+	 * Assertion error message is <js>"Value unexpectedly matched.  Value='{0}'."</js>.
+	 *
 	 * @param <T> The object type being tested.
 	 * @param value The specified value.
 	 * @return A new predicate.
 	 */
-	public static final <T> Predicate<T> ne(Object value) {
-		return new AssertionPredicate<>(x -> ! Objects.equals(x, value), "Value unexpectedly matched.  Value='{VALUE}'.");
+	public static final <T> AssertionPredicate<T> ne(Object value) {
+		return test(x -> ! Objects.equals(x, value), MSG_valueUnexpectedlyMatched, VALUE);
 	}
 
 	/**
 	 * Predicate that returns <jk>true</jk> if the tested value converted to a string does not match the specified value.
 	 *
+	 * <p>
+	 * Assertion error message is <js>"Value unexpectedly matched.  Value='{0}'."</js>.
+	 *
 	 * @param <T> The object type being tested.
 	 * @param value The specified value.
 	 * @return A new predicate.
 	 */
-	public static final <T> Predicate<T> ne(String value) {
-		return new AssertionPredicate<>(x -> ! Objects.equals(stringify(x), value), "Value unexpectedly matched.  Value='{VALUE}'.");
+	public static final <T> AssertionPredicate<T> ne(String value) {
+		return test(x -> ! Objects.equals(stringify(x), value), MSG_valueUnexpectedlyMatched, VALUE);
 	}
 
 	/**
 	 * Predicate that returns <jk>true</jk> if the tested value converted to a string does not match the specified value ignoring case.
 	 *
+	 * <p>
+	 * Assertion error message is <js>"Value did not match expected.  Expected='{0}', Actual='{1}'."</js>.
+	 *
 	 * @param <T> The object type being tested.
 	 * @param value The specified value.
 	 * @return A new predicate.
 	 */
-	public static final <T> Predicate<T> eqic(String value) {
-		return new AssertionPredicate<>(x -> StringUtils.eqic(stringify(x), value), "Value did not match expected.  Expected=''{0}'', Actual='{VALUE}'.", value);
+	public static final <T> AssertionPredicate<T> eqic(String value) {
+		return test(x -> StringUtils.eqic(stringify(x), value), MSG_valueDidNotMatchExpected, value, VALUE);
 	}
 
 	/**
-	 * Predicate that returns <jk>true</jk> if the tested value is the specified type.
+	 * Predicate that returns <jk>true</jk> if the tested value is the specified or child type.
+	 *
+	 * <p>
+	 * Assertion error message is <js>"Value was not expected type.  Expected='{0}', Actual='{1}'."</js>.
 	 *
 	 * @param <T> The object type being tested.
 	 * @param type The specified type.
 	 * @return A new predicate.
 	 */
-	public static final <T> Predicate<T> type(Class<?> type) {
-		return new AssertionPredicate<>(x -> x.getClass().isAssignableFrom(type), "Value was not expected type  Expected=''{0}'', Actual='{VALUE}'.", type);
+	public static final <T> AssertionPredicate<T> type(Class<?> type) {
+		assertArgNotNull("type", type);
+		return test(x -> x != null && type.isAssignableFrom(x.getClass()), MSG_valueWasNotExpectedType, type, TYPENAME);
+	}
+
+	/**
+	 * Predicate that returns <jk>true</jk> if the tested value is exactly specified type.
+	 *
+	 * <p>
+	 * Assertion error message is <js>"Value was not expected type.  Expected='{0}', Actual='{1}'."</js>.
+	 *
+	 * @param <T> The object type being tested.
+	 * @param type The specified type.
+	 * @return A new predicate.
+	 */
+	public static final <T> AssertionPredicate<T> exactType(Class<?> type) {
+		assertArgNotNull("type", type);
+		return test(x -> x != null && x.getClass().equals(type), MSG_valueWasNotExpectedType, type, TYPENAME);
 	}
 
 	/**
@@ -141,65 +206,155 @@ public class AssertionPredicates {
 	 * <p>
 	 * Match pattern can contain the <js>"*"</js> meta-character.
 	 *
+	 * <p>
+	 * Assertion error message is <js>"Value did not match pattern.  Pattern='{0}', Actual='{1}'."</js>.
+	 *
 	 * @param <T> The object type being tested.
 	 * @param value The specified value.
 	 * @return A new predicate.
 	 */
-	public static final <T> Predicate<T> match(String value) {
-		return regex(StringUtils.getMatchPattern(value));
+	public static final <T> AssertionPredicate<T> match(String value) {
+		assertArgNotNull("value", value);
+		Pattern p = StringUtils.getMatchPattern(value);
+		return test(x -> x != null && p.matcher(stringify(x)).matches(), MSG_valueDidNotMatchPattern, value, VALUE);
+	}
+
+	/**
+	 * Predicate that returns <jk>true</jk> if the tested value converted to a string matches the specified regular expression.
+	 *
+	 * <p>
+	 * Assertion error message is <js>"Value did not match pattern.  Pattern='{0}', Actual='{1}'."</js>.
+	 *
+	 * @param <T> The object type being tested.
+	 * @param expression The regular expression to match.
+	 * @return A new predicate.
+	 */
+	public static final <T> AssertionPredicate<T> regex(String expression) {
+		assertArgNotNull("expression", expression);
+		Pattern p = Pattern.compile(expression);
+		return test(x -> x != null && p.matcher(stringify(x)).matches(), MSG_valueDidNotMatchPattern, expression, VALUE);
 	}
 
 	/**
 	 * Predicate that returns <jk>true</jk> if the tested value converted to a string matches the specified regular expression.
 	 *
+	 * <p>
+	 * Assertion error message is <js>"Value did not match pattern.  Pattern='{0}', Actual='{1}'."</js>.
+	 *
 	 * @param <T> The object type being tested.
 	 * @param expression The regular expression to match.
+	 * @param flags Match flags, a bit mask that may include:
+	 * 	<ul>
+	 * 		<li>{@link Pattern#CASE_INSENSITIVE CASE_INSENSITIVE}
+	 * 		<li>{@link Pattern#MULTILINE MULTILINE}
+	 * 		<li>{@link Pattern#DOTALL DOTALL}
+	 * 		<li>{@link Pattern#UNICODE_CASE UNICODE_CASE}
+	 * 		<li>{@link Pattern#CANON_EQ CANON_EQ}
+	 * 		<li>{@link Pattern#UNIX_LINES UNIX_LINES}
+	 * 		<li>{@link Pattern#LITERAL LITERAL}
+	 * 		<li>{@link Pattern#UNICODE_CHARACTER_CLASS UNICODE_CHARACTER_CLASS}
+	 * 		<li>{@link Pattern#COMMENTS COMMENTS}
 	 * @return A new predicate.
 	 */
-	public static final <T> Predicate<T> regex(String expression) {
-		return regex(Pattern.compile(expression));
+	public static final <T> AssertionPredicate<T> regex(String expression, int flags) {
+		assertArgNotNull("expression", expression);
+		Pattern p = Pattern.compile(expression, flags);
+		return test(x -> x != null && p.matcher(stringify(x)).matches(), MSG_valueDidNotMatchPattern, expression, VALUE);
 	}
 
 	/**
 	 * Predicate that returns <jk>true</jk> if the tested value converted to a string matches the specified regular expression.
 	 *
+	 * <p>
+	 * Assertion error message is <js>"Value did not match pattern.  Pattern='{0}', Actual='{1}'."</js>.
+	 *
 	 * @param <T> The object type being tested.
 	 * @param value The regular expression to match.
 	 * @return A new predicate.
 	 */
-	public static final <T> Predicate<T> regex(Pattern value) {
-		return new AssertionPredicate<>(x -> x != null && value.matcher(stringify(x)).matches(), "Value did not match pattern.  Pattern=''{0}'', Actual='{VALUE}'", value.pattern());
+	public static final <T> AssertionPredicate<T> regex(Pattern value) {
+		assertArgNotNull("value", value);
+		return test(x -> x != null && value.matcher(stringify(x)).matches(), MSG_valueDidNotMatchPattern, value.pattern(), VALUE);
+	}
+
+	/**
+	 * Predicate that wraps another predicate.
+	 *
+	 * <p>
+	 * If the predicate extends from {@link AssertionPredicate}, the assertion error
+	 * message is <js>"Value did not pass test."</js> followed by the inner assertion error.
+	 * Otherwise the message is <js>"Value did not pass test.  Value='{0}'."</js>
+	 *
+	 * @param <T> The object type being tested.
+	 * @param predicate The predicate to run.
+	 * @return A new predicate.
+	 */
+	public static final <T> AssertionPredicate<T> test(Predicate<T> predicate) {
+		return new AssertionPredicate<>(predicate, null);
+	}
+
+	/**
+	 * Predicate that wraps another predicate.
+	 *
+	 * <p>
+	 * If the message specified is <jk>null</jk> and the predicate extends from {@link AssertionPredicate}, the assertion error
+	 * message is <js>"Value did not pass test."</js> followed by the inner assertion error.
+	 * Otherwise the message is <js>"Value did not pass test.  Value='{0}'."</js>
+	 *
+	 * @param <T> The object type being tested.
+	 * @param predicate The predicate to run.
+	 * @param msg
+	 * 	The error message if predicate fails.
+	 * 	<br>Supports {@link MessageFormat}-style arguments.
+	 * @param args
+	 * 	Optional message arguments.
+	 * 	<br>Can contain {@link #VALUE} to specify the value itself as an argument.
+	 * 	<br>Can contain {@link Function functions} to apply to the tested value.
+	 * @return A new predicate.
+	 */
+	public static final <T> AssertionPredicate<T> test(Predicate<T> predicate, String msg, Object...args) {
+		return new AssertionPredicate<>(predicate, msg, args);
 	}
 
 	/**
 	 * Combines the specified predicates into a singled AND'ed predicate.
 	 *
+	 * <p>
+	 * Assertion error message is <js>"Predicate test #x failed."</js> followed by
+	 * the inner failed message if the failed predicate extends from {@link AssertionPredicate}.
+	 *
 	 * @param predicates The predicates to combine.
 	 * @return The combined predicates.
 	 */
 	@SafeVarargs
-	public static final <T> Predicate<T> and(Predicate<T>...predicates) {
+	public static final <T> AssertionPredicate<T> and(Predicate<T>...predicates) {
 		return new AssertionPredicate.And<>(predicates);
 	}
 
 	/**
 	 * Combines the specified predicates into a singled OR'ed predicate.
 	 *
+	 * <p>
+	 * Assertion error message is <js>"No predicate tests passed."</js>.
+	 *
 	 * @param predicates The predicates to combine.
 	 * @return The combined predicates.
 	 */
 	@SafeVarargs
-	public static final <T> Predicate<T> or(Predicate<T>...predicates) {
+	public static final <T> AssertionPredicate<T> or(Predicate<T>...predicates) {
 		return new AssertionPredicate.Or<>(predicates);
 	}
 
 	/**
 	 * Negates the specified predicate.
 	 *
+	 * <p>
+	 * Assertion error message is <js>"Predicate test unexpectedly passed."</js>.
+	 *
 	 * @param predicate The predicate to negate.
 	 * @return The combined predicates.
 	 */
-	public static final <T> Predicate<T> not(Predicate<T> predicate) {
+	public static final <T> AssertionPredicate<T> not(Predicate<T> predicate) {
 		return new AssertionPredicate.Not<>(predicate);
 	}
 }
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/assertions/Assertions.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/assertions/Assertions.java
index e728bfb..65e1a58 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/assertions/Assertions.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/assertions/Assertions.java
@@ -20,12 +20,17 @@ import java.time.*;
 import java.util.*;
 
 import org.apache.juneau.*;
+import org.apache.juneau.cp.*;
 
 /**
  * Main class for creation of assertions for testing.
  */
 public class Assertions {
 
+	private static final Messages MESSAGES = Messages.of(Assertions.class, "Messages");
+	static final String
+		MSG_argumentCannotBeNull = MESSAGES.getString("argumentCannotBeNull");
+
 	/**
 	 * Used for assertion calls against {@link Date} objects.
 	 *
@@ -616,7 +621,7 @@ public class Assertions {
 	 * @throws IllegalArgumentException Constructed exception.
 	 */
 	public static final <T> T assertArgNotNull(String name, T o) throws IllegalArgumentException {
-		assertArg(o != null, "Argument ''{0}'' cannot be null", name);
+		assertArg(o != null, MSG_argumentCannotBeNull, name);
 		return o;
 	}
 
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/assertions/FluentArrayAssertion.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/assertions/FluentArrayAssertion.java
index 66d31c4..b04170b 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/assertions/FluentArrayAssertion.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/assertions/FluentArrayAssertion.java
@@ -21,6 +21,7 @@ import java.util.*;
 import java.util.function.*;
 
 import org.apache.juneau.collections.*;
+import org.apache.juneau.cp.*;
 import org.apache.juneau.internal.*;
 
 /**
@@ -32,6 +33,15 @@ import org.apache.juneau.internal.*;
 @FluentSetters(returns="FluentArrayAssertion<E,R>")
 public class FluentArrayAssertion<E,R> extends FluentObjectAssertion<E[],R> {
 
+	private static final Messages MESSAGES = Messages.of(FluentArrayAssertion.class, "Messages");
+	static final String
+		MSG_arrayWasNotEmpty = MESSAGES.getString("arrayWasNotEmpty"),
+		MSG_arrayWasEmpty = MESSAGES.getString("arrayWasEmpty"),
+		MSG_arrayUnexpectedSize = MESSAGES.getString("arrayUnexpectedSize"),
+		MSG_arrayDidNotContainExpectedValue = MESSAGES.getString("arrayDidNotContainExpectedValue"),
+		MSG_arrayContainedUnexpectedValue = MESSAGES.getString("arrayContainedUnexpectedValue"),
+		MSG_arrayDidNotContainExpectedValueAt = MESSAGES.getString("arrayDidNotContainExpectedValueAt");
+
 	/**
 	 * Constructor.
 	 *
@@ -61,7 +71,7 @@ public class FluentArrayAssertion<E,R> extends FluentObjectAssertion<E[],R> {
 	 */
 	public R isEmpty() throws AssertionError {
 		if (length() != 0)
-			throw error("Array was not empty.");
+			throw error(MSG_arrayWasNotEmpty);
 		return returns();
 	}
 
@@ -73,7 +83,7 @@ public class FluentArrayAssertion<E,R> extends FluentObjectAssertion<E[],R> {
 	 */
 	public R isNotEmpty() throws AssertionError {
 		if (length() == 0)
-			throw error("Array was empty.");
+			throw error(MSG_arrayWasEmpty);
 		return returns();
 	}
 
@@ -86,7 +96,7 @@ public class FluentArrayAssertion<E,R> extends FluentObjectAssertion<E[],R> {
 	 */
 	public R isSize(int size) throws AssertionError {
 		if (length() != size)
-			throw error("Array did not have the expected size.  Expect={0}, Actual={1}.", size, length());
+			throw error(MSG_arrayUnexpectedSize, size, length());
 		return returns();
 	}
 
@@ -101,7 +111,7 @@ public class FluentArrayAssertion<E,R> extends FluentObjectAssertion<E[],R> {
 		for (int i = 0, j = length(); i < j; i++)
 			if (eq(at(i), entry))
 				return returns();
-		throw error("Array did not contain expected value.\n\tContents: {0}\n\tExpected: {1}", toString(), entry);
+		throw error(MSG_arrayDidNotContainExpectedValue, entry, toString());
 	}
 
 	/**
@@ -115,7 +125,7 @@ public class FluentArrayAssertion<E,R> extends FluentObjectAssertion<E[],R> {
 		for (int i = 0, j = length(); i < j; i++)
 			if (eq(stringify(at(i)), entry))
 				return returns();
-		throw error("Array did not contain expected value.\n\tContents: {0}\n\tExpected: {1}", toString(), entry);
+		throw error(MSG_arrayDidNotContainExpectedValue, entry, toString());
 	}
 
 	/**
@@ -128,7 +138,7 @@ public class FluentArrayAssertion<E,R> extends FluentObjectAssertion<E[],R> {
 	public R doesNotContain(Object entry) throws AssertionError {
 		for (int i = 0, j = length(); i < j; i++)
 			if (eq(at(i), entry))
-				throw error("Array contained unexpected value.\n\tContents: {0}\n\tUnexpected: {1}", toString(), entry);
+				throw error(MSG_arrayContainedUnexpectedValue, entry, toString());
 		return returns();
 	}
 
@@ -249,7 +259,7 @@ public class FluentArrayAssertion<E,R> extends FluentObjectAssertion<E[],R> {
 		for (int i = 0, j = length(); i < j; i++) {
 			Predicate<E> t = tests[i];
 			if (t != null && ! t.test(at(i)))
-				throw error("Array did not contain expected value at index {0}.\n\t{1}", i, getFailureMessage(t, at(i)));
+				throw error(MSG_arrayDidNotContainExpectedValueAt, i, getFailureMessage(t, at(i)));
 		}
 		return returns();
 	}
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/assertions/FluentBooleanAssertion.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/assertions/FluentBooleanAssertion.java
index e82fbb2..b463a8d 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/assertions/FluentBooleanAssertion.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/assertions/FluentBooleanAssertion.java
@@ -15,6 +15,7 @@ package org.apache.juneau.assertions;
 
 import java.io.*;
 
+import org.apache.juneau.cp.*;
 import org.apache.juneau.internal.*;
 
 /**
@@ -25,6 +26,11 @@ import org.apache.juneau.internal.*;
 @FluentSetters(returns="FluentBooleanAssertion<R>")
 public class FluentBooleanAssertion<R> extends FluentComparableAssertion<Boolean,R> {
 
+	private static final Messages MESSAGES = Messages.of(FluentBooleanAssertion.class, "Messages");
+	static final String
+		MSG_valueWasFalse = MESSAGES.getString("valueWasFalse"),
+		MSG_valueWasTrue = MESSAGES.getString("valueWasTrue");
+
 	/**
 	 * Constructor.
 	 *
@@ -54,7 +60,7 @@ public class FluentBooleanAssertion<R> extends FluentComparableAssertion<Boolean
 	 */
 	public R isTrue() throws AssertionError {
 		if (value() == false)
-			throw error("Value was false.");
+			throw error(MSG_valueWasFalse);
 		return returns();
 	}
 
@@ -66,7 +72,7 @@ public class FluentBooleanAssertion<R> extends FluentComparableAssertion<Boolean
 	 */
 	public R isFalse() throws AssertionError {
 		if (value() == true)
-			throw error("Value was true.");
+			throw error(MSG_valueWasTrue);
 		return returns();
 	}
 
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/assertions/FluentCollectionAssertion.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/assertions/FluentCollectionAssertion.java
index e04f92f..d8f4a33 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/assertions/FluentCollectionAssertion.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/assertions/FluentCollectionAssertion.java
@@ -17,6 +17,7 @@ import static org.apache.juneau.internal.ObjectUtils.*;
 import java.io.*;
 import java.util.*;
 
+import org.apache.juneau.cp.*;
 import org.apache.juneau.internal.*;
 
 /**
@@ -28,6 +29,14 @@ import org.apache.juneau.internal.*;
 @FluentSetters(returns="FluentCollectionAssertion<E,R>")
 public class FluentCollectionAssertion<E,R> extends FluentObjectAssertion<Collection<E>,R> {
 
+	private static final Messages MESSAGES = Messages.of(FluentCollectionAssertion.class, "Messages");
+	static final String
+		MSG_collectionWasNotEmpty = MESSAGES.getString("collectionWasNotEmpty"),
+		MSG_collectionDidNotContainExpectedValue = MESSAGES.getString("collectionDidNotContainExpectedValue"),
+		MSG_collectionContainedUnexpectedValue = MESSAGES.getString("collectionContainedUnexpectedValue"),
+		MSG_collectionWasEmpty = MESSAGES.getString("collectionWasEmpty"),
+		MSG_collectionDidNotHaveExpectedSize = MESSAGES.getString("collectionDidNotHaveExpectedSize");
+
 	/**
 	 * Constructor.
 	 *
@@ -57,7 +66,7 @@ public class FluentCollectionAssertion<E,R> extends FluentObjectAssertion<Collec
 	 */
 	public R isEmpty() throws AssertionError {
 		if (! value().isEmpty())
-			throw error("Collection was not empty.");
+			throw error(MSG_collectionWasNotEmpty);
 		return returns();
 	}
 
@@ -72,7 +81,7 @@ public class FluentCollectionAssertion<E,R> extends FluentObjectAssertion<Collec
 		for (Object v : value())
 			if (eq(v, entry))
 				return returns();
-		throw error("Collection did not contain expected value.\n\tContents: {0}\n\tExpected: {1}", value(), entry);
+		throw error(MSG_collectionDidNotContainExpectedValue, entry, value());
 	}
 
 	/**
@@ -85,7 +94,7 @@ public class FluentCollectionAssertion<E,R> extends FluentObjectAssertion<Collec
 	public R doesNotContain(E entry) throws AssertionError {
 		for (Object v : value())
 			if (eq(v, entry))
-				throw error("Collection contained unexpected value.\n\tContents: {0}\n\tUnexpected: {1}", value(), entry);
+				throw error(MSG_collectionContainedUnexpectedValue, entry, value());
 		return returns();
 	}
 
@@ -97,7 +106,7 @@ public class FluentCollectionAssertion<E,R> extends FluentObjectAssertion<Collec
 	 */
 	public R isNotEmpty() throws AssertionError {
 		if (value().isEmpty())
-			throw error("Collection was empty.");
+			throw error(MSG_collectionWasEmpty);
 		return returns();
 	}
 
@@ -110,7 +119,7 @@ public class FluentCollectionAssertion<E,R> extends FluentObjectAssertion<Collec
 	 */
 	public R isSize(int size) throws AssertionError {
 		if (size() != size)
-			throw error("Collection did not have the expected size.  Expect={0}, Actual={1}.", size, size());
+			throw error(MSG_collectionDidNotHaveExpectedSize, size, size());
 		return returns();
 	}
 
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/assertions/FluentComparableAssertion.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/assertions/FluentComparableAssertion.java
index c6b5ddb..7ed39cd 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/assertions/FluentComparableAssertion.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/assertions/FluentComparableAssertion.java
@@ -12,8 +12,11 @@
 // ***************************************************************************************************************************
 package org.apache.juneau.assertions;
 
+import static org.apache.juneau.assertions.Assertions.*;
+
 import java.io.*;
 
+import org.apache.juneau.cp.*;
 import org.apache.juneau.internal.*;
 
 /**
@@ -26,6 +29,13 @@ import org.apache.juneau.internal.*;
 @SuppressWarnings("rawtypes")
 public class FluentComparableAssertion<T extends Comparable,R> extends FluentObjectAssertion<T,R> {
 
+	private static final Messages MESSAGES = Messages.of(FluentComparableAssertion.class, "Messages");
+	static final String
+		MSG_valueWasNotGreaterThanExpected = MESSAGES.getString("valueWasNotGreaterThanExpected"),
+		MSG_valueWasNotGreaterOrEqualsToExpected = MESSAGES.getString("valueWasNotGreaterOrEqualsToExpected"),
+		MSG_valueWasNotLessThanExpected = MESSAGES.getString("valueWasNotLessThanExpected"),
+		MSG_valueWasNotLessOrEqualsToExpected = MESSAGES.getString("valueWasNotLessOrEqualsToExpected");
+
 	/**
 	 * Constructor.
 	 *
@@ -55,9 +65,9 @@ public class FluentComparableAssertion<T extends Comparable,R> extends FluentObj
 	 * @throws AssertionError If assertion failed.
 	 */
 	public R isGreaterThan(Comparable value) throws AssertionError {
-		assertNotNull("value", value);
+		assertArgNotNull("value", value);
 		if (compareTo(value) <= 0)
-			throw error("Value was not greater than expected.\n\tExpect=[{0}]\n\tActual=[{1}]", value, value());
+			throw error(MSG_valueWasNotGreaterThanExpected, value, value());
 		return returns();
 	}
 
@@ -83,9 +93,9 @@ public class FluentComparableAssertion<T extends Comparable,R> extends FluentObj
 	 * @throws AssertionError If assertion failed.
 	 */
 	public R isGreaterThanOrEqual(Comparable value) throws AssertionError {
-		assertNotNull("value", value);
+		assertArgNotNull("value", value);
 		if (compareTo(value) < 0)
-				throw error("Value was not greater than or equals to expected.\n\tExpect=[{0}]\n\tActual=[{1}]", value, value());
+				throw error(MSG_valueWasNotGreaterOrEqualsToExpected, value, value());
 		return returns();
 	}
 
@@ -111,9 +121,9 @@ public class FluentComparableAssertion<T extends Comparable,R> extends FluentObj
 	 * @throws AssertionError If assertion failed.
 	 */
 	public R isLessThan(Comparable value) throws AssertionError {
-		assertNotNull("value", value);
+		assertArgNotNull("value", value);
 		if (compareTo(value) >= 0)
-				throw error("Value was not less than expected.\n\tExpect=[{0}]\n\tActual=[{1}]", value, value());
+				throw error(MSG_valueWasNotLessThanExpected, value, value());
 		return returns();
 	}
 
@@ -139,9 +149,9 @@ public class FluentComparableAssertion<T extends Comparable,R> extends FluentObj
 	 * @throws AssertionError If assertion failed.
 	 */
 	public R isLessThanOrEqual(Comparable value) throws AssertionError {
-		assertNotNull("value", value);
+		assertArgNotNull("value", value);
 		if (compareTo(value) > 0)
-				throw error("Value was not less than or equals to expected.\n\tExpect=[{0}]\n\tActual=[{1}]", value, value());
+				throw error(MSG_valueWasNotLessOrEqualsToExpected, value, value());
 		return returns();
 	}
 
@@ -169,8 +179,8 @@ public class FluentComparableAssertion<T extends Comparable,R> extends FluentObj
 	 */
 	public R isBetween(Comparable lower, Comparable upper) throws AssertionError {
 		exists();
-		assertNotNull("lower", lower);
-		assertNotNull("upper", upper);
+		assertArgNotNull("lower", lower);
+		assertArgNotNull("upper", upper);
 		isLessThanOrEqual(upper);
 		isGreaterThanOrEqual(lower);
 		return returns();
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/assertions/FluentDateAssertion.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/assertions/FluentDateAssertion.java
index 28a095a..32867e4 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/assertions/FluentDateAssertion.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/assertions/FluentDateAssertion.java
@@ -12,12 +12,14 @@
 // ***************************************************************************************************************************
 package org.apache.juneau.assertions;
 
+import static org.apache.juneau.assertions.Assertions.*;
 import static org.apache.juneau.internal.ObjectUtils.*;
 
 import java.io.*;
 import java.time.temporal.*;
 import java.util.*;
 
+import org.apache.juneau.cp.*;
 import org.apache.juneau.internal.*;
 
 /**
@@ -37,6 +39,12 @@ import org.apache.juneau.internal.*;
 @FluentSetters(returns="FluentDateAssertion<R>")
 public class FluentDateAssertion<R> extends FluentComparableAssertion<Date,R> {
 
+	private static final Messages MESSAGES = Messages.of(FluentDateAssertion.class, "Messages");
+	static final String
+		MSG_unexpectedValue = MESSAGES.getString("unexpectedValue"),
+		MSG_valueWasNotAfterExpected = MESSAGES.getString("valueWasNotAfterExpected"),
+		MSG_valueWasNotBeforeExpected = MESSAGES.getString("valueWasNotBeforeExpected");
+
 	/**
 	 * Constructor.
 	 *
@@ -68,7 +76,7 @@ public class FluentDateAssertion<R> extends FluentComparableAssertion<Date,R> {
 	 */
 	public R isEqual(Date value, ChronoUnit precision) throws AssertionError {
 		if (ne(value(), value, (x,y)->x.toInstant().truncatedTo(precision).equals(y.toInstant().truncatedTo(precision))))
-			throw error("Unexpected value.\n\tExpect=[{0}]\n\tActual=[{1}]", value, value());
+			throw error(MSG_unexpectedValue, value, value());
 		return returns();
 	}
 
@@ -80,9 +88,9 @@ public class FluentDateAssertion<R> extends FluentComparableAssertion<Date,R> {
 	 * @throws AssertionError If assertion failed.
 	 */
 	public R isAfter(Date value) throws AssertionError {
-		assertNotNull("value", value);
+		assertArgNotNull("value", value);
 		if (! (value().after(value)))
-			throw error("Value was not after expected.\n\tExpect=[{0}]\n\tActual=[{1}]", value, value());
+			throw error(MSG_valueWasNotAfterExpected, value, value());
 		return returns();
 	}
 
@@ -104,9 +112,9 @@ public class FluentDateAssertion<R> extends FluentComparableAssertion<Date,R> {
 	 * @throws AssertionError If assertion failed.
 	 */
 	public R isBefore(Date value) throws AssertionError {
-		assertNotNull("value", value);
+		assertArgNotNull("value", value);
 		if (! (value().before(value)))
-			throw error("Value was not before expected.\n\tExpect=[{0}]\n\tActual=[{1}]", value, value());
+			throw error(MSG_valueWasNotBeforeExpected, value, value());
 		return returns();
 	}
 
@@ -130,8 +138,8 @@ public class FluentDateAssertion<R> extends FluentComparableAssertion<Date,R> {
 	 */
 	public R isBetween(Date lower, Date upper) throws AssertionError {
 		exists();
-		assertNotNull("lower", lower);
-		assertNotNull("upper", upper);
+		assertArgNotNull("lower", lower);
+		assertArgNotNull("upper", upper);
 		isBefore(upper);
 		isAfter(lower);
 		return returns();
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/assertions/FluentListAssertion.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/assertions/FluentListAssertion.java
index ae5c2bb..fd064bb 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/assertions/FluentListAssertion.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/assertions/FluentListAssertion.java
@@ -19,6 +19,7 @@ import java.util.*;
 import java.util.function.*;
 
 import org.apache.juneau.collections.*;
+import org.apache.juneau.cp.*;
 import org.apache.juneau.internal.*;
 
 /**
@@ -30,6 +31,10 @@ import org.apache.juneau.internal.*;
 @FluentSetters(returns="FluentListAssertion<E,R>")
 public class FluentListAssertion<E,R> extends FluentCollectionAssertion<E,R> {
 
+	private static final Messages MESSAGES = Messages.of(FluentListAssertion.class, "Messages");
+	static final String
+		MSG_listDidNotContainExpectedValueAt = MESSAGES.getString("listDidNotContainExpectedValueAt");
+
 	/**
 	 * Constructor.
 	 *
@@ -155,7 +160,7 @@ public class FluentListAssertion<E,R> extends FluentCollectionAssertion<E,R> {
 		for (int i = 0, j = size(); i < j; i++) {
 			Predicate<E> t = tests[i];
 			if (t != null && ! t.test(at(i)))
-				throw error("List did not contain expected value at index {0}.\n\t{1}", i, getFailureMessage(t, at(i)));
+				throw error(MSG_listDidNotContainExpectedValueAt, i, getFailureMessage(t, at(i)));
 		}
 		return returns();
 	}
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/assertions/FluentMapAssertion.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/assertions/FluentMapAssertion.java
index c514165..a706162 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/assertions/FluentMapAssertion.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/assertions/FluentMapAssertion.java
@@ -19,6 +19,7 @@ import static java.util.Arrays.*;
 import java.io.*;
 import java.util.*;
 
+import org.apache.juneau.cp.*;
 import org.apache.juneau.internal.*;
 
 /**
@@ -31,6 +32,14 @@ import org.apache.juneau.internal.*;
 @FluentSetters(returns="FluentMapAssertion<K,V,R>")
 public class FluentMapAssertion<K,V,R> extends FluentObjectAssertion<Map<K,V>,R>  {
 
+	private static final Messages MESSAGES = Messages.of(FluentMapAssertion.class, "Messages");
+	static final String
+		MSG_mapWasNotEmpty = MESSAGES.getString("mapWasNotEmpty"),
+		MSG_mapDidNotContainExpectedKey = MESSAGES.getString("mapDidNotContainExpectedKey"),
+		MSG_mapContainedUnexpectedKey = MESSAGES.getString("mapContainedUnexpectedKey"),
+		MSG_mapWasEmpty = MESSAGES.getString("mapWasEmpty"),
+		MSG_mapDidNotHaveTheExpectedSize = MESSAGES.getString("mapDidNotHaveTheExpectedSize");
+
 	/**
 	 * Constructor.
 	 *
@@ -87,7 +96,7 @@ public class FluentMapAssertion<K,V,R> extends FluentObjectAssertion<Map<K,V>,R>
 	 */
 	public R isEmpty() throws AssertionError {
 		if (! value().isEmpty())
-			throw error("Map was not empty.");
+			throw error(MSG_mapWasNotEmpty);
 		return returns();
 	}
 
@@ -101,7 +110,7 @@ public class FluentMapAssertion<K,V,R> extends FluentObjectAssertion<Map<K,V>,R>
 	public R containsKey(String name) throws AssertionError {
 		if (value().containsKey(name))
 			return returns();
-		throw error("Map did not contain expected key.\n\tContents: {0}\n\tExpected key: {1}", value(), name);
+		throw error(MSG_mapDidNotContainExpectedKey, name, value());
 	}
 
 	/**
@@ -114,7 +123,7 @@ public class FluentMapAssertion<K,V,R> extends FluentObjectAssertion<Map<K,V>,R>
 	public R doesNotContainKey(String name) throws AssertionError {
 		if (! value().containsKey(name))
 			return returns();
-		throw error("Map contained unexpected key.\n\tContents: {0}\n\tUnexpected key: {1}", value(), name);
+		throw error(MSG_mapContainedUnexpectedKey, name, value());
 	}
 
 	/**
@@ -125,7 +134,7 @@ public class FluentMapAssertion<K,V,R> extends FluentObjectAssertion<Map<K,V>,R>
 	 */
 	public R isNotEmpty() throws AssertionError {
 		if (value().isEmpty())
-			throw error("Map was empty.");
+			throw error(MSG_mapWasEmpty);
 		return returns();
 	}
 
@@ -138,7 +147,7 @@ public class FluentMapAssertion<K,V,R> extends FluentObjectAssertion<Map<K,V>,R>
 	 */
 	public R isSize(int size) throws AssertionError {
 		if (size() != size)
-			throw error("Map did not have the expected size.  Expect={0}, Actual={1}.", size, size());
+			throw error(MSG_mapDidNotHaveTheExpectedSize, size, size());
 		return returns();
 	}
 
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/assertions/FluentObjectAssertion.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/assertions/FluentObjectAssertion.java
index 0ec2372..78a891c 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/assertions/FluentObjectAssertion.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/assertions/FluentObjectAssertion.java
@@ -12,6 +12,7 @@
 // ***************************************************************************************************************************
 package org.apache.juneau.assertions;
 
+import static org.apache.juneau.assertions.Assertions.*;
 import static org.apache.juneau.internal.ExceptionUtils.*;
 import static org.apache.juneau.internal.StringUtils.*;
 
@@ -21,6 +22,7 @@ import java.util.*;
 import java.util.function.*;
 
 import org.apache.juneau.*;
+import org.apache.juneau.cp.*;
 import org.apache.juneau.internal.*;
 import org.apache.juneau.json.*;
 import org.apache.juneau.reflect.*;
@@ -35,7 +37,19 @@ import org.apache.juneau.serializer.*;
 @FluentSetters(returns="FluentObjectAssertion<T,R>")
 public class FluentObjectAssertion<T,R> extends FluentAssertion<R> {
 
-	private final T value;
+	private static final Messages MESSAGES = Messages.of(FluentObjectAssertion.class, "Messages");
+	static final String
+		MSG_unexpectedType = MESSAGES.getString("unexpectedType"),
+		MSG_unexpectedComparison = MESSAGES.getString("unexpectedComparison"),
+		MSG_unexpectedValue = MESSAGES.getString("unexpectedValue"),
+		MSG_unexpectedValueDidNotExpect = MESSAGES.getString("unexpectedValueDidNotExpect"),
+		MSG_notTheSameValue = MESSAGES.getString("notTheSameValue"),
+		MSG_valueWasNull = MESSAGES.getString("valueWasNull"),
+		MSG_valueWasNotNull = MESSAGES.getString("valueWasNotNull"),
+		MSG_expectedValueNotFound = MESSAGES.getString("expectedValueNotFound"),
+		MSG_unexpectedValueFound = MESSAGES.getString("unexpectedValueFound"),
+		MSG_objectWasNotType = MESSAGES.getString("objectWasNotType"),
+		MSG_unexpectedValue2 = MESSAGES.getString("unexpectedValue2");
 
 	private static JsonSerializer JSON = JsonSerializer.create()
 		.ssq()
@@ -48,6 +62,8 @@ public class FluentObjectAssertion<T,R> extends FluentAssertion<R> {
 		.sortMaps()
 		.build();
 
+	private final T value;
+
 	/**
 	 * Constructor.
 	 *
@@ -84,9 +100,9 @@ public class FluentObjectAssertion<T,R> extends FluentAssertion<R> {
 	 * @throws AssertionError If assertion failed.
 	 */
 	public R isType(Class<?> parent) throws AssertionError {
-		assertNotNull("parent", parent);
+		assertArgNotNull("parent", parent);
 		if (! ClassInfo.of(value()).isChildOf(parent))
-			throw error("Unexpected class.\n\tExpect=[{0}]\n\tActual=[{1}]", className(parent), className(value));
+			throw error(MSG_unexpectedType, className(parent), className(value));
 		return returns();
 	}
 
@@ -209,7 +225,7 @@ public class FluentObjectAssertion<T,R> extends FluentAssertion<R> {
 			String s1 = serializer.serialize(value);
 			String s2 = serializer.serialize(o);
 			if (ne(s1, s2))
-				throw error("Unexpected comparison.\n\tExpect=[{0}]\n\tActual=[{1}]", s2, s1);
+				throw error(MSG_unexpectedComparison, s2, s1);
 		} catch (SerializeException e) {
 			throw runtimeException(e);
 		}
@@ -227,7 +243,7 @@ public class FluentObjectAssertion<T,R> extends FluentAssertion<R> {
 		if (this.value == value)
 			return returns();
 		if (! value().equals(equivalent(value)))
-			throw error("Unexpected value.\n\tExpect=[{0}]\n\tActual=[{1}]", value, this.value);
+			throw error(MSG_unexpectedValue, value, this.value);
 		return returns();
 	}
 
@@ -256,7 +272,7 @@ public class FluentObjectAssertion<T,R> extends FluentAssertion<R> {
 		if (this.value == null && value != null || this.value != null && value == null)
 			return returns();
 		if (this.value == null || this.value.equals(equivalent(value)))
-			throw error("Unexpected value.\n\tDid not expect=[{0}]\n\tActual=[{1}]", value, orElse(null));
+			throw error(MSG_unexpectedValueDidNotExpect, value, orElse(null));
 		return returns();
 	}
 
@@ -270,7 +286,7 @@ public class FluentObjectAssertion<T,R> extends FluentAssertion<R> {
 	public R isSameObjectAs(Object value) throws AssertionError {
 		if (this.value == value)
 			return returns();
-		throw error("Not the same value.\n\tExpect=[{0}]\n\tActual=[{1}]", value, this.value);
+		throw error(MSG_notTheSameValue, value, this.value);
 	}
 
 	/**
@@ -323,7 +339,7 @@ public class FluentObjectAssertion<T,R> extends FluentAssertion<R> {
 	 */
 	public R isNotNull() throws AssertionError {
 		if (value == null)
-			throw error("Value was null.");
+			throw error(MSG_valueWasNull);
 		return returns();
 	}
 
@@ -338,7 +354,7 @@ public class FluentObjectAssertion<T,R> extends FluentAssertion<R> {
 	 */
 	public R isNull() throws AssertionError {
 		if (value != null)
-			throw error("Value was not null.");
+			throw error(MSG_valueWasNotNull);
 		return returns();
 	}
 
@@ -367,7 +383,7 @@ public class FluentObjectAssertion<T,R> extends FluentAssertion<R> {
 		for (Object v : values)
 			if (value().equals(equivalent(v)))
 				return returns();
-		throw error("Expected value not found.\n\tExpect=[{0}]\n\tActual=[{1}]", values, value);
+		throw error(MSG_expectedValueNotFound, values, value);
 	}
 
 	/**
@@ -380,7 +396,7 @@ public class FluentObjectAssertion<T,R> extends FluentAssertion<R> {
 	public R isNotAny(Object...values) throws AssertionError {
 		for (Object v : values)
 			if (value().equals(equivalent(v)))
-				throw error("Unexpected value found.\n\tUnexpected=[{0}]\n\tActual=[{1}]", v, value);
+				throw error(MSG_unexpectedValueFound, v, value);
 		return returns();
 	}
 
@@ -431,7 +447,7 @@ public class FluentObjectAssertion<T,R> extends FluentAssertion<R> {
 		Object o = value;
 		if (o == null || c.isInstance(o))
 			return (T2)o;
-		throw new BasicAssertionError("Object was not type ''{0}''.  Actual=''{1}''", ClassInfo.of(c).getFullName(), o.getClass());
+		throw new BasicAssertionError(MSG_objectWasNotType, ClassInfo.of(c).getFullName(), o.getClass());
 	}
 
 	/**
@@ -763,7 +779,7 @@ public class FluentObjectAssertion<T,R> extends FluentAssertion<R> {
 	protected String getFailureMessage(Predicate<?> p, Object value) {
 		if (p instanceof AssertionPredicate)
 			return ((AssertionPredicate<?>)p).getFailureMessage();
-		return format("Unexpected value: ''{0}''", value);
+		return format(MSG_unexpectedValue2, value);
 	}
 
 	/**
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/assertions/FluentPrimitiveArrayAssertion.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/assertions/FluentPrimitiveArrayAssertion.java
index 71d823c..59adaa3 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/assertions/FluentPrimitiveArrayAssertion.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/assertions/FluentPrimitiveArrayAssertion.java
@@ -21,6 +21,7 @@ import java.util.*;
 import java.util.function.*;
 
 import org.apache.juneau.*;
+import org.apache.juneau.cp.*;
 import org.apache.juneau.internal.*;
 
 /**
@@ -44,6 +45,16 @@ public class FluentPrimitiveArrayAssertion<T,R> extends FluentObjectAssertion<T,
 		STRINGIFIERS.put(short.class, (x) -> Arrays.toString((short[])x));
 	}
 
+	private static final Messages MESSAGES = Messages.of(FluentPrimitiveArrayAssertion.class, "Messages");
+	static final String
+		MSG_objectWasNotAnArray = MESSAGES.getString("objectWasNotAnArray"),
+		MSG_arrayWasNotEmpty = MESSAGES.getString("arrayWasNotEmpty"),
+		MSG_arrayWasEmpty = MESSAGES.getString("arrayWasEmpty"),
+		MSG_arrayDidNotHaveExpectedSize = MESSAGES.getString("arrayDidNotHaveExpectedSize"),
+		MSG_arrayDidNotContainExpectedValue = MESSAGES.getString("arrayDidNotContainExpectedValue"),
+		MSG_arrayDidNotContainExpectedValueAt = MESSAGES.getString("arrayDidNotContainExpectedValueAt"),
+		MSG_arrayContainedUnexpectedValue = MESSAGES.getString("arrayContainedUnexpectedValue");
+
 	/**
 	 * Constructor.
 	 *
@@ -64,7 +75,7 @@ public class FluentPrimitiveArrayAssertion<T,R> extends FluentObjectAssertion<T,
 	public FluentPrimitiveArrayAssertion(Assertion creator, T contents, R returns) {
 		super(creator, contents, returns);
 		if (contents != null && ! contents.getClass().isArray())
-			throw new BasicAssertionError("Object was not an array.  Actual=''{0}''", contents.getClass());
+			throw new BasicAssertionError(MSG_objectWasNotAnArray, contents.getClass());
 	}
 
 	@Override /* FluentBaseAssertion */
@@ -80,7 +91,7 @@ public class FluentPrimitiveArrayAssertion<T,R> extends FluentObjectAssertion<T,
 	 */
 	public R isEmpty() throws AssertionError {
 		if (length() != 0)
-			throw error("Array was not empty.");
+			throw error(MSG_arrayWasNotEmpty);
 		return returns();
 	}
 
@@ -92,7 +103,7 @@ public class FluentPrimitiveArrayAssertion<T,R> extends FluentObjectAssertion<T,
 	 */
 	public R isNotEmpty() throws AssertionError {
 		if (length() == 0)
-			throw error("Array was empty.");
+			throw error(MSG_arrayWasEmpty);
 		return returns();
 	}
 
@@ -105,7 +116,7 @@ public class FluentPrimitiveArrayAssertion<T,R> extends FluentObjectAssertion<T,
 	 */
 	public R isSize(int size) throws AssertionError {
 		if (length() != size)
-			throw error("Array did not have the expected size.  Expect={0}, Actual={1}.", size, length());
+			throw error(MSG_arrayDidNotHaveExpectedSize, size, length());
 		return returns();
 	}
 
@@ -120,7 +131,7 @@ public class FluentPrimitiveArrayAssertion<T,R> extends FluentObjectAssertion<T,
 		for (int i = 0, j = length(); i < j; i++)
 			if (eq(at(i), entry))
 				return returns();
-		throw error("Array did not contain expected value.\n\tContents: {0}\n\tExpected: {1}", value(), entry);
+		throw error(MSG_arrayDidNotContainExpectedValue, entry, value());
 	}
 
 	/**
@@ -190,7 +201,7 @@ public class FluentPrimitiveArrayAssertion<T,R> extends FluentObjectAssertion<T,
 		for (int i = 0, j = length(); i < j; i++) {
 			Predicate<T> t = tests[i];
 			if (t != null && ! t.test(at(i)))
-				throw error("Array did not contain expected value at index {0}.\n\t{1}", i, getFailureMessage(t, at(i)));
+				throw error(MSG_arrayDidNotContainExpectedValueAt, i, getFailureMessage(t, at(i)));
 		}
 		return returns();
 	}
@@ -205,7 +216,7 @@ public class FluentPrimitiveArrayAssertion<T,R> extends FluentObjectAssertion<T,
 	public R doesNotContain(Object entry) throws AssertionError {
 		for (int i = 0; i < length(); i++)
 			if (eq(at(i), entry))
-				throw error("Array contained unexpected value.\n\tContents: {0}\n\tUnexpected: {1}", value(), entry);
+				throw error(MSG_arrayContainedUnexpectedValue, entry, value());
 		return returns();
 	}
 
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/assertions/FluentStringAssertion.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/assertions/FluentStringAssertion.java
index 3cf3c94..462b9f7 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/assertions/FluentStringAssertion.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/assertions/FluentStringAssertion.java
@@ -12,6 +12,7 @@
 // ***************************************************************************************************************************
 package org.apache.juneau.assertions;
 
+import static org.apache.juneau.assertions.Assertions.*;
 import static org.apache.juneau.internal.StringUtils.*;
 import static java.util.stream.Collectors.*;
 import static java.util.Arrays.*;
@@ -21,6 +22,7 @@ import java.util.*;
 import java.util.function.*;
 import java.util.regex.*;
 
+import org.apache.juneau.cp.*;
 import org.apache.juneau.internal.*;
 
 /**
@@ -40,6 +42,23 @@ import org.apache.juneau.internal.*;
 @FluentSetters(returns="FluentStringAssertion<R>")
 public class FluentStringAssertion<R> extends FluentObjectAssertion<String,R> {
 
+	private static final Messages MESSAGES = Messages.of(FluentStringAssertion.class, "Messages");
+	static final String
+		MSG_stringDifferedAtPosition = MESSAGES.getString("stringDifferedAtPosition"),
+		MSG_expectedStringHadDifferentNumbersOfLines = MESSAGES.getString("expectedStringHadDifferentNumbersOfLines"),
+		MSG_expectedStringHadDifferentValuesAtLine = MESSAGES.getString("expectedStringHadDifferentValuesAtLine"),
+		MSG_unexpectedValue = MESSAGES.getString("unexpectedValue"),
+		MSG_stringEqualedUnexpected = MESSAGES.getString("stringEqualedUnexpected"),
+		MSG_stringDidNotContainExpectedSubstring = MESSAGES.getString("stringDidNotContainExpectedSubstring"),
+		MSG_stringContainedUnexpectedSubstring = MESSAGES.getString("stringContainedUnexpectedSubstring"),
+		MSG_stringWasNotEmpty = MESSAGES.getString("stringWasNotEmpty"),
+		MSG_stringWasNull = MESSAGES.getString("stringWasNull"),
+		MSG_stringWasEmpty = MESSAGES.getString("stringWasEmpty"),
+		MSG_stringDidNotMatchExpectedPattern = MESSAGES.getString("stringDidNotMatchExpectedPattern"),
+		MSG_stringMatchedUnexpectedPattern = MESSAGES.getString("stringMatchedUnexpectedPattern"),
+		MSG_stringDidNotStartWithExpected = MESSAGES.getString("stringDidNotStartWithExpected"),
+		MSG_stringDidNotEndWithExpected = MESSAGES.getString("stringDidNotEndWithExpected");
+
 	private boolean javaStrings;
 
 	/**
@@ -86,8 +105,8 @@ public class FluentStringAssertion<R> extends FluentObjectAssertion<String,R> {
 	 * @return This object (for method chaining).
 	 */
 	public FluentStringAssertion<R> replaceAll(String regex, String replacement) {
-		assertNotNull("regex", regex);
-		assertNotNull("replacement", replacement);
+		assertArgNotNull("regex", regex);
+		assertArgNotNull("replacement", replacement);
 		return apply(x -> x == null ? null : x.replaceAll(regex, replacement));
 	}
 
@@ -99,8 +118,8 @@ public class FluentStringAssertion<R> extends FluentObjectAssertion<String,R> {
 	 * @return This object (for method chaining).
 	 */
 	public FluentStringAssertion<R> replace(String target, String replacement) {
-		assertNotNull("target", target);
-		assertNotNull("replacement", replacement);
+		assertArgNotNull("target", target);
+		assertArgNotNull("replacement", replacement);
 		return apply(x -> x == null ? null : x.replace(target, replacement));
 	}
 
@@ -174,7 +193,7 @@ public class FluentStringAssertion<R> extends FluentObjectAssertion<String,R> {
 	public R isEqualTo(String value) throws AssertionError {
 		String s = orElse(null);
 		if (ne(value, s))
-			throw error("Text differed at position {0}.\n\tExpect=[{1}]\n\tActual=[{2}]", diffPosition(value, s), fix(value), fix(s));
+			throw error(MSG_stringDifferedAtPosition, diffPosition(value, s), fix(value), fix(s));
 		return returns();
 	}
 
@@ -201,11 +220,11 @@ public class FluentStringAssertion<R> extends FluentObjectAssertion<String,R> {
 	 * @throws AssertionError If assertion failed.
 	 */
 	public R isEqualLinesTo(String...lines) throws AssertionError {
-		assertNotNull("lines", lines);
+		assertArgNotNull("lines", lines);
 		String v = join(lines, '\n');
 		String s = value();
 		if (ne(v, s))
-			throw error("Text differed at position {0}.\n\tExpect=[{1}]\n\tActual=[{2}]", diffPosition(v, s), fix(v), fix(s));
+			throw error(MSG_stringDifferedAtPosition, diffPosition(v, s), fix(v), fix(s));
 		return returns();
 	}
 
@@ -232,20 +251,20 @@ public class FluentStringAssertion<R> extends FluentObjectAssertion<String,R> {
 	 * @throws AssertionError If assertion failed.
 	 */
 	public R isEqualSortedLinesTo(String...lines) {
-		assertNotNull("lines", lines);
+		assertArgNotNull("lines", lines);
 
 		// Must work for windows too.
 		String[] e = StringUtils.join(lines, '\n').trim().split("[\r\n]+"), a = value().trim().split("[\r\n]+");
 
 		if (e.length != a.length)
-			throw error("Expected text had different numbers of lines.\n\tExpect=[{0}]\n\tActual=[{1}]", e.length, a.length);
+			throw error(MSG_expectedStringHadDifferentNumbersOfLines, e.length, a.length);
 
 		Arrays.sort(e);
 		Arrays.sort(a);
 
 		for (int i = 0; i < e.length; i++)
 			if (! e[i].equals(a[i]))
-				throw error("Expected text had different values at line {0}.\n\tExpect=[{1}]\n\tActual=[{2}]", i+1, e[i], a[i]);
+				throw error(MSG_expectedStringHadDifferentValuesAtLine, i+1, e[i], a[i]);
 
 		return returns();
 	}
@@ -274,7 +293,7 @@ public class FluentStringAssertion<R> extends FluentObjectAssertion<String,R> {
 	public R is(String value) throws AssertionError {
 		String s = orElse(null);
 		if (ne(value, s))
-			throw error("Unexpected value.\n\tExpect=[{0}]\n\tActual=[{1}]", fix(value), fix(s));
+			throw error(MSG_unexpectedValue, fix(value), fix(s));
 		return isEqualTo(value);
 	}
 
@@ -288,7 +307,7 @@ public class FluentStringAssertion<R> extends FluentObjectAssertion<String,R> {
 	public R isEqualIgnoreCaseTo(String value) throws AssertionError {
 		String s = orElse(null);
 		if (neic(value, s))
-			throw error("Text differed at position {0}.\n\tExpect=[{1}]\n\tActual=[{2}]", diffPositionIc(value, s), fix(value), fix(s));
+			throw error(MSG_stringDifferedAtPosition, diffPositionIc(value, s), fix(value), fix(s));
 		return returns();
 	}
 
@@ -302,7 +321,7 @@ public class FluentStringAssertion<R> extends FluentObjectAssertion<String,R> {
 	public R doesNotEqual(String value) throws AssertionError {
 		String s = orElse(null);
 		if (eq(value, s))
-			throw error("Text equaled unexpected.\n\tText=[{0}]", fix(s));
+			throw error(MSG_stringEqualedUnexpected, fix(s));
 		return returns();
 	}
 
@@ -330,7 +349,7 @@ public class FluentStringAssertion<R> extends FluentObjectAssertion<String,R> {
 	public R doesNotEqualIc(String value) throws AssertionError {
 		String s = orElse(null);
 		if (eqic(value, s))
-			throw error("Text equaled unexpected.\n\tText=[{0}]", fix(s));
+			throw error(MSG_stringEqualedUnexpected, fix(s));
 		return returns();
 	}
 
@@ -342,11 +361,11 @@ public class FluentStringAssertion<R> extends FluentObjectAssertion<String,R> {
 	 * @throws AssertionError If assertion failed.
 	 */
 	public R contains(String...values) throws AssertionError {
-		assertNotNull("values", values);
+		assertArgNotNull("values", values);
 		String s = orElse(null);
 		for (String substring : values)
 			if (substring != null && ! StringUtils.contains(s, substring))
-				throw error("Text did not contain expected substring.\n\tSubstring=[{0}]\n\tText=[{1}]", fix(substring), fix(s));
+				throw error(MSG_stringDidNotContainExpectedSubstring, fix(substring), fix(s));
 		return returns();
 	}
 
@@ -358,11 +377,11 @@ public class FluentStringAssertion<R> extends FluentObjectAssertion<String,R> {
 	 * @throws AssertionError If assertion failed.
 	 */
 	public R doesNotContain(String...values) throws AssertionError {
-		assertNotNull("values", values);
+		assertArgNotNull("values", values);
 		String s = orElse(null);
 		for (String substring : values)
 			if (substring != null && StringUtils.contains(s, substring))
-				throw error("Text contained unexpected substring.\n\tSubstring=[{0}]\n\tText=[{1}]", fix(substring), fix(s));
+				throw error(MSG_stringContainedUnexpectedSubstring, fix(substring), fix(s));
 		return returns();
 	}
 
@@ -375,7 +394,7 @@ public class FluentStringAssertion<R> extends FluentObjectAssertion<String,R> {
 	public R isEmpty() throws AssertionError {
 		String s = orElse(null);
 		if (s != null && ! s.isEmpty())
-			throw error("Text was not empty.\n\tText=[{0}]", fix(s));
+			throw error(MSG_stringWasNotEmpty, fix(s));
 		return returns();
 	}
 
@@ -388,9 +407,9 @@ public class FluentStringAssertion<R> extends FluentObjectAssertion<String,R> {
 	public R isNotEmpty() throws AssertionError {
 		String s = orElse(null);
 		if (s == null)
-			throw error("Text was null.");
+			throw error(MSG_stringWasNull);
 		if (s.isEmpty())
-			throw error("Text was empty.");
+			throw error(MSG_stringWasEmpty);
 		return returns();
 	}
 
@@ -416,7 +435,7 @@ public class FluentStringAssertion<R> extends FluentObjectAssertion<String,R> {
 	 * @throws AssertionError If assertion failed.
 	 */
 	public R matchesSimple(String searchPattern) throws AssertionError {
-		assertNotNull("searchPattern", searchPattern);
+		assertArgNotNull("searchPattern", searchPattern);
 		return matches(getMatchPattern(searchPattern));
 	}
 
@@ -428,7 +447,7 @@ public class FluentStringAssertion<R> extends FluentObjectAssertion<String,R> {
 	 * @throws AssertionError If assertion failed.
 	 */
 	public R doesNotMatch(String regex) throws AssertionError {
-		assertNotNull("regex", regex);
+		assertArgNotNull("regex", regex);
 		return doesNotMatch(regex, 0);
 	}
 
@@ -441,11 +460,11 @@ public class FluentStringAssertion<R> extends FluentObjectAssertion<String,R> {
 	 * @throws AssertionError If assertion failed.
 	 */
 	public R matches(String regex, int flags) throws AssertionError {
-		assertNotNull("regex", regex);
+		assertArgNotNull("regex", regex);
 		Pattern p = Pattern.compile(regex, flags);
 		String s = value();
 		if (! p.matcher(s).matches())
-			throw error("Text did not match expected pattern.\n\tPattern=[{0}]\n\tText=[{1}]", fix(regex), fix(s));
+			throw error(MSG_stringDidNotMatchExpectedPattern, fix(regex), fix(s));
 		return returns();
 	}
 
@@ -458,7 +477,7 @@ public class FluentStringAssertion<R> extends FluentObjectAssertion<String,R> {
 	 * @throws AssertionError If assertion failed.
 	 */
 	public R doesNotMatch(String regex, int flags) throws AssertionError {
-		assertNotNull("regex", regex);
+		assertArgNotNull("regex", regex);
 		return doesNotMatch(Pattern.compile(regex, flags));
 	}
 
@@ -470,10 +489,10 @@ public class FluentStringAssertion<R> extends FluentObjectAssertion<String,R> {
 	 * @throws AssertionError If assertion failed.
 	 */
 	public R matches(Pattern pattern) throws AssertionError {
-		assertNotNull("pattern", pattern);
+		assertArgNotNull("pattern", pattern);
 		String s = value();
 		if (! pattern.matcher(s).matches())
-			throw error("Text did not match expected pattern.\n\tPattern=[{0}]\n\tText=[{1}]", fix(pattern.pattern()), fix(s));
+			throw error(MSG_stringDidNotMatchExpectedPattern, fix(pattern.pattern()), fix(s));
 		return returns();
 	}
 
@@ -485,10 +504,10 @@ public class FluentStringAssertion<R> extends FluentObjectAssertion<String,R> {
 	 * @throws AssertionError If assertion failed.
 	 */
 	public R doesNotMatch(Pattern pattern) throws AssertionError {
-		assertNotNull("pattern", pattern);
+		assertArgNotNull("pattern", pattern);
 		String s = orElse(null);
 		if (s != null && pattern.matcher(s).matches())
-			throw error("Text matched unexpected pattern.\n\tPattern=[{0}]\n\tText=[{1}]", fix(pattern.pattern()), fix(s));
+			throw error(MSG_stringMatchedUnexpectedPattern, fix(pattern.pattern()), fix(s));
 		return returns();
 	}
 
@@ -500,10 +519,10 @@ public class FluentStringAssertion<R> extends FluentObjectAssertion<String,R> {
 	 * @throws AssertionError If assertion failed.
 	 */
 	public R startsWith(String string) {
-		assertNotNull("string", string);
+		assertArgNotNull("string", string);
 		String s = value();
 		if (! s.startsWith(string))
-			throw error("Text did not start with expected string.\n\tString=[{0}]\n\tText=[{1}]", fix(string), fix(s));
+			throw error(MSG_stringDidNotStartWithExpected, fix(string), fix(s));
 		return returns();
 	}
 
@@ -515,10 +534,10 @@ public class FluentStringAssertion<R> extends FluentObjectAssertion<String,R> {
 	 * @throws AssertionError If assertion failed.
 	 */
 	public R endsWith(String string) {
-		assertNotNull("string", string);
+		assertArgNotNull("string", string);
 		String s = value();
 		if (! s.endsWith(string))
-			throw error("Text did not end with expected string.\n\tString=[{0}]\n\tText=[{1}]", fix(string), fix(s));
+			throw error(MSG_stringDidNotEndWithExpected, fix(string), fix(s));
 		return returns();
 	}
 
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/assertions/FluentThrowableAssertion.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/assertions/FluentThrowableAssertion.java
index 485c491..a5164c8 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/assertions/FluentThrowableAssertion.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/assertions/FluentThrowableAssertion.java
@@ -12,8 +12,11 @@
 // ***************************************************************************************************************************
 package org.apache.juneau.assertions;
 
+import static org.apache.juneau.assertions.Assertions.*;
+
 import java.io.*;
 
+import org.apache.juneau.cp.*;
 import org.apache.juneau.internal.*;
 
 /**
@@ -25,6 +28,14 @@ import org.apache.juneau.internal.*;
 @FluentSetters(returns="FluentThrowableAssertion<T,R>")
 public class FluentThrowableAssertion<T extends Throwable,R> extends FluentObjectAssertion<T,R> {
 
+	private static final Messages MESSAGES = Messages.of(FluentThrowableAssertion.class, "Messages");
+	static final String
+		MSG_exceptionWasNotExpectedType = MESSAGES.getString("exceptionWasNotExpectedType"),
+		MSG_exceptionMessageDidNotContainExpectedSubstring = MESSAGES.getString("exceptionMessageDidNotContainExpectedSubstring"),
+		MSG_exceptionWasNotThrown = MESSAGES.getString("exceptionWasNotThrown"),
+		MSG_exceptionWasThrown = MESSAGES.getString("exceptionWasThrown"),
+		MSG_causedByExceptionNotExpectedType = MESSAGES.getString("causedByExceptionNotExpectedType");
+
 	/**
 	 * Constructor.
 	 *
@@ -52,7 +63,7 @@ public class FluentThrowableAssertion<T extends Throwable,R> extends FluentObjec
 	 * <h5 class='section'>Example:</h5>
 	 * <p class='bcode w800'>
 	 * 	<jc>// Asserts that the specified method throws a RuntimeException. </jc>
-	 * 	ThrowableAssertion.<jsm>assertThrown</jsm>(() -&gt; {foo.getBar();})
+	 * 	ThrowableAssertion.<jsm>assertThrown</jsm>(() -&gt; {<jv>foo</jv>.getBar();})
 	 * 		.isType(RuntimeException.<jk>class</jk>);
 	 * </p>
 	 *
@@ -61,9 +72,29 @@ public class FluentThrowableAssertion<T extends Throwable,R> extends FluentObjec
 	 */
 	@Override
 	public R isType(Class<?> type) {
-		assertNotNull("type", type);
+		assertArgNotNull("type", type);
 		if (! type.isInstance(value()))
-			throw error("Exception was not expected type.\n\tExpect=[{0}]\n\tActual=[{1}]", className(type), className(value()));
+			throw error(MSG_exceptionWasNotExpectedType, className(type), className(value()));
+		return returns();
+	}
+
+	/**
+	 * Asserts that this throwable is exactly the specified type.
+	 *
+	 * <h5 class='section'>Example:</h5>
+	 * <p class='bcode w800'>
+	 * 	<jc>// Asserts that the specified method throws a RuntimeException. </jc>
+	 * 	ThrowableAssertion.<jsm>assertThrown</jsm>(() -&gt; {<jv>foo</jv>.getBar();})
+	 * 		.isExactType(RuntimeException.<jk>class</jk>);
+	 * </p>
+	 *
+	 * @param type The type.
+	 * @return This object (for method chaining).
+	 */
+	public R isExactType(Class<?> type) {
+		assertArgNotNull("type", type);
+		if (type != value().getClass())
+			throw error(MSG_exceptionWasNotExpectedType, className(type), className(value()));
 		return returns();
 	}
 
@@ -73,14 +104,14 @@ public class FluentThrowableAssertion<T extends Throwable,R> extends FluentObjec
 	 * <h5 class='section'>Example:</h5>
 	 * <p class='bcode w800'>
 	 * 	<jc>// Asserts that the specified method throws an exception with 'foobar' somewhere in the messages. </jc>
-	 * 	ThrowableAssertion.<jsm>assertThrown</jsm>(() -&gt; {foo.getBar();}).contains(<js>"foobar"</js>);
+	 * 	ThrowableAssertion.<jsm>assertThrown</jsm>(() -&gt; {<jv>foo</jv>.getBar();}).contains(<js>"foobar"</js>);
 	 * </p>
 	 *
 	 * @param substrings The substrings to check for.
 	 * @return This object (for method chaining).
 	 */
 	public R contains(String...substrings) {
-		assertNotNull("substrings", substrings);
+		assertArgNotNull("substrings", substrings);
 		for (String substring : substrings) {
 			if (substring != null) {
 				Throwable e2 = value();
@@ -90,7 +121,7 @@ public class FluentThrowableAssertion<T extends Throwable,R> extends FluentObjec
 					e2 = e2.getCause();
 				}
 				if (! found) {
-					throw error("Exception message did not contain expected substring.\n\tSubstring=[{0}]\n\tText=[{1}]", substring, value().getMessage());
+					throw error(MSG_exceptionMessageDidNotContainExpectedSubstring, substring, value().getMessage());
 				}
 			}
 		}
@@ -103,7 +134,7 @@ public class FluentThrowableAssertion<T extends Throwable,R> extends FluentObjec
 	 * <h5 class='section'>Example:</h5>
 	 * <p class='bcode w800'>
 	 * 	<jc>// Asserts that the specified method throws an exception with the message 'foobar'.</jc>
-	 * 	ThrowableAssertion.<jsm>assertThrown</jsm>(() -&gt; {foo.getBar();}).is(<js>"foobar"</js>);
+	 * 	ThrowableAssertion.<jsm>assertThrown</jsm>(() -&gt; {<jv>foo</jv>.getBar();}).is(<js>"foobar"</js>);
 	 * </p>
 	 *
 	 * @param msg The message to check for.
@@ -119,7 +150,7 @@ public class FluentThrowableAssertion<T extends Throwable,R> extends FluentObjec
 	 * <h5 class='section'>Example:</h5>
 	 * <p class='bcode w800'>
 	 * 	<jc>// Asserts that the specified method throws any exception.</jc>
-	 * 	ThrowableAssertion.<jsm>assertThrown</jsm>(() -&gt; {foo.getBar();}).exists();
+	 * 	ThrowableAssertion.<jsm>assertThrown</jsm>(() -&gt; {<jv>foo</jv>.getBar();}).exists();
 	 * </p>
 	 *
 	 * @return This object (for method chaining).
@@ -127,7 +158,7 @@ public class FluentThrowableAssertion<T extends Throwable,R> extends FluentObjec
 	@Override
 	public R exists() {
 		if (valueIsNull())
-			throw error("Exception was not thrown.");
+			throw error(MSG_exceptionWasNotThrown);
 		return returns();
 	}
 
@@ -137,7 +168,7 @@ public class FluentThrowableAssertion<T extends Throwable,R> extends FluentObjec
 	 * <h5 class='section'>Example:</h5>
 	 * <p class='bcode w800'>
 	 * 	<jc>// Asserts that the specified method doesn't throw any exception.</jc>
-	 * 	ThrowableAssertion.<jsm>assertThrown</jsm>(() -&gt; {foo.getBar();}).notExists();
+	 * 	ThrowableAssertion.<jsm>assertThrown</jsm>(() -&gt; {<jv>foo</jv>.getBar();}).notExists();
 	 * </p>
 	 *
 	 * @return This object (for method chaining).
@@ -145,7 +176,7 @@ public class FluentThrowableAssertion<T extends Throwable,R> extends FluentObjec
 	@Override
 	public R doesNotExist() {
 		if (valueIsNotNull())
-			throw error("Exception was thrown.");
+			throw error(MSG_exceptionWasThrown);
 		return returns();
 	}
 
@@ -155,7 +186,7 @@ public class FluentThrowableAssertion<T extends Throwable,R> extends FluentObjec
 	 * <h5 class='section'>Example:</h5>
 	 * <p class='bcode w800'>
 	 * 	<jc>// Asserts that the specified method throws an exception with 'foobar' somewhere in the messages. </jc>
-	 * 	ThrowableAssertion.<jsm>assertThrown</jsm>(() -&gt; {foo.getBar();}).message().matches(<js>".*foobar.*"</js>);
+	 * 	ThrowableAssertion.<jsm>assertThrown</jsm>(() -&gt; {<jv>foo</jv>.getBar();}).message().matches(<js>".*foobar.*"</js>);
 	 * </p>
 	 *
 	 * @return An assertion against the throwable message.  Never <jk>null</jk>.
@@ -170,7 +201,7 @@ public class FluentThrowableAssertion<T extends Throwable,R> extends FluentObjec
 	 * <h5 class='section'>Example:</h5>
 	 * <p class='bcode w800'>
 	 * 	<jc>// Asserts that the specified method throws an exception with 'foobar' somewhere in the localized messages. </jc>
-	 * 	ThrowableAssertion.<jsm>assertThrown</jsm>(() -&gt; {foo.getBar();}).localizedMessage().matches(<js>".*foobar.*"</js>);
+	 * 	ThrowableAssertion.<jsm>assertThrown</jsm>(() -&gt; {<jv>foo</jv>.getBar();}).localizedMessage().matches(<js>".*foobar.*"</js>);
 	 * </p>
 	 *
 	 * @return An assertion against the throwable localized message.  Never <jk>null</jk>.
@@ -185,7 +216,7 @@ public class FluentThrowableAssertion<T extends Throwable,R> extends FluentObjec
 	 * <h5 class='section'>Example:</h5>
 	 * <p class='bcode w800'>
 	 * 	<jc>// Asserts that the specified method throws an exception with 'foobar' somewhere in the stack trace. </jc>
-	 * 	ThrowableAssertion.<jsm>assertThrown</jsm>(() -&gt; {foo.getBar();}).stackTrace().contains(<js>"foobar"</js>);
+	 * 	ThrowableAssertion.<jsm>assertThrown</jsm>(() -&gt; {<jv>foo</jv>.getBar();}).stackTrace().contains(<js>"foobar"</js>);
 	 * </p>
 	 *
 	 * @return An assertion against the throwable stacktrace.  Never <jk>null</jk>.
@@ -200,7 +231,7 @@ public class FluentThrowableAssertion<T extends Throwable,R> extends FluentObjec
 	 * <h5 class='section'>Example:</h5>
 	 * <p class='bcode w800'>
 	 * 	<jc>// Asserts that the specified method throws an exception whose caused-by message contains 'foobar'. </jc>
-	 * 	ThrowableAssertion.<jsm>assertThrown</jsm>(() -&gt; {foo.getBar();}).causedBy().message().contains(<js>"foobar"</js>);
+	 * 	ThrowableAssertion.<jsm>assertThrown</jsm>(() -&gt; {<jv>foo</jv>.getBar();}).causedBy().message().contains(<js>"foobar"</js>);
 	 * </p>
 	 *
 	 * @return An assertion against the caused-by.  Never <jk>null</jk>.
@@ -215,7 +246,7 @@ public class FluentThrowableAssertion<T extends Throwable,R> extends FluentObjec
 	 * <h5 class='section'>Example:</h5>
 	 * <p class='bcode w800'>
 	 * 	<jc>// Asserts that the specified method throws an exception whose caused-by message contains 'foobar'. </jc>
-	 * 	ThrowableAssertion.<jsm>assertThrown</jsm>(() -&gt; {foo.getBar();}).causedBy().message().contains(<js>"foobar"</js>);
+	 * 	ThrowableAssertion.<jsm>assertThrown</jsm>(() -&gt; {<jv>foo</jv>.getBar();}).causedBy().message().contains(<js>"foobar"</js>);
 	 * </p>
 	 *
 	 * @param type The expected exception type.
@@ -226,7 +257,7 @@ public class FluentThrowableAssertion<T extends Throwable,R> extends FluentObjec
 		Throwable t = map(Throwable::getCause).orElse(null);
 		if (t == null || type.isInstance(t))
 			return new FluentThrowableAssertion<>(this, (E)t, returns());
-		throw error("Caused-by exception not of expected type.\n\tExpected: {1}.\n\tActual: {2}", type, t.getClass());
+		throw error(MSG_causedByExceptionNotExpectedType, type, t.getClass());
 	}
 
 	/**
@@ -235,7 +266,7 @@ public class FluentThrowableAssertion<T extends Throwable,R> extends FluentObjec
 	 * <h5 class='section'>Example:</h5>
 	 * <p class='bcode w800'>
 	 * 	<jc>// Asserts that the specified method throws an exception with a caused-by RuntimeException containing 'foobar'</jc>
-	 * 	ThrowableAssertion.<jsm>assertThrown</jsm>(() -&gt; {foo.getBar();}).causedBy(RuntimeException.<jk>class</jk>).exists().contains(<js>"foobar"</js>);
+	 * 	ThrowableAssertion.<jsm>assertThrown</jsm>(() -&gt; {<jv>foo</jv>.getBar();}).causedBy(RuntimeException.<jk>class</jk>).exists().contains(<js>"foobar"</js>);
 	 * </p>
 	 *
 	 * @param throwableClass The class type to search for in the caused-by chain.
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/assertions/FluentZonedDateTimeAssertion.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/assertions/FluentZonedDateTimeAssertion.java
index bdf0acc..67597c4 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/assertions/FluentZonedDateTimeAssertion.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/assertions/FluentZonedDateTimeAssertion.java
@@ -12,12 +12,14 @@
 // ***************************************************************************************************************************
 package org.apache.juneau.assertions;
 
+import static org.apache.juneau.assertions.Assertions.*;
 import static org.apache.juneau.internal.ObjectUtils.*;
 
 import java.io.*;
 import java.time.*;
 import java.time.temporal.*;
 
+import org.apache.juneau.cp.*;
 import org.apache.juneau.internal.*;
 
 /**
@@ -37,6 +39,12 @@ import org.apache.juneau.internal.*;
 @FluentSetters(returns="FluentZonedDateTimeAssertion<R>")
 public class FluentZonedDateTimeAssertion<R> extends FluentComparableAssertion<ZonedDateTime,R> {
 
+	private static final Messages MESSAGES = Messages.of(FluentZonedDateTimeAssertion.class, "Messages");
+	static final String
+		MSG_unexpectedValue = MESSAGES.getString("unexpectedValue"),
+		MSG_valueWasNotAfterExpected = MESSAGES.getString("valueWasNotAfterExpected"),
+		MSG_valueWasNotBeforeExpected = MESSAGES.getString("valueWasNotBeforeExpected");
+
 	/**
 	 * Constructor.
 	 *
@@ -69,7 +77,7 @@ public class FluentZonedDateTimeAssertion<R> extends FluentComparableAssertion<Z
 	public R isEqual(ZonedDateTime value, ChronoUnit precision) throws AssertionError {
 		ZonedDateTime v = orElse(null);
 		if (ne(v, value, (x,y)->x.toInstant().truncatedTo(precision).equals(y.toInstant().truncatedTo(precision))))
-			throw error("Unexpected value.\n\tExpect=[{0}]\n\tActual=[{1}]", value, v);
+			throw error(MSG_unexpectedValue, value, v);
 		return returns();
 	}
 
@@ -81,9 +89,9 @@ public class FluentZonedDateTimeAssertion<R> extends FluentComparableAssertion<Z
 	 * @throws AssertionError If assertion failed.
 	 */
 	public R isAfter(ZonedDateTime value) throws AssertionError {
-		assertNotNull("value", value);
+		assertArgNotNull("value", value);
 		if (! (value().isAfter(value)))
-			throw error("Value was not after expected.\n\tExpect=[{0}]\n\tActual=[{1}]", value, value());
+			throw error(MSG_valueWasNotAfterExpected, value, value());
 		return returns();
 	}
 
@@ -105,9 +113,9 @@ public class FluentZonedDateTimeAssertion<R> extends FluentComparableAssertion<Z
 	 * @throws AssertionError If assertion failed.
 	 */
 	public R isBefore(ZonedDateTime value) throws AssertionError {
-		assertNotNull("value", value);
+		assertArgNotNull("value", value);
 		if (! (value().isBefore(value)))
-			throw error("Value was not before expected.\n\tExpect=[{0}]\n\tActual=[{1}]", value, value());
+			throw error(MSG_valueWasNotBeforeExpected, value, value());
 		return returns();
 	}
 
@@ -131,8 +139,8 @@ public class FluentZonedDateTimeAssertion<R> extends FluentComparableAssertion<Z
 	 */
 	public R isBetween(ZonedDateTime lower, ZonedDateTime upper) throws AssertionError {
 		exists();
-		assertNotNull("lower", lower);
-		assertNotNull("upper", upper);
+		assertArgNotNull("lower", lower);
+		assertArgNotNull("upper", upper);
 		isBefore(upper);
 		isAfter(lower);
 		return returns();
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/assertions/Verify.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/assertions/Verify.java
index 16590be..aa50a08 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/assertions/Verify.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/assertions/Verify.java
@@ -14,6 +14,7 @@ package org.apache.juneau.assertions;
 
 import java.util.function.*;
 
+import org.apache.juneau.cp.*;
 import org.apache.juneau.internal.*;
 
 /**
@@ -33,6 +34,11 @@ import org.apache.juneau.internal.*;
  */
 public class Verify {
 
+	private static final Messages MESSAGES = Messages.of(Verify.class, "Messages");
+	static final String
+		MSG_unexpectedType = MESSAGES.getString("unexpectedType"),
+		MSG_unexpectedValue = MESSAGES.getString("unexpectedValue");
+
 	private final Object o;
 	private Supplier<String> msg;
 
@@ -78,7 +84,7 @@ public class Verify {
 			return null;
 		if (type != null && type.isInstance(o))
 			return null;
-		return msg != null ? msg.get() : StringUtils.format("Expected type [{0}] but was [{1}].", type, (o == null ? null : o.getClass()));
+		return msg != null ? msg.get() : StringUtils.format(MSG_unexpectedType, type, (o == null ? null : o.getClass()));
 	}
 
 	/**
@@ -91,9 +97,9 @@ public class Verify {
 		if (expected == o)
 			return null;
 		if (expected == null || o == null)
-			return msg != null ? msg.get() : StringUtils.format("Expected [{0}] but was [{1}].", expected, o);
+			return msg != null ? msg.get() : StringUtils.format(MSG_unexpectedValue, expected, o);
 		if (! expected.equals(o))
-			return msg != null ? msg.get() : StringUtils.format("Expected [{0}] but was [{1}].", expected, o);
+			return msg != null ? msg.get() : StringUtils.format(MSG_unexpectedValue, expected, o);
 		return null;
 	}
 
diff --git a/juneau-core/juneau-marshall/src/main/resources/org/apache/juneau/assertions/Messages.properties b/juneau-core/juneau-marshall/src/main/resources/org/apache/juneau/assertions/Messages.properties
new file mode 100644
index 0000000..e01e330
--- /dev/null
+++ b/juneau-core/juneau-marshall/src/main/resources/org/apache/juneau/assertions/Messages.properties
@@ -0,0 +1,113 @@
+# ***************************************************************************************************************************
+# * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.  See the NOTICE file *
+# * distributed with this work for additional information regarding copyright ownership.  The ASF licenses this file        *
+# * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance            * 
+# * with the License.  You may obtain a copy of the License at                                                              *
+# *                                                                                                                         *
+# *  http://www.apache.org/licenses/LICENSE-2.0                                                                             *
+# *                                                                                                                         *
+# * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an  *
+# * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the        *
+# * specific language governing permissions and limitations under the License.                                              *
+# *                                                                                                                         *
+# ***************************************************************************************************************************
+
+Assertion.causedBy = Caused by
+
+AssertionPredicate.valueDidNotPassTest = Value did not pass test.
+AssertionPredicate.valueDidNotPassTestWithValue = Value did not pass test.\n\tValue="{0}".
+AssertionPredicate.predicateTestFailed = Predicate test #{0} failed.
+AssertionPredicate.noPredicateTestsPassed = No predicate tests passed.
+AssertionPredicate.predicateTestsUnexpectedlyPassed = Predicate test unexpectedly passed.
+
+AssertionPredicates.valueWasNull = Value was null.
+AssertionPredicates.valueWasNotNull = Value was not null.
+AssertionPredicates.valueDidNotMatchExpected = Value did not match expected.\n\tExpect="{0}".\n\tActual="{1}".
+AssertionPredicates.valueUnexpectedlyMatched = Value unexpectedly matched.\n\tValue="{0}".
+AssertionPredicates.valueWasNotExpectedType = Value was not expected type.\n\tExpect="{0}".\n\tActual="{1}".
+AssertionPredicates.valueDidNotMatchPattern = Value did not match pattern.\n\tPattern="{0}".\n\tValue="{1}".
+
+Assertions.argumentCannotBeNull = Argument "{0}" cannot be null.
+
+FluentArrayAssertion.arrayWasNotEmpty = Array was not empty.
+FluentArrayAssertion.arrayWasEmpty = Array was empty.
+FluentArrayAssertion.arrayUnexpectedSize = Array did not have the expected size.\n\tExpect={0}.\n\tActual={1}.
+FluentArrayAssertion.arrayDidNotContainExpectedValue = Array did not contain expected value.\n\tExpect="{0}".\n\tActual="{1}".
+FluentArrayAssertion.arrayContainedUnexpectedValue = Array contained unexpected value.\n\tUnexpected="{0}".\n\tActual="{1}".
+FluentArrayAssertion.arrayDidNotContainExpectedValueAt = Array did not contain expected value at index {0}.\n\t{1}.
+
+FluentBooleanAssertion.valueWasFalse = Value was false.
+FluentBooleanAssertion.valueWasTrue = Value was true.
+
+FluentCollectionAssertion.collectionWasNotEmpty = Collection was not empty.
+FluentCollectionAssertion.collectionDidNotContainExpectedValue = Collection did not contain expected value.\n\tExpect="{0}".\n\tValue="{1}".
+FluentCollectionAssertion.collectionContainedUnexpectedValue = Collection contained unexpected value.\n\tUnexpected="{0}".\n\tValue="{1}".
+FluentCollectionAssertion.collectionWasEmpty = Collection was empty.
+FluentCollectionAssertion.collectionDidNotHaveExpectedSize = Collection did not have the expected size.\n\tExpect={0}.\n\tActual={1}.
+
+FluentComparableAssertion.valueWasNotGreaterThanExpected = Value was not greater than expected.\n\tExpect="{0}".\n\tActual="{1}".
+FluentComparableAssertion.valueWasNotGreaterOrEqualsToExpected = Value was not greater than or equals to expected.\n\tExpect="{0}".\n\tActual="{1}".
+FluentComparableAssertion.valueWasNotLessThanExpected = Value was not less than expected.\n\tExpect="{0}".\n\tActual="{1}".
+FluentComparableAssertion.valueWasNotLessOrEqualsToExpected = Value was not less than or equals to expected.\n\tExpect="{0}".\n\tActual="{1}".
+
+FluentDateAssertion.unexpectedValue = Unexpected value.\n\tExpect="{0}".\n\tActual="{1}".
+FluentDateAssertion.valueWasNotAfterExpected = Value was not after expected.\n\tExpect="{0}".\n\tActual="{1}".
+FluentDateAssertion.valueWasNotBeforeExpected = Value was not before expected.\n\tExpect=\"{0}".\n\tActual="{1}".
+
+FluentListAssertion.listDidNotContainExpectedValueAt = List did not contain expected value at index {0}.\n\t{1}
+
+FluentMapAssertion.mapWasNotEmpty = Map was not empty.
+FluentMapAssertion.mapDidNotContainExpectedKey = Map did not contain expected key.\n\tExpected key="{0}".\n\tValue="{1}".
+FluentMapAssertion.mapContainedUnexpectedKey = Map contained unexpected key.\n\tUnexpected key="{0}".\n\tValue="{1}".
+FluentMapAssertion.mapWasEmpty = Map was empty.
+FluentMapAssertion.mapDidNotHaveTheExpectedSize = Map did not have the expected size.\n\tExpect={0}.\n\tActual={1}.
+
+FluentObjectAssertion.unexpectedType = Unexpected type.\n\tExpect="{0}".\n\tActual="{1}".
+FluentObjectAssertion.unexpectedComparison = Unexpected comparison.\n\tExpect="{0}".\n\tActual="{1}".
+FluentObjectAssertion.unexpectedValue = Unexpected value.\n\tExpect="{0}".\n\tActual="{1}".
+FluentObjectAssertion.unexpectedValueDidNotExpect = Unexpected value.\n\tDid not expect="{0}".\n\tActual="{1}".
+FluentObjectAssertion.notTheSameValue = Not the same value.\n\tExpect="{0}".\n\tActual="{1}".
+FluentObjectAssertion.valueWasNull = Value was null.
+FluentObjectAssertion.valueWasNotNull = Value was not null.
+FluentObjectAssertion.expectedValueNotFound = Expected value not found.\n\tExpect="{0}".\n\tActual="{1}".
+FluentObjectAssertion.unexpectedValueFound = Unexpected value found.\n\tUnexpected="{0}".\n\tActual="{1}".
+FluentObjectAssertion.objectWasNotType = Object was not type "{0}".\n\tActual="{1}".
+FluentObjectAssertion.unexpectedValue2 = Unexpected value: "{0}".
+
+FluentPrimitiveArrayAssertion.objectWasNotAnArray = Object was not an array.\n\tActual="{0}".
+FluentPrimitiveArrayAssertion.arrayWasNotEmpty = Array was not empty.
+FluentPrimitiveArrayAssertion.arrayWasEmpty = Array was empty.
+FluentPrimitiveArrayAssertion.arrayDidNotHaveExpectedSize = Array did not have the expected size.\n\tExpect={0}.\n\tActual={1}.
+FluentPrimitiveArrayAssertion.arrayDidNotContainExpectedValue = Array did not contain expected value.\n\tExpect="{0}.\n\tActual="{1}"
+FluentPrimitiveArrayAssertion.arrayDidNotContainExpectedValueAt = Array did not contain expected value at index {0}.\n\t{1}
+FluentPrimitiveArrayAssertion.arrayContainedUnexpectedValue = Array contained unexpected value.\n\tUnexpected="{0}".\n\tActual="{1}".
+
+FluentStringAssertion.stringDifferedAtPosition = String differed at position {0}.\n\tExpect="{1}".\n\tActual="{2}".
+FluentStringAssertion.expectedStringHadDifferentNumbersOfLines = Expected string had different numbers of lines.\n\tExpect="{0}".\n\tActual="{1}".
+FluentStringAssertion.expectedStringHadDifferentValuesAtLine = Expected string had different values at line {0}.\n\tExpect="{1}".\n\tActual="{2}".
+FluentStringAssertion.unexpectedValue = Unexpected value.\n\tExpect="{0}".\n\tActual="{1}".
+FluentStringAssertion.stringEqualedUnexpected = String equaled unexpected.\n\tValue="{0}".
+FluentStringAssertion.stringDidNotContainExpectedSubstring = String did not contain expected substring.\n\tSubstring="{0}".\n\tValue="{1}".
+FluentStringAssertion.stringContainedUnexpectedSubstring = String contained unexpected substring.\n\tSubstring="{0}".\n\tValue="{1}".
+FluentStringAssertion.stringWasNotEmpty = String was not empty.\n\tValue="{0}".
+FluentStringAssertion.stringWasNull = String was null.
+FluentStringAssertion.stringWasEmpty = String was empty.
+FluentStringAssertion.stringDidNotMatchExpectedPattern = String did not match expected pattern.\n\tPattern="{0}".\n\tValue="{1}".
+FluentStringAssertion.stringMatchedUnexpectedPattern = String matched unexpected pattern.\n\tPattern="{0}".\n\tValue="{1}".
+FluentStringAssertion.stringDidNotStartWithExpected = String did not start with expected substring.\n\tSubstring="{0}".\n\tValue="{1}".
+FluentStringAssertion.stringDidNotEndWithExpected = String did not end with expected substring.\n\tSubstring="{0}".\n\tValue="{1}".
+
+FluentThrowableAssertion.exceptionWasNotExpectedType = Exception was not expected type.\n\tExpect="{0}".\n\tActual="{1}".
+FluentThrowableAssertion.exceptionMessageDidNotContainExpectedSubstring = Exception message did not contain expected substring.\n\tSubstring="{0}".\n\tValue="{1}".
+FluentThrowableAssertion.exceptionWasNotThrown = Exception was not thrown.
+FluentThrowableAssertion.exceptionWasThrown = Exception was thrown.
+FluentThrowableAssertion.causedByExceptionNotExpectedType = Caused-by exception not of expected type.\n\tExpected="{0}".\n\tActual="{1}".
+
+FluentZonedDateTimeAssertion.unexpectedValue = Unexpected value.\n\tExpect="{0}".\n\tActual="{1}".
+FluentZonedDateTimeAssertion.valueWasNotAfterExpected = Value was not after expected.\n\tExpect="{0}".\n\tActual="{1}".
+FluentZonedDateTimeAssertion.valueWasNotBeforeExpected = Value was not before expected.\n\tExpect="{0}".\n\tActual="{1}".
+
+Verify.unexpectedType = Expected type "{0}" but was "{1}".
+Verify.unexpectedValue = Expected "{0}" but was "{1}".
+
+
diff --git a/juneau-utest/src/test/java/org/apache/juneau/assertions/ArrayAssertion_Test.java b/juneau-utest/src/test/java/org/apache/juneau/assertions/ArrayAssertion_Test.java
index caddd01..51f663b 100644
--- a/juneau-utest/src/test/java/org/apache/juneau/assertions/ArrayAssertion_Test.java
+++ b/juneau-utest/src/test/java/org/apache/juneau/assertions/ArrayAssertion_Test.java
@@ -36,9 +36,9 @@ public class ArrayAssertion_Test {
 
 		assertThrown(()->test(null).isSize(0)).is("Value was null.");
 		test(x1).isSize(0);
-		assertThrown(()->test(x1).isSize(2)).is("Array did not have the expected size.  Expect=2, Actual=0.");
+		assertThrown(()->test(x1).isSize(2)).is("Array did not have the expected size.\n\tExpect=2.\n\tActual=0.");
 		test(x2).isSize(2);
-		assertThrown(()->test(x2).isSize(0)).is("Array did not have the expected size.  Expect=0, Actual=2.");
+		assertThrown(()->test(x2).isSize(0)).is("Array did not have the expected size.\n\tExpect=0.\n\tActual=2.");
 
 		assertThrown(()->test(null).isEmpty()).is("Value was null.");
 		test(x1).isEmpty();
@@ -53,16 +53,17 @@ public class ArrayAssertion_Test {
 		test(x2).item(0).exists();
 
 		test(x2).contains("foo");
-		assertThrown(()->test(x2).contains("z")).is("Array did not contain expected value.\n\tContents: [foo, bar]\n\tExpected: z");
+		assertThrown(()->test(x2).contains("z")).is("Array did not contain expected value.\n\tExpect=\"z\".\n\tActual=\"[foo, bar]\".");
 
 		test(x1).doesNotContain("foo");
-		assertThrown(()->test(x2).doesNotContain("foo")).is("Array contained unexpected value.\n\tContents: [foo, bar]\n\tUnexpected: foo");
-		assertThrown(()->test(x2).doesNotContain("bar")).is("Array contained unexpected value.\n\tContents: [foo, bar]\n\tUnexpected: bar");
+		assertThrown(()->test(x2).doesNotContain("foo")).is("Array contained unexpected value.\n\tUnexpected=\"foo\".\n\tActual=\"[foo, bar]\".");
+		assertThrown(()->test(x2).doesNotContain("bar")).is("Array contained unexpected value.\n\tUnexpected=\"bar\".\n\tActual=\"[foo, bar]\".");
 	}
 
 	@Test
 	public void a02_other() throws Exception {
 		assertThrown(()->test(null).msg("Foo {0}", 1).exists()).is("Foo 1");
+		assertThrown(()->test(null).msg("Foo {0}", 1).throwable(RuntimeException.class).exists()).isExactType(RuntimeException.class).is("Foo 1");
 		test(null).stdout();
 	}
 }
diff --git a/juneau-utest/src/test/java/org/apache/juneau/assertions/AssertionPredicates_Test.java b/juneau-utest/src/test/java/org/apache/juneau/assertions/AssertionPredicates_Test.java
new file mode 100644
index 0000000..742751e
--- /dev/null
+++ b/juneau-utest/src/test/java/org/apache/juneau/assertions/AssertionPredicates_Test.java
@@ -0,0 +1,157 @@
+// ***************************************************************************************************************************
+// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.  See the NOTICE file *
+// * distributed with this work for additional information regarding copyright ownership.  The ASF licenses this file        *
+// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance            *
+// * with the License.  You may obtain a copy of the License at                                                              *
+// *                                                                                                                         *
+// *  http://www.apache.org/licenses/LICENSE-2.0                                                                             *
+// *                                                                                                                         *
+// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an  *
+// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the License for the        *
+// * specific language governing permissions and limitations under the License.                                              *
+// ***************************************************************************************************************************
+package org.apache.juneau.assertions;
+
+import static org.apache.juneau.assertions.Assertions.*;
+import static org.apache.juneau.assertions.AssertionPredicates.*;
+import static org.junit.runners.MethodSorters.*;
+import static java.util.regex.Pattern.*;
+
+import java.util.*;
+import java.util.regex.*;
+
+import org.junit.*;
+
+@FixMethodOrder(NAME_ASCENDING)
+public class AssertionPredicates_Test {
+
+	private StringAssertion x1 = assertString("foo").silent(), x2 = assertString(Optional.empty()).silent();
+
+	@Test
+	public void a00_dummyConstructor() {
+		new AssertionPredicates();
+	}
+
+	@Test
+	public void a01_any() {
+		x1.passes(any());
+		x2.passes(any());
+	}
+
+	@Test
+	public void a02_notNull() {
+		x1.passes(notNull());
+		assertThrown(()->x2.passes(notNull())).is("Value was null.");
+	}
+
+	@Test
+	public void a03_isNull() {
+		assertThrown(()->x1.passes(isNull())).is("Value was not null.");
+		x2.passes(isNull());
+	}
+
+	@Test
+	public void a04_eq() {
+		x1.passes(eq("foo"));
+		x1.passes(eq((Object)"foo"));
+		assertThrown(()->x1.passes(eq("FOO"))).is("Value did not match expected.\n\tExpect=\"FOO\".\n\tActual=\"foo\".");
+		assertThrown(()->x1.passes(eq("bar"))).is("Value did not match expected.\n\tExpect=\"bar\".\n\tActual=\"foo\".");
+		assertThrown(()->x1.passes(eq((Object)"bar"))).is("Value did not match expected.\n\tExpect=\"bar\".\n\tActual=\"foo\".");
+		x2.passes(eq(null));
+		assertThrown(()->x2.passes(eq("foo"))).is("Value did not match expected.\n\tExpect=\"foo\".\n\tActual=\"null\".");
+	}
+
+	@Test
+	public void a05_eqic() {
+		x1.passes(eqic("foo"));
+		x1.passes(eqic("FOO"));
+		assertThrown(()->x1.passes(eqic("bar"))).is("Value did not match expected.\n\tExpect=\"bar\".\n\tActual=\"foo\".");
+		x2.passes(eqic(null));
+		assertThrown(()->x2.passes(eqic("bar"))).is("Value did not match expected.\n\tExpect=\"bar\".\n\tActual=\"null\".");
+	}
+
+	@Test
+	public void a06_ne() {
+		x1.passes(ne("bar"));
+		x1.passes(ne((Object)"bar"));
+		assertThrown(()->x1.passes(ne("foo"))).is("Value unexpectedly matched.\n\tValue=\"foo\".");
+		assertThrown(()->x1.passes(ne((Object)"foo"))).is("Value unexpectedly matched.\n\tValue=\"foo\".");
+		x2.passes(ne("bar"));
+		assertThrown(()->x2.passes(ne(null))).is("Value unexpectedly matched.\n\tValue=\"null\".");
+	}
+
+	@Test
+	public void a07_type() {
+		x1.passes(type(String.class));
+		x1.passes(type(Object.class));
+		assertThrown(()->x1.passes(type(Integer.class))).is("Value was not expected type.\n\tExpect=\"java.lang.Integer\".\n\tActual=\"java.lang.String\".");
+		assertThrown(()->x2.passes(type(String.class))).is("Value was not expected type.\n\tExpect=\"java.lang.String\".\n\tActual=\"null\".");
+	}
+
+	@Test
+	public void a08_exactType() {
+		x1.passes(exactType(String.class));
+		assertThrown(()->x1.passes(exactType(Object.class))).is("Value was not expected type.\n\tExpect=\"java.lang.Object\".\n\tActual=\"java.lang.String\".");
+		assertThrown(()->x2.passes(exactType(Object.class))).is("Value was not expected type.\n\tExpect=\"java.lang.Object\".\n\tActual=\"null\".");
+	}
+
+	@Test
+	public void a03_() {
+		x1.passes(match("fo*"));
+		assertThrown(()->x1.passes(match("ba*"))).is("Value did not match pattern.\n\tPattern=\"ba*\".\n\tValue=\"foo\".");
+		assertThrown(()->x2.passes(match("ba*"))).is("Value did not match pattern.\n\tPattern=\"ba*\".\n\tValue=\"null\".");
+	}
+
+
+	@Test
+	public void a10_regex() {
+		x1.passes(regex("fo.*"));
+		assertThrown(()->x1.passes(regex("ba.*"))).is("Value did not match pattern.\n\tPattern=\"ba.*\".\n\tValue=\"foo\".");
+		assertThrown(()->x2.passes(regex("ba.*"))).is("Value did not match pattern.\n\tPattern=\"ba.*\".\n\tValue=\"null\".");
+	}
+
+	@Test
+	public void a11_regex_wFlags() {
+		x1.passes(regex("FO.*", CASE_INSENSITIVE));
+		assertThrown(()->x1.passes(regex("BA.*", CASE_INSENSITIVE))).is("Value did not match pattern.\n\tPattern=\"BA.*\".\n\tValue=\"foo\".");
+		assertThrown(()->x2.passes(regex("BA.*", CASE_INSENSITIVE))).is("Value did not match pattern.\n\tPattern=\"BA.*\".\n\tValue=\"null\".");
+	}
+
+	@Test
+	public void a12_regex_wPattern() {
+		Pattern p1 = Pattern.compile("FO.*", CASE_INSENSITIVE), p2 = Pattern.compile("BA.*", CASE_INSENSITIVE);
+		x1.passes(regex(p1));
+		assertThrown(()->x1.passes(regex(p2))).is("Value did not match pattern.\n\tPattern=\"BA.*\".\n\tValue=\"foo\".");
+		assertThrown(()->x2.passes(regex(p2))).is("Value did not match pattern.\n\tPattern=\"BA.*\".\n\tValue=\"null\".");
+	}
+
+	@Test
+	public void a13_and() {
+		x1.passes(and(notNull(),eq("foo"),null,x->x.equals("foo")));
+		assertThrown(()->x1.passes(and(isNull()))).is("Predicate test #1 failed.\n\tValue was not null.");
+		assertThrown(()->x1.passes(and(x -> x.equals("bar")))).is("Predicate test #1 failed.");
+	}
+
+	@Test
+	public void a14_or() {
+		x1.passes(or(null,isNull(),eq("foo"),x->x.equals("bar")));
+		assertThrown(()->x1.passes(or(isNull()))).is("No predicate tests passed.");
+		assertThrown(()->x1.passes(or(x -> x.equals("bar")))).is("No predicate tests passed.");
+	}
+
+	@Test
+	public void a15_not() {
+		x1.passes(not(ne("foo")));
+		assertThrown(()->x2.passes(not(ne("foo")))).is("Predicate test unexpectedly passed.");
+		assertThrown(()->x1.passes(not(eq("foo")))).is("Predicate test unexpectedly passed.");
+		x2.passes(not(x -> x != null && x.equals("bar")));
+		x1.passes(not(null));
+		x2.passes(not(null));
+	}
+
+	@Test
+	public void a16_test() {
+		x1.passes(test(eq("foo")));
+		assertThrown(()->x1.passes(test(eq("bar")))).is("Value did not pass test.\n\tValue did not match expected.\n\tExpect=\"bar\".\n\tActual=\"foo\".");
+	}
+}
diff --git a/juneau-utest/src/test/java/org/apache/juneau/assertions/Assertions_Test.java b/juneau-utest/src/test/java/org/apache/juneau/assertions/Assertions_Test.java
index f705e8c..76ff892 100644
--- a/juneau-utest/src/test/java/org/apache/juneau/assertions/Assertions_Test.java
+++ b/juneau-utest/src/test/java/org/apache/juneau/assertions/Assertions_Test.java
@@ -79,6 +79,7 @@ public class Assertions_Test {
 	@Test
 	public void a02_stdout_stderr() throws Exception {
 		assertThrown(()->assertObject(null).msg("Test message").stdout().exists()).exists();
+		assertThrown(()->assertObject(Optional.empty()).msg("Foo {0}", 1).throwable(RuntimeException.class).exists()).isExactType(RuntimeException.class).is("Foo 1");
 		assertObject(assertObject("foo").silent().error("test {0}", "message").getMessage()).is("test message");
 	}
 }
diff --git a/juneau-utest/src/test/java/org/apache/juneau/assertions/BeanAssertion_Test.java b/juneau-utest/src/test/java/org/apache/juneau/assertions/BeanAssertion_Test.java
index db30047..56e1f94 100644
--- a/juneau-utest/src/test/java/org/apache/juneau/assertions/BeanAssertion_Test.java
+++ b/juneau-utest/src/test/java/org/apache/juneau/assertions/BeanAssertion_Test.java
@@ -49,6 +49,7 @@ public class BeanAssertion_Test {
 	@Test
 	public void a02_other() throws Exception {
 		assertThrown(()->test(null).msg("Foo {0}", 1).exists()).is("Foo 1");
+		assertThrown(()->test(null).msg("Foo {0}", 1).throwable(RuntimeException.class).exists()).isExactType(RuntimeException.class).is("Foo 1");
 		test(null).stdout();
 	}
 }
diff --git a/juneau-utest/src/test/java/org/apache/juneau/assertions/BooleanAssertion_Test.java b/juneau-utest/src/test/java/org/apache/juneau/assertions/BooleanAssertion_Test.java
index 61de9bf..1f28fc5 100644
--- a/juneau-utest/src/test/java/org/apache/juneau/assertions/BooleanAssertion_Test.java
+++ b/juneau-utest/src/test/java/org/apache/juneau/assertions/BooleanAssertion_Test.java
@@ -61,6 +61,7 @@ public class BooleanAssertion_Test {
 	@Test
 	public void a02_other() throws Exception {
 		assertThrown(()->test((Boolean)null).msg("Foo {0}", 1).exists()).is("Foo 1");
+		assertThrown(()->test((Boolean)null).msg("Foo {0}", 1).throwable(RuntimeException.class).exists()).isExactType(RuntimeException.class).is("Foo 1");
 		test((Boolean)null).stdout();
 	}
 }
diff --git a/juneau-utest/src/test/java/org/apache/juneau/assertions/ByteArrayAssertion_Test.java b/juneau-utest/src/test/java/org/apache/juneau/assertions/ByteArrayAssertion_Test.java
index 5509a32..0da9ccb 100644
--- a/juneau-utest/src/test/java/org/apache/juneau/assertions/ByteArrayAssertion_Test.java
+++ b/juneau-utest/src/test/java/org/apache/juneau/assertions/ByteArrayAssertion_Test.java
@@ -48,11 +48,11 @@ public class ByteArrayAssertion_Test {
 
 		assertThrown(()->test(empty()).isSize(0)).is("Value was null.");
 		test(x1).isSize(0);
-		assertThrown(()->test(x1).isSize(1)).is("Array did not have the expected size.  Expect=1, Actual=0.");
+		assertThrown(()->test(x1).isSize(1)).is("Array did not have the expected size.\n\tExpect=1.\n\tActual=0.");
 		test(x2).isSize(2);
-		assertThrown(()->test(of(x1)).isSize(1)).is("Array did not have the expected size.  Expect=1, Actual=0.");
+		assertThrown(()->test(of(x1)).isSize(1)).is("Array did not have the expected size.\n\tExpect=1.\n\tActual=0.");
 		test(x2).isSize(2);
-		assertThrown(()->test(x2).isSize(0)).is("Array did not have the expected size.  Expect=0, Actual=2.");
+		assertThrown(()->test(x2).isSize(0)).is("Array did not have the expected size.\n\tExpect=0.\n\tActual=2.");
 
 		assertThrown(()->test(empty()).isEmpty()).is("Value was null.");
 		test(x1).isEmpty();
@@ -69,27 +69,28 @@ public class ByteArrayAssertion_Test {
 		test(empty()).asString().isNull();
 		test(x1).asString().is("");
 		test(x2).asString().is("ab");
-		assertThrown(()->test(x2).asString().is("xx")).is("Unexpected value.\n\tExpect=[xx]\n\tActual=[ab]");
+		assertThrown(()->test(x2).asString().is("xx")).is("Unexpected value.\n\tExpect=\"xx\".\n\tActual=\"ab\".");
 
 		test(empty()).asBase64().isNull();
 		test(x1).asBase64().is("");
 		test(x2).asBase64().is("YWI=");
-		assertThrown(()->test(x2).asBase64().is("xx")).is("Unexpected value.\n\tExpect=[xx]\n\tActual=[YWI=]");
+		assertThrown(()->test(x2).asBase64().is("xx")).is("Unexpected value.\n\tExpect=\"xx\".\n\tActual=\"YWI=\".");
 
 		test(empty()).asHex().isNull();
 		test(x1).asHex().is("");
 		test(x2).asHex().is("6162");
-		assertThrown(()->test(x2).asHex().is("xx")).is("Unexpected value.\n\tExpect=[xx]\n\tActual=[6162]");
+		assertThrown(()->test(x2).asHex().is("xx")).is("Unexpected value.\n\tExpect=\"xx\".\n\tActual=\"6162\".");
 
 		test(empty()).asSpacedHex().isNull();
 		test(x1).asSpacedHex().is("");
 		test(x2).asSpacedHex().is("61 62");
-		assertThrown(()->test(x2).asSpacedHex().is("xx")).is("Unexpected value.\n\tExpect=[xx]\n\tActual=[61 62]");
+		assertThrown(()->test(x2).asSpacedHex().is("xx")).is("Unexpected value.\n\tExpect=\"xx\".\n\tActual=\"61 62\".");
 	}
 
 	@Test
 	public void a02_other() throws Exception {
 		assertThrown(()->test((byte[])null).msg("Foo {0}", 1).exists()).is("Foo 1");
+		assertThrown(()->test((byte[])null).msg("Foo {0}", 1).throwable(RuntimeException.class).exists()).isExactType(RuntimeException.class).is("Foo 1");
 		test((byte[])null).stdout();
 	}
 }
diff --git a/juneau-utest/src/test/java/org/apache/juneau/assertions/CollectionAssertion_Test.java b/juneau-utest/src/test/java/org/apache/juneau/assertions/CollectionAssertion_Test.java
index a280ca8..79e22aa 100644
--- a/juneau-utest/src/test/java/org/apache/juneau/assertions/CollectionAssertion_Test.java
+++ b/juneau-utest/src/test/java/org/apache/juneau/assertions/CollectionAssertion_Test.java
@@ -39,9 +39,9 @@ public class CollectionAssertion_Test {
 
 		assertThrown(()->test(null).isSize(0)).is("Value was null.");
 		test(x1).isSize(0);
-		assertThrown(()->test(x1).isSize(1)).is("Collection did not have the expected size.  Expect=1, Actual=0.");
+		assertThrown(()->test(x1).isSize(1)).is("Collection did not have the expected size.\n\tExpect=1.\n\tActual=0.");
 		test(x2).isSize(2);
-		assertThrown(()->test(x2).isSize(0)).is("Collection did not have the expected size.  Expect=0, Actual=2.");
+		assertThrown(()->test(x2).isSize(0)).is("Collection did not have the expected size.\n\tExpect=0.\n\tActual=2.");
 
 		assertThrown(()->test(null).isEmpty()).is("Value was null.");
 		test(x1).isEmpty();
@@ -52,15 +52,16 @@ public class CollectionAssertion_Test {
 		test(x2).isNotEmpty();
 
 		test(x2).contains("a");
-		assertThrown(()->test(x2).contains("z")).is("Collection did not contain expected value.\n\tContents: ['a','b']\n\tExpected: z");
+		assertThrown(()->test(x2).contains("z")).is("Collection did not contain expected value.\n\tExpect=\"z\".\n\tValue=\"['a','b']\".");
 
 		test(x1).doesNotContain("a");
-		assertThrown(()->test(x2).doesNotContain("a")).is("Collection contained unexpected value.\n\tContents: ['a','b']\n\tUnexpected: a");
+		assertThrown(()->test(x2).doesNotContain("a")).is("Collection contained unexpected value.\n\tUnexpected=\"a\".\n\tValue=\"['a','b']\".");
 	}
 
 	@Test
 	public void a02_other() throws Exception {
 		assertThrown(()->test(null).msg("Foo {0}", 1).exists()).is("Foo 1");
+		assertThrown(()->test(null).msg("Foo {0}", 1).throwable(RuntimeException.class).exists()).isExactType(RuntimeException.class).is("Foo 1");
 		test(null).stdout().silent();
 	}
 }
diff --git a/juneau-utest/src/test/java/org/apache/juneau/assertions/ComparableAssertion_Test.java b/juneau-utest/src/test/java/org/apache/juneau/assertions/ComparableAssertion_Test.java
index 01dd6de..5bf2aad 100644
--- a/juneau-utest/src/test/java/org/apache/juneau/assertions/ComparableAssertion_Test.java
+++ b/juneau-utest/src/test/java/org/apache/juneau/assertions/ComparableAssertion_Test.java
@@ -33,6 +33,7 @@ public class ComparableAssertion_Test {
 	@Test
 	public void a02_other() throws Exception {
 		assertThrown(()->test(null).msg("Foo {0}", 1).exists()).is("Foo 1");
+		assertThrown(()->test(null).msg("Foo {0}", 1).throwable(RuntimeException.class).exists()).isExactType(RuntimeException.class).is("Foo 1");
 		test(null).stdout().silent();
 	}
 }
diff --git a/juneau-utest/src/test/java/org/apache/juneau/assertions/DateAssertion_Test.java b/juneau-utest/src/test/java/org/apache/juneau/assertions/DateAssertion_Test.java
index 199cdd7..69ef66b 100644
--- a/juneau-utest/src/test/java/org/apache/juneau/assertions/DateAssertion_Test.java
+++ b/juneau-utest/src/test/java/org/apache/juneau/assertions/DateAssertion_Test.java
@@ -56,12 +56,12 @@ public class DateAssertion_Test {
 		assertThrown(()->test(x2).isEqual(x1, ChronoUnit.DAYS)).contains("Unexpected value.");
 
 		assertThrown(()->test(empty()).isBefore(x1)).is("Value was null.");
-		assertThrown(()->test(x1).isBefore(null)).is("Parameter 'value' cannot be null.");
+		assertThrown(()->test(x1).isBefore(null)).is("Argument \"value\" cannot be null.");
 		test(x1).isBefore(x2);
 		assertThrown(()->test(x2).isBefore(x1)).contains("Value was not before expected.");
 
 		assertThrown(()->test(empty()).isAfter(x1)).is("Value was null.");
-		assertThrown(()->test(x1).isAfter(null)).is("Parameter 'value' cannot be null.");
+		assertThrown(()->test(x1).isAfter(null)).is("Argument \"value\" cannot be null.");
 		test(x2).isAfter(x1);
 		assertThrown(()->test(x1).isAfter(x2)).contains("Value was not after expected.");
 
@@ -74,8 +74,8 @@ public class DateAssertion_Test {
 		assertThrown(()->test(x1).isAfterNow()).contains("Value was not after expected.");
 
 		assertThrown(()->test(empty()).isBetween(x1,x2)).is("Value was null.");
-		assertThrown(()->test(now).isBetween(null,x2)).is("Parameter 'lower' cannot be null.");
-		assertThrown(()->test(now).isBetween(x1,null)).is("Parameter 'upper' cannot be null.");
+		assertThrown(()->test(now).isBetween(null,x2)).is("Argument \"lower\" cannot be null.");
+		assertThrown(()->test(now).isBetween(x1,null)).is("Argument \"upper\" cannot be null.");
 		test(now).isBetween(x1, x2);
 		assertThrown(()->test(x1).isBetween(now,x2)).contains("Value was not after expected.");
 		assertThrown(()->test(x2).isBetween(x1,now)).contains("Value was not before expected.");
@@ -84,6 +84,7 @@ public class DateAssertion_Test {
 	@Test
 	public void a02_other() throws Exception {
 		assertThrown(()->test((Date)null).msg("Foo {0}", 1).exists()).is("Foo 1");
+		assertThrown(()->test((Date)null).msg("Foo {0}", 1).throwable(RuntimeException.class).exists()).isExactType(RuntimeException.class).is("Foo 1");
 		test((Date)null).stdout();
 	}
 }
diff --git a/juneau-utest/src/test/java/org/apache/juneau/assertions/IntegerAssertion_Test.java b/juneau-utest/src/test/java/org/apache/juneau/assertions/IntegerAssertion_Test.java
index 2d56968..27a2248 100644
--- a/juneau-utest/src/test/java/org/apache/juneau/assertions/IntegerAssertion_Test.java
+++ b/juneau-utest/src/test/java/org/apache/juneau/assertions/IntegerAssertion_Test.java
@@ -51,61 +51,61 @@ public class IntegerAssertion_Test {
 		test(empty()).isEqual(null);
 
 		assertThrown(()->test(empty()).isGreaterThan(1)).is("Value was null.");
-		assertThrown(()->test(1).isGreaterThan(null)).is("Parameter 'value' cannot be null.");
+		assertThrown(()->test(1).isGreaterThan(null)).is("Argument \"value\" cannot be null.");
 		test(2).isGreaterThan(1);
-		assertThrown(()->test(1).isGreaterThan(2)).is("Value was not greater than expected.\n\tExpect=[2]\n\tActual=[1]");
-		assertThrown(()->test(1).isGreaterThan(1)).is("Value was not greater than expected.\n\tExpect=[1]\n\tActual=[1]");
+		assertThrown(()->test(1).isGreaterThan(2)).is("Value was not greater than expected.\n\tExpect=\"2\".\n\tActual=\"1\".");
+		assertThrown(()->test(1).isGreaterThan(1)).is("Value was not greater than expected.\n\tExpect=\"1\".\n\tActual=\"1\".");
 
 		assertThrown(()->test(empty()).isGt(1)).is("Value was null.");
-		assertThrown(()->test(1).isGt(null)).is("Parameter 'value' cannot be null.");
+		assertThrown(()->test(1).isGt(null)).is("Argument \"value\" cannot be null.");
 		test(2).isGt(1);
-		assertThrown(()->test(1).isGt(2)).is("Value was not greater than expected.\n\tExpect=[2]\n\tActual=[1]");
-		assertThrown(()->test(1).isGt(1)).is("Value was not greater than expected.\n\tExpect=[1]\n\tActual=[1]");
+		assertThrown(()->test(1).isGt(2)).is("Value was not greater than expected.\n\tExpect=\"2\".\n\tActual=\"1\".");
+		assertThrown(()->test(1).isGt(1)).is("Value was not greater than expected.\n\tExpect=\"1\".\n\tActual=\"1\".");
 
 		assertThrown(()->test(empty()).isGreaterThanOrEqual(1)).is("Value was null.");
-		assertThrown(()->test(1).isGreaterThanOrEqual(null)).is("Parameter 'value' cannot be null.");
+		assertThrown(()->test(1).isGreaterThanOrEqual(null)).is("Argument \"value\" cannot be null.");
 		test(2).isGreaterThanOrEqual(1);
-		assertThrown(()->test(1).isGreaterThanOrEqual(2)).is("Value was not greater than or equals to expected.\n\tExpect=[2]\n\tActual=[1]");
+		assertThrown(()->test(1).isGreaterThanOrEqual(2)).is("Value was not greater than or equals to expected.\n\tExpect=\"2\".\n\tActual=\"1\".");
 		test(1).isGreaterThanOrEqual(1);
 
 		assertThrown(()->test(empty()).isGte(1)).is("Value was null.");
-		assertThrown(()->test(1).isGte(null)).is("Parameter 'value' cannot be null.");
+		assertThrown(()->test(1).isGte(null)).is("Argument \"value\" cannot be null.");
 		test(2).isGte(1);
-		assertThrown(()->test(1).isGte(2)).is("Value was not greater than or equals to expected.\n\tExpect=[2]\n\tActual=[1]");
+		assertThrown(()->test(1).isGte(2)).is("Value was not greater than or equals to expected.\n\tExpect=\"2\".\n\tActual=\"1\".");
 		test(1).isGte(1);
 
 		assertThrown(()->test(empty()).isLessThan(1)).is("Value was null.");
-		assertThrown(()->test(1).isLessThan(null)).is("Parameter 'value' cannot be null.");
+		assertThrown(()->test(1).isLessThan(null)).is("Argument \"value\" cannot be null.");
 		test(1).isLessThan(2);
-		assertThrown(()->test(2).isLessThan(1)).is("Value was not less than expected.\n\tExpect=[1]\n\tActual=[2]");
-		assertThrown(()->test(1).isLessThan(1)).is("Value was not less than expected.\n\tExpect=[1]\n\tActual=[1]");
+		assertThrown(()->test(2).isLessThan(1)).is("Value was not less than expected.\n\tExpect=\"1\".\n\tActual=\"2\".");
+		assertThrown(()->test(1).isLessThan(1)).is("Value was not less than expected.\n\tExpect=\"1\".\n\tActual=\"1\".");
 
 		assertThrown(()->test(empty()).isLt(1)).is("Value was null.");
-		assertThrown(()->test(1).isLt(null)).is("Parameter 'value' cannot be null.");
+		assertThrown(()->test(1).isLt(null)).is("Argument \"value\" cannot be null.");
 		test(1).isLt(2);
-		assertThrown(()->test(2).isLt(1)).is("Value was not less than expected.\n\tExpect=[1]\n\tActual=[2]");
-		assertThrown(()->test(1).isLt(1)).is("Value was not less than expected.\n\tExpect=[1]\n\tActual=[1]");
+		assertThrown(()->test(2).isLt(1)).is("Value was not less than expected.\n\tExpect=\"1\".\n\tActual=\"2\".");
+		assertThrown(()->test(1).isLt(1)).is("Value was not less than expected.\n\tExpect=\"1\".\n\tActual=\"1\".");
 
 		assertThrown(()->test(empty()).isLessThanOrEqual(1)).is("Value was null.");
-		assertThrown(()->test(1).isLessThanOrEqual(null)).is("Parameter 'value' cannot be null.");
+		assertThrown(()->test(1).isLessThanOrEqual(null)).is("Argument \"value\" cannot be null.");
 		test(1).isLessThanOrEqual(2);
-		assertThrown(()->test(2).isLessThanOrEqual(1)).is("Value was not less than or equals to expected.\n\tExpect=[1]\n\tActual=[2]");
+		assertThrown(()->test(2).isLessThanOrEqual(1)).is("Value was not less than or equals to expected.\n\tExpect=\"1\".\n\tActual=\"2\".");
 		test(1).isLessThanOrEqual(1);
 
 		assertThrown(()->test(empty()).isLte(1)).is("Value was null.");
-		assertThrown(()->test(1).isLte(null)).is("Parameter 'value' cannot be null.");
+		assertThrown(()->test(1).isLte(null)).is("Argument \"value\" cannot be null.");
 		test(1).isLte(2);
-		assertThrown(()->test(2).isLte(1)).is("Value was not less than or equals to expected.\n\tExpect=[1]\n\tActual=[2]");
+		assertThrown(()->test(2).isLte(1)).is("Value was not less than or equals to expected.\n\tExpect=\"1\".\n\tActual=\"2\".");
 		test(1).isLte(1);
 
 		assertThrown(()->test(empty()).isBetween(1,3)).is("Value was null.");
-		assertThrown(()->test(2).isBetween(null,3)).is("Parameter 'lower' cannot be null.");
-		assertThrown(()->test(2).isBetween(1,null)).is("Parameter 'upper' cannot be null.");
+		assertThrown(()->test(2).isBetween(null,3)).is("Argument \"lower\" cannot be null.");
+		assertThrown(()->test(2).isBetween(1,null)).is("Argument \"upper\" cannot be null.");
 		test(2).isBetween(1,3);
 		test(1).isBetween(1,3);
 		test(3).isBetween(1,3);
-		assertThrown(()->test(2).isBetween(1,1)).is("Value was not less than or equals to expected.\n\tExpect=[1]\n\tActual=[2]");
-		assertThrown(()->test(2).isBetween(3,3)).is("Value was not greater than or equals to expected.\n\tExpect=[3]\n\tActual=[2]");
+		assertThrown(()->test(2).isBetween(1,1)).is("Value was not less than or equals to expected.\n\tExpect=\"1\".\n\tActual=\"2\".");
+		assertThrown(()->test(2).isBetween(3,3)).is("Value was not greater than or equals to expected.\n\tExpect=\"3\".\n\tActual=\"2\".");
 
 		test(2).isNot("2");
 	}
@@ -113,6 +113,7 @@ public class IntegerAssertion_Test {
 	@Test
 	public void a02_other() throws Exception {
 		assertThrown(()->test((Integer)null).msg("Foo {0}", 1).exists()).is("Foo 1");
+		assertThrown(()->test((Integer)null).msg("Foo {0}", 1).throwable(RuntimeException.class).exists()).isExactType(RuntimeException.class).is("Foo 1");
 		test((Integer)null).stdout();
 	}
 }
diff --git a/juneau-utest/src/test/java/org/apache/juneau/assertions/ListAssertion_Test.java b/juneau-utest/src/test/java/org/apache/juneau/assertions/ListAssertion_Test.java
index 263a395..e2df21c 100644
--- a/juneau-utest/src/test/java/org/apache/juneau/assertions/ListAssertion_Test.java
+++ b/juneau-utest/src/test/java/org/apache/juneau/assertions/ListAssertion_Test.java
@@ -39,9 +39,9 @@ public class ListAssertion_Test {
 
 		assertThrown(()->test(null).isSize(0)).is("Value was null.");
 		test(x1).isSize(0);
-		assertThrown(()->test(x1).isSize(1)).is("Collection did not have the expected size.  Expect=1, Actual=0.");
+		assertThrown(()->test(x1).isSize(1)).is("Collection did not have the expected size.\n\tExpect=1.\n\tActual=0.");
 		test(x2).isSize(2);
-		assertThrown(()->test(x2).isSize(0)).is("Collection did not have the expected size.  Expect=0, Actual=2.");
+		assertThrown(()->test(x2).isSize(0)).is("Collection did not have the expected size.\n\tExpect=0.\n\tActual=2.");
 
 		assertThrown(()->test(null).isEmpty()).is("Value was null.");
 		test(x1).isEmpty();
@@ -59,6 +59,7 @@ public class ListAssertion_Test {
 	@Test
 	public void a02_other() throws Exception {
 		assertThrown(()->test(null).msg("Foo {0}", 1).exists()).is("Foo 1");
+		assertThrown(()->test(null).msg("Foo {0}", 1).throwable(RuntimeException.class).exists()).isExactType(RuntimeException.class).is("Foo 1");
 		test(null).stdout();
 	}
 }
diff --git a/juneau-utest/src/test/java/org/apache/juneau/assertions/LongAssertion_Test.java b/juneau-utest/src/test/java/org/apache/juneau/assertions/LongAssertion_Test.java
index 7552785..ba4714b 100644
--- a/juneau-utest/src/test/java/org/apache/juneau/assertions/LongAssertion_Test.java
+++ b/juneau-utest/src/test/java/org/apache/juneau/assertions/LongAssertion_Test.java
@@ -51,64 +51,64 @@ public class LongAssertion_Test {
 		test(empty()).isEqual(null);
 
 		assertThrown(()->test(empty()).isGreaterThan(1)).is("Value was null.");
-		assertThrown(()->test(1l).isGreaterThan(null)).is("Parameter 'value' cannot be null.");
+		assertThrown(()->test(1l).isGreaterThan(null)).is("Argument \"value\" cannot be null.");
 		test(2l).isGreaterThan(1);
-		assertThrown(()->test(1l).isGreaterThan(2l)).is("Value was not greater than expected.\n\tExpect=[2]\n\tActual=[1]");
-		assertThrown(()->test(1l).isGreaterThan(1l)).is("Value was not greater than expected.\n\tExpect=[1]\n\tActual=[1]");
+		assertThrown(()->test(1l).isGreaterThan(2l)).is("Value was not greater than expected.\n\tExpect=\"2\".\n\tActual=\"1\".");
+		assertThrown(()->test(1l).isGreaterThan(1l)).is("Value was not greater than expected.\n\tExpect=\"1\".\n\tActual=\"1\".");
 
 		test(2l).asInteger().isGreaterThan(1);
 		test(empty()).asInteger().isNull();
 
 		assertThrown(()->test(empty()).isGt(1l)).is("Value was null.");
-		assertThrown(()->test(1l).isGt(null)).is("Parameter 'value' cannot be null.");
+		assertThrown(()->test(1l).isGt(null)).is("Argument \"value\" cannot be null.");
 		test(2l).isGt(1);
-		assertThrown(()->test(1l).isGt(2)).is("Value was not greater than expected.\n\tExpect=[2]\n\tActual=[1]");
-		assertThrown(()->test(1l).isGt(1)).is("Value was not greater than expected.\n\tExpect=[1]\n\tActual=[1]");
+		assertThrown(()->test(1l).isGt(2)).is("Value was not greater than expected.\n\tExpect=\"2\".\n\tActual=\"1\".");
+		assertThrown(()->test(1l).isGt(1)).is("Value was not greater than expected.\n\tExpect=\"1\".\n\tActual=\"1\".");
 
 		assertThrown(()->test(empty()).isGreaterThanOrEqual(1)).is("Value was null.");
-		assertThrown(()->test(1l).isGreaterThanOrEqual(null)).is("Parameter 'value' cannot be null.");
+		assertThrown(()->test(1l).isGreaterThanOrEqual(null)).is("Argument \"value\" cannot be null.");
 		test(2l).isGreaterThanOrEqual(1);
-		assertThrown(()->test(1l).isGreaterThanOrEqual(2)).is("Value was not greater than or equals to expected.\n\tExpect=[2]\n\tActual=[1]");
+		assertThrown(()->test(1l).isGreaterThanOrEqual(2)).is("Value was not greater than or equals to expected.\n\tExpect=\"2\".\n\tActual=\"1\".");
 		test(1l).isGreaterThanOrEqual(1);
 
 		assertThrown(()->test(empty()).isGte(1)).is("Value was null.");
-		assertThrown(()->test(1l).isGte(null)).is("Parameter 'value' cannot be null.");
+		assertThrown(()->test(1l).isGte(null)).is("Argument \"value\" cannot be null.");
 		test(2l).isGte(1);
-		assertThrown(()->test(1l).isGte(2l)).is("Value was not greater than or equals to expected.\n\tExpect=[2]\n\tActual=[1]");
+		assertThrown(()->test(1l).isGte(2l)).is("Value was not greater than or equals to expected.\n\tExpect=\"2\".\n\tActual=\"1\".");
 		test(1l).isGte(1l);
 
 		assertThrown(()->test(empty()).isLessThan(1)).is("Value was null.");
-		assertThrown(()->test(1l).isLessThan(null)).is("Parameter 'value' cannot be null.");
+		assertThrown(()->test(1l).isLessThan(null)).is("Argument \"value\" cannot be null.");
 		test(1l).isLessThan(2l);
-		assertThrown(()->test(2l).isLessThan(1)).is("Value was not less than expected.\n\tExpect=[1]\n\tActual=[2]");
-		assertThrown(()->test(1l).isLessThan(1)).is("Value was not less than expected.\n\tExpect=[1]\n\tActual=[1]");
+		assertThrown(()->test(2l).isLessThan(1)).is("Value was not less than expected.\n\tExpect=\"1\".\n\tActual=\"2\".");
+		assertThrown(()->test(1l).isLessThan(1)).is("Value was not less than expected.\n\tExpect=\"1\".\n\tActual=\"1\".");
 
 		assertThrown(()->test(empty()).isLt(1)).is("Value was null.");
-		assertThrown(()->test(1l).isLt(null)).is("Parameter 'value' cannot be null.");
+		assertThrown(()->test(1l).isLt(null)).is("Argument \"value\" cannot be null.");
 		test(1l).isLt(2);
-		assertThrown(()->test(2l).isLt(1)).is("Value was not less than expected.\n\tExpect=[1]\n\tActual=[2]");
-		assertThrown(()->test(1l).isLt(1)).is("Value was not less than expected.\n\tExpect=[1]\n\tActual=[1]");
+		assertThrown(()->test(2l).isLt(1)).is("Value was not less than expected.\n\tExpect=\"1\".\n\tActual=\"2\".");
+		assertThrown(()->test(1l).isLt(1)).is("Value was not less than expected.\n\tExpect=\"1\".\n\tActual=\"1\".");
 
 		assertThrown(()->test(empty()).isLessThanOrEqual(1)).is("Value was null.");
-		assertThrown(()->test(1l).isLessThanOrEqual(null)).is("Parameter 'value' cannot be null.");
+		assertThrown(()->test(1l).isLessThanOrEqual(null)).is("Argument \"value\" cannot be null.");
 		test(1l).isLessThanOrEqual(2);
-		assertThrown(()->test(2l).isLessThanOrEqual(1)).is("Value was not less than or equals to expected.\n\tExpect=[1]\n\tActual=[2]");
+		assertThrown(()->test(2l).isLessThanOrEqual(1)).is("Value was not less than or equals to expected.\n\tExpect=\"1\".\n\tActual=\"2\".");
 		test(1l).isLessThanOrEqual(1);
 
 		assertThrown(()->test(empty()).isLte(1)).is("Value was null.");
-		assertThrown(()->test(1l).isLte(null)).is("Parameter 'value' cannot be null.");
+		assertThrown(()->test(1l).isLte(null)).is("Argument \"value\" cannot be null.");
 		test(1l).isLte(2);
-		assertThrown(()->test(2l).isLte(1)).is("Value was not less than or equals to expected.\n\tExpect=[1]\n\tActual=[2]");
+		assertThrown(()->test(2l).isLte(1)).is("Value was not less than or equals to expected.\n\tExpect=\"1\".\n\tActual=\"2\".");
 		test(1l).isLte(1);
 
 		assertThrown(()->test(empty()).isBetween(1,3)).is("Value was null.");
-		assertThrown(()->test(2l).isBetween(null,3)).is("Parameter 'lower' cannot be null.");
-		assertThrown(()->test(2l).isBetween(1,null)).is("Parameter 'upper' cannot be null.");
+		assertThrown(()->test(2l).isBetween(null,3)).is("Argument \"lower\" cannot be null.");
+		assertThrown(()->test(2l).isBetween(1,null)).is("Argument \"upper\" cannot be null.");
 		test(2l).isBetween(1,3);
 		test(1l).isBetween(1,3);
 		test(3l).isBetween(1,3);
-		assertThrown(()->test(2l).isBetween(1,1)).is("Value was not less than or equals to expected.\n\tExpect=[1]\n\tActual=[2]");
-		assertThrown(()->test(2l).isBetween(3,3)).is("Value was not greater than or equals to expected.\n\tExpect=[3]\n\tActual=[2]");
+		assertThrown(()->test(2l).isBetween(1,1)).is("Value was not less than or equals to expected.\n\tExpect=\"1\".\n\tActual=\"2\".");
+		assertThrown(()->test(2l).isBetween(3,3)).is("Value was not greater than or equals to expected.\n\tExpect=\"3\".\n\tActual=\"2\".");
 
 		test(2l).isNot("2");
 	}
@@ -116,6 +116,7 @@ public class LongAssertion_Test {
 	@Test
 	public void a02_other() throws Exception {
 		assertThrown(()->test((Long)null).msg("Foo {0}", 1).exists()).is("Foo 1");
+		assertThrown(()->test((Long)null).msg("Foo {0}", 1).throwable(RuntimeException.class).exists()).isExactType(RuntimeException.class).is("Foo 1");
 		test((Long)null).stdout();
 	}
 }
diff --git a/juneau-utest/src/test/java/org/apache/juneau/assertions/MapAssertion_Test.java b/juneau-utest/src/test/java/org/apache/juneau/assertions/MapAssertion_Test.java
index 0be48e3..0dd2bfe 100644
--- a/juneau-utest/src/test/java/org/apache/juneau/assertions/MapAssertion_Test.java
+++ b/juneau-utest/src/test/java/org/apache/juneau/assertions/MapAssertion_Test.java
@@ -39,9 +39,9 @@ public class MapAssertion_Test {
 
 		assertThrown(()->test(null).isSize(0)).is("Value was null.");
 		test(x1).isSize(0);
-		assertThrown(()->test(x1).isSize(1)).is("Map did not have the expected size.  Expect=1, Actual=0.");
+		assertThrown(()->test(x1).isSize(1)).is("Map did not have the expected size.\n\tExpect=1.\n\tActual=0.");
 		test(x2).isSize(2);
-		assertThrown(()->test(x2).isSize(0)).is("Map did not have the expected size.  Expect=0, Actual=2.");
+		assertThrown(()->test(x2).isSize(0)).is("Map did not have the expected size.\n\tExpect=0.\n\tActual=2.");
 
 		assertThrown(()->test(null).isEmpty()).is("Value was null.");
 		test(x1).isEmpty();
@@ -56,17 +56,18 @@ public class MapAssertion_Test {
 		test((Map<String,Object>)null).value("a").asInteger().isNull();
 
 		test(x2).containsKey("a");
-		assertThrown(()->test(x2).containsKey("x")).is("Map did not contain expected key.\n\tContents: {a:1,b:2}\n\tExpected key: x");
+		assertThrown(()->test(x2).containsKey("x")).is("Map did not contain expected key.\n\tExpected key=\"x\".\n\tValue=\"{a:1,b:2}\".");
 		assertThrown(()->test((Map<?,?>)null).containsKey("x")).is("Value was null.");
 
 		test(x2).doesNotContainKey("x");
-		assertThrown(()->test(x2).doesNotContainKey("a")).is("Map contained unexpected key.\n\tContents: {a:1,b:2}\n\tUnexpected key: a");
+		assertThrown(()->test(x2).doesNotContainKey("a")).is("Map contained unexpected key.\n\tUnexpected key=\"a\".\n\tValue=\"{a:1,b:2}\".");
 		assertThrown(()->test((Map<?,?>)null).containsKey("x")).is("Value was null.");
 	}
 
 	@Test
 	public void a02_other() throws Exception {
 		assertThrown(()->test(null).msg("Foo {0}", 1).exists()).is("Foo 1");
+		assertThrown(()->test(null).msg("Foo {0}", 1).throwable(RuntimeException.class).exists()).isExactType(RuntimeException.class).is("Foo 1");
 		test(null).stdout();
 	}
 }
diff --git a/juneau-utest/src/test/java/org/apache/juneau/assertions/ObjectAssertion_Test.java b/juneau-utest/src/test/java/org/apache/juneau/assertions/ObjectAssertion_Test.java
index 6ff00f3..edd52bf 100644
--- a/juneau-utest/src/test/java/org/apache/juneau/assertions/ObjectAssertion_Test.java
+++ b/juneau-utest/src/test/java/org/apache/juneau/assertions/ObjectAssertion_Test.java
@@ -57,11 +57,11 @@ public class ObjectAssertion_Test {
 		test(of(1)).exists();
 
 		assertThrown(()->test(empty()).isType(String.class)).is("Value was null.");
-		assertThrown(()->test("foo").isType(null)).is("Parameter 'parent' cannot be null.");
+		assertThrown(()->test("foo").isType(null)).is("Argument \"parent\" cannot be null.");
 		test("foo").isType(String.class);
 		test("foo").isType(CharSequence.class);
 		test("foo").isType(Comparable.class);
-		assertThrown(()->test(1).isType(String.class)).is("Unexpected class.\n\tExpect=[java.lang.String]\n\tActual=[java.lang.Integer]");
+		assertThrown(()->test(1).isType(String.class)).is("Unexpected type.\n\tExpect=\"java.lang.String\".\n\tActual=\"java.lang.Integer\".");
 
 		test("foo").asString(JsonSerializer.DEFAULT).is("\"foo\"");
 		test(empty()).asString(JsonSerializer.DEFAULT).is("null");
@@ -75,39 +75,39 @@ public class ObjectAssertion_Test {
 
 		int[] x1 = {1,2}, x2 = {2,1};
 		test(x2).asJsonSorted().is("[1,2]");
-		assertThrown(()->test(x2).asJsonSorted().is("[2,1]")).is("Unexpected value.\n\tExpect=[[2,1]]\n\tActual=[[1,2]]");
+		assertThrown(()->test(x2).asJsonSorted().is("[2,1]")).is("Unexpected value.\n\tExpect=\"[2,1]\".\n\tActual=\"[1,2]\".");
 		test(empty()).asJsonSorted().is("null");
 
 		test(x1).isSameJsonAs(x1);
-		assertThrown(()->test(x1).isSameJsonAs(x2)).is("Unexpected comparison.\n\tExpect=[[2,1]]\n\tActual=[[1,2]]");
+		assertThrown(()->test(x1).isSameJsonAs(x2)).is("Unexpected comparison.\n\tExpect=\"[2,1]\".\n\tActual=\"[1,2]\".");
 		test(empty()).isSameJsonAs(null);
 		assertThrown(()->test(new A1()).isSameJsonAs(null)).contains("Could not call getValue() on property 'foo'");
 
 		test(x1).isSameSortedAs(x1);
 		test(x1).isSameSortedAs(x2);
-		assertThrown(()->test(x1).isSameJsonAs(null)).is("Unexpected comparison.\n\tExpect=[null]\n\tActual=[[1,2]]");
+		assertThrown(()->test(x1).isSameJsonAs(null)).is("Unexpected comparison.\n\tExpect=\"null\".\n\tActual=\"[1,2]\".");
 		test(empty()).isSameSortedAs(null);
 
 		test(x1).doesNotEqual(null);
 		test(empty()).doesNotEqual(x1);
 		test(x1).doesNotEqual(x2);
 
-		assertThrown(()->test(empty()).doesNotEqual(null)).is("Unexpected value.\n\tDid not expect=[null]\n\tActual=[null]");
-		assertThrown(()->test(x1).doesNotEqual(x1)).is("Unexpected value.\n\tDid not expect=[[1,2]]\n\tActual=[[1,2]]");
+		assertThrown(()->test(empty()).doesNotEqual(null)).is("Unexpected value.\n\tDid not expect=\"null\".\n\tActual=\"null\".");
+		assertThrown(()->test(x1).doesNotEqual(x1)).is("Unexpected value.\n\tDid not expect=\"[1,2]\".\n\tActual=\"[1,2]\".");
 
 		test(x1).passes(x->x != null);
-		assertThrown(()->test(x1).passes(x->x == null)).is("Unexpected value: '[1,2]'");
+		assertThrown(()->test(x1).passes(x->x == null)).is("Unexpected value: \"[1,2]\".");
 
 		test(x1).passes(x->x[0] == 1);
-		assertThrown(()->test(x1).passes(x->x[0]==2)).is("Unexpected value: '[1,2]'");
+		assertThrown(()->test(x1).passes(x->x[0]==2)).is("Unexpected value: \"[1,2]\".");
 
 		test(x1).isNot(null);
 
 		test(x1).isAny(x1,x2);
-		assertThrown(()->test(x1).isAny(x2)).is("Expected value not found.\n\tExpect=[[[2,1]]]\n\tActual=[[1,2]]");
+		assertThrown(()->test(x1).isAny(x2)).is("Expected value not found.\n\tExpect=\"[[2,1]]\".\n\tActual=\"[1,2]\".");
 
 		test(x1).isNotAny(x2);
-		assertThrown(()->test(x1).isNotAny(x1,x2)).is("Unexpected value found.\n\tUnexpected=[[1,2]]\n\tActual=[[1,2]]");
+		assertThrown(()->test(x1).isNotAny(x1,x2)).is("Unexpected value found.\n\tUnexpected=\"[1,2]\".\n\tActual=\"[1,2]\".");
 
 		Date d1 = new Date(0), d2 = new Date(0);
 		test(d1).is(d2);
@@ -121,16 +121,17 @@ public class ObjectAssertion_Test {
 	@Test
 	public void a02_other() throws Exception {
 		assertThrown(()->test((Object)null).msg("Foo {0}", 1).exists()).is("Foo 1");
+		assertThrown(()->test((Object)null).msg("Foo {0}", 1).throwable(RuntimeException.class).exists()).isExactType(RuntimeException.class).is("Foo 1");
 		test((Object)null).stdout();
 	}
 
 	@Test
 	public void a03_conversions() throws Exception {
 		test(new String[]{"foo"}).asArray(String.class).item(0).is("foo");
-		assertThrown(()->test("foo").asArray(String.class)).contains("Object was not type 'java.lang.String[]'.  Actual='java.lang.String'");
+		assertThrown(()->test("foo").asArray(String.class)).contains("Object was not type \"java.lang.String[]\".\n\tActual=\"java.lang.String\".");
 
 		test(true).asBoolean().isTrue();
-		assertThrown(()->test("foo").asBoolean()).contains("Object was not type 'java.lang.Boolean'.  Actual='java.lang.String'");
+		assertThrown(()->test("foo").asBoolean()).contains("Object was not type \"java.lang.Boolean\".\n\tActual=\"java.lang.String\".");
 
 		test(new byte[]{123}).asByteArray().asJson().is("[123]");
 		test(AList.of(123)).asCollection().asJson().is("[123]");
diff --git a/juneau-utest/src/test/java/org/apache/juneau/assertions/StringAssertion_Test.java b/juneau-utest/src/test/java/org/apache/juneau/assertions/StringAssertion_Test.java
index da82bd7..14058ef 100644
--- a/juneau-utest/src/test/java/org/apache/juneau/assertions/StringAssertion_Test.java
+++ b/juneau-utest/src/test/java/org/apache/juneau/assertions/StringAssertion_Test.java
@@ -41,13 +41,13 @@ public class StringAssertion_Test {
 		test("foobar").replaceAll("fo+","bar").is("barbar").is("foobar");
 		test(of("foobar")).replaceAll("fo+","bar").is("barbar").is("foobar");
 		test(empty()).replaceAll("fo+","bar").isNull();
-		assertThrown(()->test("foobar").replaceAll(null,"")).is("Parameter 'regex' cannot be null.");
-		assertThrown(()->test("foobar").replaceAll("",null)).is("Parameter 'replacement' cannot be null.");
+		assertThrown(()->test("foobar").replaceAll(null,"")).is("Argument \"regex\" cannot be null.");
+		assertThrown(()->test("foobar").replaceAll("",null)).is("Argument \"replacement\" cannot be null.");
 
 		test("foobar").replace("foo","bar").is("barbar").is("foobar");
 		test(empty()).replace("foo","bar").isNull();
-		assertThrown(()->test("").replace(null,"bar").isNull()).is("Parameter 'target' cannot be null.");
-		assertThrown(()->test("").replace("foo",null).isNull()).is("Parameter 'replacement' cannot be null.");
+		assertThrown(()->test("").replace(null,"bar").isNull()).is("Argument \"target\" cannot be null.");
+		assertThrown(()->test("").replace("foo",null).isNull()).is("Argument \"replacement\" cannot be null.");
 
 		test("foo%20bar").urlDecode().is("foo bar").is("foo%20bar");
 		test(empty()).urlDecode().isNull();
@@ -62,91 +62,92 @@ public class StringAssertion_Test {
 		test(empty()).uc().isNull();
 
 		test("foo\nbar\nbaz").isEqualLinesTo("foo","bar","baz");
-		assertThrown(()->test(empty()).isEqualLinesTo((String[])null)).is("Parameter 'lines' cannot be null.");
+		assertThrown(()->test(empty()).isEqualLinesTo((String[])null)).is("Argument \"lines\" cannot be null.");
 		assertThrown(()->test(empty()).isEqualLinesTo((String)null)).is("Value was null.");
-		assertThrown(()->test("foo\nbar\nbaz").javaStrings().isEqualLinesTo("foo","bar","bar")).is("Text differed at position 10.\n\tExpect=[foo\\nbar\\nbar]\n\tActual=[foo\\nbar\\nbaz]");
+		assertThrown(()->test("foo\nbar\nbaz").javaStrings().isEqualLinesTo("foo","bar","bar")).is("String differed at position 10.\n\tExpect=\"foo\\nbar\\nbar\".\n\tActual=\"foo\\nbar\\nbaz\".");
 
 		test("foo\nbar\nbaz").isEqualSortedLinesTo("bar","foo","baz");
-		assertThrown(()->test(empty()).isEqualSortedLinesTo((String[])null)).is("Parameter 'lines' cannot be null.");
+		assertThrown(()->test(empty()).isEqualSortedLinesTo((String[])null)).is("Argument \"lines\" cannot be null.");
 		test("").isEqualSortedLinesTo((String)null);
 		assertThrown(()->test(empty()).isEqualSortedLinesTo()).is("Value was null.");
-		assertThrown(()->test("foo\nbar\nbaz").isEqualSortedLinesTo("bar","foo","bar")).is("Expected text had different values at line 2.\n\tExpect=[bar]\n\tActual=[baz]");
-		assertThrown(()->test("foo\nbar\nbaz").isEqualSortedLinesTo("bar","foo")).is("Expected text had different numbers of lines.\n\tExpect=[2]\n\tActual=[3]");
+		assertThrown(()->test("foo\nbar\nbaz").isEqualSortedLinesTo("bar","foo","bar")).is("Expected string had different values at line 2.\n\tExpect=\"bar\".\n\tActual=\"baz\".");
+		assertThrown(()->test("foo\nbar\nbaz").isEqualSortedLinesTo("bar","foo")).is("Expected string had different numbers of lines.\n\tExpect=\"2\".\n\tActual=\"3\".");
 		assertThrown(()->test(empty()).isEqualSortedLinesTo("foo")).is("Value was null.");
-		assertThrown(()->test("foo").isEqualSortedLinesTo((String)null)).is("Expected text had different values at line 1.\n\tExpect=[]\n\tActual=[foo]");
+		assertThrown(()->test("foo").isEqualSortedLinesTo((String)null)).is("Expected string had different values at line 1.\n\tExpect=\"\".\n\tActual=\"foo\".");
 
 		test("foo\nbar\nbaz").isEqualLinesTo("foo","bar","baz");
 
 		test("foobar").isEqualIgnoreCaseTo("FOOBAR");
 		test(empty()).isEqualIgnoreCaseTo(null);
-		assertThrown(()->test("foobar").isEqualIgnoreCaseTo("FOOBAZ")).is("Text differed at position 5.\n\tExpect=[FOOBAZ]\n\tActual=[foobar]");
-		assertThrown(()->test(empty()).isEqualIgnoreCaseTo("FOOBAZ")).is("Text differed at position 0.\n\tExpect=[FOOBAZ]\n\tActual=[null]");
-		assertThrown(()->test("foobar").isEqualIgnoreCaseTo(null)).is("Text differed at position 0.\n\tExpect=[null]\n\tActual=[foobar]");
+		assertThrown(()->test("foobar").isEqualIgnoreCaseTo("FOOBAZ")).is("String differed at position 5.\n\tExpect=\"FOOBAZ\".\n\tActual=\"foobar\".");
+		assertThrown(()->test(empty()).isEqualIgnoreCaseTo("FOOBAZ")).is("String differed at position 0.\n\tExpect=\"FOOBAZ\".\n\tActual=\"null\".");
+		assertThrown(()->test("foobar").isEqualIgnoreCaseTo(null)).is("String differed at position 0.\n\tExpect=\"null\".\n\tActual=\"foobar\".");
 
 		test("foobar").doesNotEqual("foobaz");
-		assertThrown(()->test("foobar").doesNotEqual("foobar")).is("Text equaled unexpected.\n\tText=[foobar]");
+		assertThrown(()->test("foobar").doesNotEqual("foobar")).is("String equaled unexpected.\n\tValue=\"foobar\".");
 
-		assertThrown(()->test("foobar").isEqualTo("foobaz")).is("Text differed at position 5.\n\tExpect=[foobaz]\n\tActual=[foobar]");
+		assertThrown(()->test("foobar").isEqualTo("foobaz")).is("String differed at position 5.\n\tExpect=\"foobaz\".\n\tActual=\"foobar\".");
 
 		test("foobar").isNot("foobaz");
-		assertThrown(()->test("foobar").isNot("foobar")).is("Text equaled unexpected.\n\tText=[foobar]");
-		assertThrown(()->test(empty()).isNot(null)).is("Text equaled unexpected.\n\tText=[null]");
+		assertThrown(()->test("foobar").isNot("foobar")).is("String equaled unexpected.\n\tValue=\"foobar\".");
+		assertThrown(()->test(empty()).isNot(null)).is("String equaled unexpected.\n\tValue=\"null\".");
 		test("foobar").isNot(null);
 		test(empty()).isNot("foobar");
 
 		test("foobar").doesNotEqualIc("foobaz");
-		assertThrown(()->test("foobar").doesNotEqualIc("Foobar")).is("Text equaled unexpected.\n\tText=[foobar]");
-		assertThrown(()->test(empty()).doesNotEqualIc(null)).is("Text equaled unexpected.\n\tText=[null]");
+		assertThrown(()->test("foobar").doesNotEqualIc("Foobar")).is("String equaled unexpected.\n\tValue=\"foobar\".");
+		assertThrown(()->test(empty()).doesNotEqualIc(null)).is("String equaled unexpected.\n\tValue=\"null\".");
 		test("foobar").doesNotEqualIc(null);
 		test(empty()).doesNotEqualIc("foobar");
 
 		test("foobar").contains("foo","bar");
-		assertThrown(()->test("foobar").contains("foo","baz")).is("Text did not contain expected substring.\n\tSubstring=[baz]\n\tText=[foobar]");
+		assertThrown(()->test("foobar").contains("foo","baz")).is("String did not contain expected substring.\n\tSubstring=\"baz\".\n\tValue=\"foobar\".");
 		test(empty()).contains();
-		assertThrown(()->test("foobar").contains((String[])null)).is("Parameter 'values' cannot be null.");
+		assertThrown(()->test("foobar").contains((String[])null)).is("Argument \"values\" cannot be null.");
 		test("foobar").contains((String)null);
-		assertThrown(()->test(empty()).contains("foobar")).is("Text did not contain expected substring.\n\tSubstring=[foobar]\n\tText=[null]");
+		assertThrown(()->test(empty()).contains("foobar")).is("String did not contain expected substring.\n\tSubstring=\"foobar\".\n\tValue=\"null\".");
 
 		test("foobar").doesNotContain("baz","qux");
-		assertThrown(()->test("foobar").doesNotContain("foo","baz")).is("Text contained unexpected substring.\n\tSubstring=[foo]\n\tText=[foobar]");
+		assertThrown(()->test("foobar").doesNotContain("foo","baz")).is("String contained unexpected substring.\n\tSubstring=\"foo\".\n\tValue=\"foobar\".");
 		test(empty()).doesNotContain();
-		assertThrown(()->test("foobar").doesNotContain((String[])null)).is("Parameter 'values' cannot be null.");
+		assertThrown(()->test("foobar").doesNotContain((String[])null)).is("Argument \"values\" cannot be null.");
 		test("foobar").doesNotContain((String)null);
 		test(empty()).doesNotContain("foobar");
 
 		test("").isEmpty();
 		test(empty()).isEmpty();
-		assertThrown(()->test("foo").isEmpty()).is("Text was not empty.\n\tText=[foo]");
+		assertThrown(()->test("foo").isEmpty()).is("String was not empty.\n\tValue=\"foo\".");
 
 		test("foo").isNotEmpty();
-		assertThrown(()->test("").isNotEmpty()).is("Text was empty.");
-		assertThrown(()->test(empty()).isNotEmpty()).is("Text was null.");
+		assertThrown(()->test("").isNotEmpty()).is("String was empty.");
+		assertThrown(()->test(empty()).isNotEmpty()).is("String was null.");
 
 		test("foo").matches("fo+");
-		assertThrown(()->test("foo").matches("bar")).is("Text did not match expected pattern.\n\tPattern=[bar]\n\tText=[foo]");
+		assertThrown(()->test("foo").matches("bar")).is("String did not match expected pattern.\n\tPattern=\"bar\".\n\tValue=\"foo\".");
 		assertThrown(()->test(empty()).matches("fo+")).is("Value was null.");
-		assertThrown(()->test("").matches((String)null)).is("Parameter 'regex' cannot be null.");
+		assertThrown(()->test("").matches((String)null)).is("Argument \"regex\" cannot be null.");
 
 		test("foo").matchesSimple("fo*");
-		assertThrown(()->test("foo").matchesSimple("b*")).is("Text did not match expected pattern.\n\tPattern=[\\Qb\\E.*\\Q\\E]\n\tText=[foo]");
+		assertThrown(()->test("foo").matchesSimple("b*")).is("String did not match expected pattern.\n\tPattern=\"\\Qb\\E.*\\Q\\E\".\n\tValue=\"foo\".");
 		assertThrown(()->test(empty()).matchesSimple("b*")).is("Value was null.");
-		assertThrown(()->test("").matchesSimple(null)).is("Parameter 'searchPattern' cannot be null.");
+		assertThrown(()->test("").matchesSimple(null)).is("Argument \"searchPattern\" cannot be null.");
 
 		test("foo").doesNotMatch("b.*");
-		assertThrown(()->test("foo").doesNotMatch("fo+")).is("Text matched unexpected pattern.\n\tPattern=[fo+]\n\tText=[foo]");
+		assertThrown(()->test("foo").doesNotMatch("fo+")).is("String matched unexpected pattern.\n\tPattern=\"fo+\".\n\tValue=\"foo\".");
 		test(empty()).doesNotMatch("fo+");
-		assertThrown(()->test("").doesNotMatch((String)null)).is("Parameter 'regex' cannot be null.");
+		assertThrown(()->test("").doesNotMatch((String)null)).is("Argument \"regex\" cannot be null.");
 
 		test("foo").startsWith("fo");
-		assertThrown(()->test("foo").startsWith("x")).is("Text did not start with expected string.\n\tString=[x]\n\tText=[foo]");
+		assertThrown(()->test("foo").startsWith("x")).is("String did not start with expected substring.\n\tSubstring=\"x\".\n\tValue=\"foo\".");
 
 		test("foo").endsWith("oo");
-		assertThrown(()->test("foo").endsWith("x")).is("Text did not end with expected string.\n\tString=[x]\n\tText=[foo]");
+		assertThrown(()->test("foo").endsWith("x")).is("String did not end with expected substring.\n\tSubstring=\"x\".\n\tValue=\"foo\".");
 	}
 
 	@Test
 	public void a02_other() throws Exception {
 		assertThrown(()->test((String)null).msg("Foo {0}", 1).exists()).is("Foo 1");
+		assertThrown(()->test((String)null).msg("Foo {0}", 1).throwable(RuntimeException.class).exists()).isExactType(RuntimeException.class).is("Foo 1");
 		test((String)null).stdout().javaStrings();
 	}
 }
diff --git a/juneau-utest/src/test/java/org/apache/juneau/assertions/ThrowableAssertion_Test.java b/juneau-utest/src/test/java/org/apache/juneau/assertions/ThrowableAssertion_Test.java
index 1062e75..d9390ec 100644
--- a/juneau-utest/src/test/java/org/apache/juneau/assertions/ThrowableAssertion_Test.java
+++ b/juneau-utest/src/test/java/org/apache/juneau/assertions/ThrowableAssertion_Test.java
@@ -31,30 +31,30 @@ public class ThrowableAssertion_Test {
 		RuntimeException x1 = new RuntimeException("foo");
 
 		test(x1).isType(Exception.class).isType(RuntimeException.class);
-		assertThrown(()->test(x1).isType(IOException.class)).is("Exception was not expected type.\n\tExpect=[java.io.IOException]\n\tActual=[java.lang.RuntimeException]");
+		assertThrown(()->test(x1).isType(IOException.class)).is("Exception was not expected type.\n\tExpect=\"java.io.IOException\".\n\tActual=\"java.lang.RuntimeException\".");
 		assertThrown(()->test(null).isType(IOException.class)).is("Exception was not thrown.");
-		assertThrown(()->test(x1).isType(null)).is("Parameter 'type' cannot be null.");
+		assertThrown(()->test(x1).isType(null)).is("Argument \"type\" cannot be null.");
 
 		test(x1).contains("foo");
-		assertThrown(()->test(x1).contains("bar")).is("Exception message did not contain expected substring.\n\tSubstring=[bar]\n\tText=[foo]");
+		assertThrown(()->test(x1).contains("bar")).is("Exception message did not contain expected substring.\n\tSubstring=\"bar\".\n\tValue=\"foo\".");
 		assertThrown(()->test(null).contains("foo")).is("Exception was not thrown.");
-		assertThrown(()->test(x1).contains((String[])null)).is("Parameter 'substrings' cannot be null.");
+		assertThrown(()->test(x1).contains((String[])null)).is("Argument \"substrings\" cannot be null.");
 		test(x1).contains((String)null);
 
 		test(null).doesNotExist();
 		assertThrown(()->test(x1).doesNotExist()).is("Exception was thrown.");
 
 		test(x1).passes(x->x.getMessage().equals("foo"));
-		assertThrown(()->test(x1).passes(x->x.getMessage().equals("bar"))).is("Unexpected value: 'java.lang.RuntimeException: foo'");
+		assertThrown(()->test(x1).passes(x->x.getMessage().equals("bar"))).is("Unexpected value: \"java.lang.RuntimeException: foo\".");
 
 		test(x1).passes(x->x.getMessage().equals("foo"));
-		assertThrown(()->test(x1).passes(x->x.getMessage().equals("bar"))).is("Unexpected value: 'java.lang.RuntimeException: foo'");
+		assertThrown(()->test(x1).passes(x->x.getMessage().equals("bar"))).is("Unexpected value: \"java.lang.RuntimeException: foo\".");
 
 		test(x1).passes(x->x.getMessage().equals("foo"));
-		assertThrown(()->test(x1).passes(x->x.getMessage().equals("bar"))).is("Unexpected value: 'java.lang.RuntimeException: foo'");
+		assertThrown(()->test(x1).passes(x->x.getMessage().equals("bar"))).is("Unexpected value: \"java.lang.RuntimeException: foo\".");
 
 		test(x1).passes(x->x.getMessage().equals("foo"));
-		assertThrown(()->test(x1).passes(x->x.getMessage().equals("bar"))).is("Unexpected value: 'java.lang.RuntimeException: foo'");
+		assertThrown(()->test(x1).passes(x->x.getMessage().equals("bar"))).is("Unexpected value: \"java.lang.RuntimeException: foo\".");
 
 		test(x1).message().is("foo");
 		test(new RuntimeException()).message().doesNotExist();
@@ -84,6 +84,7 @@ public class ThrowableAssertion_Test {
 	@Test
 	public void a02_other() throws Exception {
 		assertThrown(()->test(null).msg("Foo {0}", 1).exists()).is("Foo 1");
+		assertThrown(()->test(null).msg("Foo {0}", 1).throwable(RuntimeException.class).exists()).isExactType(RuntimeException.class).is("Foo 1");
 		test(null).stdout();
 	}
 }
diff --git a/juneau-utest/src/test/java/org/apache/juneau/assertions/Verify_Test.java b/juneau-utest/src/test/java/org/apache/juneau/assertions/Verify_Test.java
index f280fc2..8f2cfa4 100644
--- a/juneau-utest/src/test/java/org/apache/juneau/assertions/Verify_Test.java
+++ b/juneau-utest/src/test/java/org/apache/juneau/assertions/Verify_Test.java
@@ -33,23 +33,23 @@ public class Verify_Test {
 		Verify x1 = verify("foo"), x2 = verify(null), x3 = verify(new Date(0));
 
 		test(x1.is("foo")).doesNotExist();
-		test(x1.is("bar")).is("Expected [bar] but was [foo].");
-		test(x1.is(null)).is("Expected [null] but was [foo].");
+		test(x1.is("bar")).is("Expected \"bar\" but was \"foo\".");
+		test(x1.is(null)).is("Expected \"null\" but was \"foo\".");
 		test(x2.is(null)).doesNotExist();
-		test(x2.is("foo")).is("Expected [foo] but was [null].");
+		test(x2.is("foo")).is("Expected \"foo\" but was \"null\".");
 		test(x3.is(new Date(0))).doesNotExist();
 
 		test(x1.isType(String.class)).doesNotExist();
-		test(x1.isType(Integer.class)).is("Expected type [java.lang.Integer] but was [java.lang.String].");
+		test(x1.isType(Integer.class)).is("Expected type \"java.lang.Integer\" but was \"java.lang.String\".");
 		test(x2.isType(null)).doesNotExist();
-		test(x2.isType(String.class)).is("Expected type [java.lang.String] but was [null].");
-		test(x1.isType(null)).is("Expected type [null] but was [java.lang.String].");
+		test(x2.isType(String.class)).is("Expected type \"java.lang.String\" but was \"null\".");
+		test(x1.isType(null)).is("Expected type \"null\" but was \"java.lang.String\".");
 
 
 		test(verify(true).isTrue()).doesNotExist();
 		test(verify(false).isFalse()).doesNotExist();
-		test(verify(null).isTrue()).is("Expected [true] but was [null].");
-		test(verify(null).isFalse()).is("Expected [false] but was [null].");
+		test(verify(null).isTrue()).is("Expected \"true\" but was \"null\".");
+		test(verify(null).isFalse()).is("Expected \"false\" but was \"null\".");
 		test(verify(Boolean.TRUE).isTrue()).doesNotExist();
 		test(verify(Boolean.FALSE).isFalse()).doesNotExist();
 		test(x1.is("foo")).doesNotExist();
diff --git a/juneau-utest/src/test/java/org/apache/juneau/assertions/ZonedDateTimeAssertion_Test.java b/juneau-utest/src/test/java/org/apache/juneau/assertions/ZonedDateTimeAssertion_Test.java
index 0dacc5e..26132dc 100644
--- a/juneau-utest/src/test/java/org/apache/juneau/assertions/ZonedDateTimeAssertion_Test.java
+++ b/juneau-utest/src/test/java/org/apache/juneau/assertions/ZonedDateTimeAssertion_Test.java
@@ -58,12 +58,12 @@ public class ZonedDateTimeAssertion_Test {
 		assertThrown(()->test(x2).isEqual(x1, ChronoUnit.DAYS)).contains("Unexpected value.");
 
 		assertThrown(()->test(empty()).isBefore(x1)).is("Value was null.");
-		assertThrown(()->test(x1).isBefore(null)).is("Parameter 'value' cannot be null.");
+		assertThrown(()->test(x1).isBefore(null)).is("Argument \"value\" cannot be null.");
 		test(x1).isBefore(x2);
 		assertThrown(()->test(x2).isBefore(x1)).contains("Value was not before expected.");
 
 		assertThrown(()->test(empty()).isAfter(x1)).is("Value was null.");
-		assertThrown(()->test(x1).isAfter(null)).is("Parameter 'value' cannot be null.");
+		assertThrown(()->test(x1).isAfter(null)).is("Argument \"value\" cannot be null.");
 		test(x2).isAfter(x1);
 		assertThrown(()->test(x1).isAfter(x2)).contains("Value was not after expected.");
 
@@ -76,8 +76,8 @@ public class ZonedDateTimeAssertion_Test {
 		assertThrown(()->test(x1).isAfterNow()).contains("Value was not after expected.");
 
 		assertThrown(()->test(empty()).isBetween(x1,x2)).is("Value was null.");
-		assertThrown(()->test(now).isBetween(null,x2)).is("Parameter 'lower' cannot be null.");
-		assertThrown(()->test(now).isBetween(x1,null)).is("Parameter 'upper' cannot be null.");
+		assertThrown(()->test(now).isBetween(null,x2)).is("Argument \"lower\" cannot be null.");
+		assertThrown(()->test(now).isBetween(x1,null)).is("Argument \"upper\" cannot be null.");
 		test(now).isBetween(x1, x2);
 		assertThrown(()->test(x1).isBetween(now,x2)).contains("Value was not after expected.");
 		assertThrown(()->test(x2).isBetween(x1,now)).contains("Value was not before expected.");
@@ -86,6 +86,7 @@ public class ZonedDateTimeAssertion_Test {
 	@Test
 	public void a02_other() throws Exception {
 		assertThrown(()->test((ZonedDateTime)null).msg("Foo {0}", 1).exists()).is("Foo 1");
+		assertThrown(()->test((ZonedDateTime)null).msg("Foo {0}", 1).throwable(RuntimeException.class).exists()).isExactType(RuntimeException.class).is("Foo 1");
 		test((ZonedDateTime)null).stdout();
 	}
 }
diff --git a/juneau-utest/src/test/java/org/apache/juneau/config/ConfigTest.java b/juneau-utest/src/test/java/org/apache/juneau/config/ConfigTest.java
index 052bdcc..a3d75d6 100644
--- a/juneau-utest/src/test/java/org/apache/juneau/config/ConfigTest.java
+++ b/juneau-utest/src/test/java/org/apache/juneau/config/ConfigTest.java
@@ -52,7 +52,7 @@ public class ConfigTest {
 		assertNull(c.get("b"));
 		assertNull(c.get("S/c"));
 		assertNull(c.get("T/d"));
-		assertThrown(()->c.get(null)).is("Argument 'key' cannot be null");
+		assertThrown(()->c.get(null)).is("Argument \"key\" cannot be null.");
 		c.close();
 	}
 
@@ -800,7 +800,7 @@ public class ConfigTest {
 		assertObject(c.getKeys("S")).asJson().is("['b1','b2']");
 		assertTrue(c.getKeys("T").isEmpty());
 
-		assertThrown(()->c.getKeys(null)).is("Argument 'section' cannot be null");
+		assertThrown(()->c.getKeys(null)).is("Argument \"section\" cannot be null.");
 	}
 
 	//====================================================================================================
@@ -823,7 +823,7 @@ public class ConfigTest {
 		c.writeProperties("", a, true);
 		assertObject(a).asJson().is("{foo:'qux'}");
 
-		assertThrown(()->c.writeProperties(null, a, true)).is("Argument 'section' cannot be null");
+		assertThrown(()->c.writeProperties(null, a, true)).is("Argument \"section\" cannot be null.");
 	}
 
 	//====================================================================================================
@@ -852,8 +852,8 @@ public class ConfigTest {
 
 		assertThrown(()->c.getSectionAsBean("T", ABean.class)).is("Unknown property 'bar' encountered in configuration section 'T'.");
 		assertThrown(()->c.getSectionAsBean("T", BBean.class)).is("Unknown property 'bar' encountered in configuration section 'T'.");
-		assertThrown(()->c.getSectionAsBean(null, ABean.class)).is("Argument 'section' cannot be null");
-		assertThrown(()->c.getSectionAsBean(null, BBean.class)).is("Argument 'section' cannot be null");
+		assertThrown(()->c.getSectionAsBean(null, ABean.class)).is("Argument \"section\" cannot be null.");
+		assertThrown(()->c.getSectionAsBean(null, BBean.class)).is("Argument \"section\" cannot be null.");
 	}
 
 	//====================================================================================================
@@ -888,7 +888,7 @@ public class ConfigTest {
 		assertObject(c.getSectionAsMap("T")).asJson().is("{}");
 		assertNull(c.getSectionAsMap("U"));
 
-		assertThrown(()->c.getSectionAsMap(null)).is("Argument 'section' cannot be null");
+		assertThrown(()->c.getSectionAsMap(null)).is("Argument \"section\" cannot be null.");
 	}
 
 	//====================================================================================================
@@ -912,7 +912,7 @@ public class ConfigTest {
 		assertEquals("qux", a.getFoo());
 
 		assertThrown(()->c.getSectionAsInterface("T", ABean.class)).is("Class 'org.apache.juneau.config.ConfigTest$ABean' passed to getSectionAsInterface() is not an interface.");
-		assertThrown(()->c.getSectionAsInterface(null, AInterface.class)).is("Argument 'section' cannot be null");
+		assertThrown(()->c.getSectionAsInterface(null, AInterface.class)).is("Argument \"section\" cannot be null.");
 	}
 
 	public static interface AInterface {
@@ -956,7 +956,7 @@ public class ConfigTest {
 		c.setSection("S1", Collections.<String>emptyList());
 		assertString(c).replaceAll("\\r?\\n", "|").is("#C3|#C4||[S1]|");
 
-		assertThrown(()->c.setSection(null, Arrays.asList("", "#C5", "#C6"))).is("Argument 'section' cannot be null");
+		assertThrown(()->c.setSection(null, Arrays.asList("", "#C5", "#C6"))).is("Argument \"section\" cannot be null.");
 	}
 
 	//====================================================================================================
@@ -982,7 +982,7 @@ public class ConfigTest {
 		c.setSection("S1", Collections.<String>emptyList(), m);
 		assertString(c).replaceAll("\\r?\\n", "|").is("#C3|#C4||a = b|[S1]|a = b|");
 
-		assertThrown(()->c.setSection(null, Arrays.asList("", "#C5", "#C6"), m)).is("Argument 'section' cannot be null");
+		assertThrown(()->c.setSection(null, Arrays.asList("", "#C5", "#C6"), m)).is("Argument \"section\" cannot be null.");
 	}
 
 	//====================================================================================================
diff --git a/juneau-utest/src/test/java/org/apache/juneau/http/EntityTag_Test.java b/juneau-utest/src/test/java/org/apache/juneau/http/EntityTag_Test.java
index 50f7bdf..4eb58c6 100644
--- a/juneau-utest/src/test/java/org/apache/juneau/http/EntityTag_Test.java
+++ b/juneau-utest/src/test/java/org/apache/juneau/http/EntityTag_Test.java
@@ -73,7 +73,7 @@ public class EntityTag_Test {
 		assertThrown(()->new EntityTag("foo")).is("Invalid value for entity-tag: [foo]");
 		assertThrown(()->new EntityTag("\"")).is("Invalid value for entity-tag: [\"]");
 		assertThrown(()->new EntityTag("")).is("Invalid value for entity-tag: []");
-		assertThrown(()->new EntityTag(null)).is("Argument 'value' cannot be null");
+		assertThrown(()->new EntityTag(null)).is("Argument \"value\" cannot be null.");
 		assertThrown(()->new EntityTag("\"a")).is("Invalid value for entity-tag: [\"a]");
 		assertThrown(()->new EntityTag("a\"")).is("Invalid value for entity-tag: [a\"]");
 		assertThrown(()->new EntityTag("W/\"")).is("Invalid value for entity-tag: [W/\"]");
diff --git a/juneau-utest/src/test/java/org/apache/juneau/reflection/ClassInfoTest.java b/juneau-utest/src/test/java/org/apache/juneau/reflection/ClassInfoTest.java
index 8efcf30..e4837b6 100644
--- a/juneau-utest/src/test/java/org/apache/juneau/reflection/ClassInfoTest.java
+++ b/juneau-utest/src/test/java/org/apache/juneau/reflection/ClassInfoTest.java
@@ -2134,7 +2134,7 @@ public class ClassInfoTest {
 
 	@Test
 	public void getParameterType_nullParameterizedType() {
-		assertThrown(()->aClass.getParameterType(2, null)).is("Argument 'pt' cannot be null");
+		assertThrown(()->aClass.getParameterType(2, null)).is("Argument \"pt\" cannot be null.");
 	}
 
 	@Test