Comparison

Overview

As a fluent assertion library, Truth is similar to AssertJ, which was forked from FEST. Truth is significantly different from Hamcrest, which uses Polish prefix notation to compose assertions.

Why create Truth when AssertJ already exists?

In other words: Maybe Truth is better for some use cases, but why not just improve AssertJ instead? The reason is historical: AssertJ didn’t exist when we started Truth. By the time it was created, we’d begun using Truth widely internally, and we’d added some features that would be difficult to retrofit onto AssertJ.

Comparison vs. Hamcrest

Because Truth and Hamcrest differ so significantly, I’ll cover only the main points:

Comparison vs. AssertJ

Truth and AssertJ are very similar. If you’re happy with one, you probably wouldn’t benefit from switching to the other. So I provide this comparison mostly for people who don’t currently use either. I’ll try to be objective, but I acknolwedge that I’m in a better position to explain our own decisions than AssertJ’s. If you identify something wrong, missing, or misleading, please let us know.

Stability

AssertJ, while it continues to add APIs, rarely removes them anymore.

Truth has not reached 1.0. We’re still making changes, particularly in removing and reworking our extensibility APIs.

Number of assertion methods

AssertJ has more: more classes of assertions (AssertJ, Truth) and more methods per class (AssertJ, Truth). That includes advanced features like reflective field comparisons.

More is not necessarily better: It can make it harder to find what you’re looking for, and it can mean you need to learn to read multiple styles of tests or understand the interaction of more features. But naturally, more features can also mean more convenience and power.

Failure messages

I think that AssertJ generally does this better at the moment. It follows an “expected: … but was: …” model, while Truth follows a “Not true that … was …” model. We plan to improve this, but AssertJ is better today.

Chaining

AssertJ supports multiple assertion calls on the same object in the same statement:

assertThat(list)
    .contains(x, y)
    .doesNotContain(z);

Truth does not.

Both libraries support “chaining” in the sense of a method that returns a new asserter for a sub-object:

For example, AssertJ supports:

assertThat(list).last().isEqualTo(x);

And Truth supports:

assertThat(multimap).valuesForKey(x).containsExactly(y, z);

Our philosophy has been that it’s clearer to support only one kind of chaining, but I suspect that the AssertJ style is generally clear, too, and it can be convenient.

Assertion methods for other libraries’ types

AssertJ provides assertions for several libraries’ types. As of this writing, its home page lists Guava, Joda-Time, DB, Neo4j, and Swing.

Truth includes assertions for Guava and Protocol Buffers.

Both have third-party extensions, such as for Android types (AssertJ, Truth). I don’t have a feel for the overall size of each ecosystem.

Platform support (Android, GWT)

AssertJ supports Android (though I had to use 2.x because the dexer rejected 3.x, even when I used only Java6Assertions). Possibly this was an issue with my build setup.

Truth supports Android in all its versions. The downside is that it requires you to look in a separate class for Java 8 assertions.

Truth also supports GWT, but we haven’t made the necessary artifacts available outside Google. If you’d be interested in using open-source Truth under GWT, let us know.

Writing your own assertion methods

Both support this. A few notes on differences:

Failure strategies

AssertJ supports standard, fast-fail assertions. It also supports “soft” assertions, with which you can perform multiple checks and see all their failures, not just the first.

Truth supports both of these, though its “soft assertions” API (Expect) does not let you divide a single test method into multiple “groups” as AssertJ does. It additionally supports assumptions and custom FailureStrategy implementations. This support also underlies its utility for testing user-defined assertion methods.

I had some problems with AssertJ’s soft assertions:

These seem fixable in principle, but they demonstrate some of the reasons that we chose to make FailureStrategy a fundamental part of Truth.

Migration

AssertJ provides a tool to automatically migrate from JUnit and other frameworks to AssertJ.

Truth has one, but it’s only for JUnit, and it’s currently only available inside Google.

Bug-proneness

Both libraries have some sharp edges:

Under AssertJ, assertThat(someLongThatIsEqualTo1).isNotEqualTo(1) passes, even though you probably didn’t mean it to. Under Truth, it fails as intended.

Under Truth, assertThat(listOfStrings).doesNotContain(integer) passes, even though your test is probably buggy. Under AssertJ, it doesn’t compile.

Conditions

AssertJ supports Hamcrest-style “conditions.”

Truth does not. We encourage people to instead write custom Subject implementations, which IDEs can better surface during autocompletion.

And more

This list is not exhaustive. Let us know if you think we’re missing something significant.

Examples

Some typical examples that can highlight the similarities and differences of the common assertions can be found below.

Equality

// Truth
assertThat(actual).isEqualTo(expected);

// AssertJ
assertThat(actual).isEqualTo(expected);

// Hamcrest
assertThat(actual, equalTo(expected));

// JUnit
assertEquals(expected, actual);

Null checking

// Truth
assertThat(actual).isNull();

// AssertJ
assertThat(actual).isNull();

// Hamcrest
assertThat(actual, nullValue());

// JUnit
assertNull(actual);

Boolean checks

// Truth
assertThat(actual).isTrue();

// AssertJ
assertThat(actual).isTrue();

// Hamcrest
assertThat(actual, is(true));

// JUnit
assertTrue(actual);

Double comparisons

// Truth
assertThat(actualDouble).isWithin(tolerance).of(expectedDouble);

// AssertJ
assertThat(actualDouble).isCloseTo(expectedDouble, Offset.offset(offset));

// Hamcrest
assertThat(actualDouble, closeTo(expectedDouble, error));

// JUnit
assertEquals(expectedDouble, actualDouble, delta);

Assume/Assumption (JUnit’s skipping behavior)

// Truth
assume().that(actual).isEqualTo(expected);

// AssertJ
// n/a

// Hamcrest
assumeThat(actual, equalTo(expected));

// JUnit
assumeEquals(expected, actual);

Expect (fail-at-end)

// Truth
expect.that(actual).isEqualTo(expected); // supplied by @Rule

// AssertJ
softly.assertThat(actual).isEqualTo(expected); // @Rule with JUnitSoftAssertions

// Hamcrest
// n/a

// JUnit
// n/a