This page contains guidelines for writing Truth failure messages.
Facts
Truth failure messages are composed of key-value pairs, which we call “facts.” For example, here’s a message with 2 facts:
expected: 0.0
but was : -0.0
And here’s another with the same 2 keys but different values. Truth displays it differently because the values contain newlines:
expected:
<!doctype html>
<html>
</html>
but was:
<html>
</html>
Keys vs. values
The general pattern is: Keys are fixed strings, and values are runtime values.
Keys are fixed strings
When a Subject
implementation reports a failure, it generally passes a fixed
set of keys.
In the normal case, there will be one fact describing the expected value. Its name is often similar to the name of the assertion method:
expected to be greater than: // for isGreaterThan(...)
expected to contain: // for contains(...)
expected: // for isEqualTo(...)
That fact is usually followed by a “but was” fact, as in the examples at the top of the page. (Occasionally, there’s no need for “but was.” For example, take “expected not to be null.” Adding “but was null” would add no information.)
Values are runtime values
Typically, they’re the parameters the test passed in. For example:
- The value of “expected” is the parameter passed to
isEqualTo(...)
. - The value of “but was” is the parameter passed to
assertThat(...)
.
Sometimes the facts contain more than just the toString()
representation of a
parameter. For example:
expected : true
an instance of class: java.lang.Boolean
but was : (non-equal value with same toString())
an instance of class: java.lang.String
Or:
expected instance of: java.lang.CharSequence
but was instance of : java.lang.Double
with value : 4.5
But even in unusual cases, the first fact usually describes the expected value. Its key usually includes “expected,” and its value is usually a parameter to the assertion method.
Don’t mix the two
This key-value pattern is easy to extend to most assertions:
expected to start with: abc
but was : abd
expected to start with:
<html>
<body>
but was:
<html>
<head></head>
<body></body>
</html>
The main point is to separate fixed strings from runtime values. If you don’t, users may have more trouble identifying the actual and expected values. For example:
// NOT RECOMMENDED!
expected: to start with abc
but was : abd
// NOT RECOMMENDED!
expected:
to start with <html>
<body>
but was:
<html>
<head></head>
<body></body>
</html>
Because the fixed string “to start with” is part of the value instead of the
key, the expected prefix is no longer aligned with the actual value.
Additionally, the first key is now named “expected,” a name that is normally
reserved for equality assertions. A user might initially read these failure
messages as saying that the values were expected to literally be “to start with
abc” and “to start with <html>\n<body>.” And to top it all off, the
code in your Subject
implementation has to be slightly more complex because
it’s concatenating strings.
The main downside of this pattern is that the keys can sometimes be long.
Assertions about a derived object
Some assertion methods test a specific part of the original object. For example:
assertThat(list).hasSize(5);
assertThat(optional).hasValue("foo");
These methods should delegate to a Subject
for the derived property:
public void hasSize(int size) {
// ... check that the actual value isn't null ...
check("size()").that(actual.size()).isEqualTo(size);
}
public void hasValue(Object value) {
// ... check that the actual value isn't null, check isPresent() ...
check("get()").that(actual.get()).isEqualTo(value);
}
Such code produces messages like:
value of: optional.get()
expected: boo
but was : foo
Delegating to another Subject
offers several advantages:
- You don’t need to reimplement the failure messages of methods like
isEqualTo()
, which contains special cases for objects with identicaltoString()
values, different classes, etc. - For
isEqualTo()
in particular, you automatically get aComparisonFailure
with diff-style output when appropriate. - You don’t need to reimplement the logic of the checks. In particular, you
don’t need to handle unusual cases like
null
,double
equality, and array equality. - The failure message focuses on the “expected” and “but was” values of the sub-object, with context available but on a separate line, merged with any context from other derived steps.
Phrasing of keys
In addition to the points above, we suggest:
Consider omitting “to be”
In some of the examples above, the key implicitly contains “to be.” For example:
expected: 0.0
expected instance of: java.lang.CharSequence
These could be written as:
expected to be: 0.0
expected to be an instance of: java.lang.CharSequence
We recommend omitting “to be” when the key is clear without it. This keeps keys short, which encourages users to read them fully in case they contain more interesting information. (We omit “an” in “an instance of” for similar reasons.)
In some cases, omitting words is less clear of a win. For example:
// somewhat discouraged
expected starts with: abc
This phrasing might suggest that the test supplied a complete expected value but that Truth is displaying only the begining of it. We suggest:
expected to start with: abc
Additionally, negative assertions seem to read better with “expected not to be…” than just “expected not….”
“expected to be empty” vs. “expected an empty string”
Most Subject
implementations requre a certain kind of object – in this
example, a string. Saying “expected an empty string” suggests that the test had
the ability to pass a non-string to the assertion, such as:
assertThat(someInt).isEmpty();
But such a call wouldn’t compile. Thus, failure messages shouldn’t emphasize the type of value that was expected so much as the properties of the value.
(It may still be useful to mention the type, as in “expected string to be empty.” We don’t have a recommendation for whether to prefer this or “expected to be empty.”)
There’s one notable exception: when the value under test is unexpectedly null. In this case, the value isn’t really of the expected type. Thus, it makes sense to say “expected a string that… but was null.”
Occasionally use a key with no value
In some cases, there is nothing more to say:
expected a present optional
This could be written as:
// NOT RECOMMENDED!
expected: a present optional
But usually “expected” is reserved for values, not descriptions of properties that the values are expected to have.
Don’t say “expected to be equal to”
Just say “expected.” “to be equal to” is assumed.
Similarly, don’t say “expected to be equal to one of.” Say “expected to be one of.”
The default assumption is that values are compared using equality. Including “equal to” boilerplate makes it harder to users to spot unusual cases when they do arise, like “expected specific instance.”