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:
- Truth assertions are made with chained method calls, so IDEs can suggest the appropriate assertions for a given object.
- Hamcrest assertions are made with nested method calls, so users can compose assertions in arbitrary ways.
- Hamcrest’s APIs are also usable with mocking frameworks.
- But by supporting nesting and mocking frameworks, Hamcrest has the hard (perhaps impossible) job of bending Java’s generics system to its needs.
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.
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
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.
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.
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:
And Truth supports:
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.
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.
Writing your own assertion methods
Both support this. A few notes on differences:
- The two are verbose in different places: AssertJ requires (at least by
return this;at the end of each method; Truth requires a method that returns a
Subject.Factory(generally implemented as a method reference). Both require some verbose generics (and even an additional abstract class if you want to properly support subclassing), though we have plans to improve things in Truth.
Subjectclass provides some convenience methods that build a failure message for you. (But we need to improve the format of that message, so the methods will change soon.)
- AssertJ offers a tool to generate the methods for you.
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 (
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:
- They don’t work with chained assertions like
last(). Specifically, they fall back to behaving as fail-fast assertions.
- They don’t work on Android. The implementation of soft assertions uses bytecode generation, which doesn’t work there.
These seem fixable in principle, but they demonstrate some of the reasons that
we chose to make
FailureStrategy a fundamental part of Truth.
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.
Both libraries have some sharp edges:
assertThat(someLongThatIsEqualTo1).isNotEqualTo(1) passes, even
though you probably didn’t mean it to. Under Truth, it fails as intended.
assertThat(listOfStrings).doesNotContain(integer) passes, even
though your test is probably buggy. Under AssertJ, it doesn’t compile.
AssertJ supports Hamcrest-style “conditions.”
Truth does not. We encourage people to instead write custom
implementations, which IDEs can better surface during autocompletion.
This list is not exhaustive. Let us know if you think we’re missing something significant.
Some typical examples that can highlight the similarities and differences of the common assertions can be found below.
// Truth assertThat(actual).isEqualTo(expected); // AssertJ assertThat(actual).isEqualTo(expected); // Hamcrest assertThat(actual, equalTo(expected)); // JUnit assertEquals(expected, actual);
// Truth assertThat(actual).isNull(); // AssertJ assertThat(actual).isNull(); // Hamcrest assertThat(actual, nullValue()); // JUnit assertNull(actual);
// Truth assertThat(actual).isTrue(); // AssertJ assertThat(actual).isTrue(); // Hamcrest assertThat(actual, is(true)); // JUnit assertTrue(actual);
// 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);
// Truth expect.that(actual).isEqualTo(expected); // supplied by @Rule // AssertJ softly.assertThat(actual).isEqualTo(expected); // @Rule with JUnitSoftAssertions // Hamcrest // n/a // JUnit // n/a