Open-source project
assertj/assertj avatar
assertj/assertj

AssertJ: assertions that know what type they are looking at

Fluent testing assertions for Java and the JVM

2,847 stars788 forksJavaApache-2.0

At a glance

What is it?
A Java assertion library built around one idea, that assertThat on a String should offer String methods and assertThat on a Map should offer Map methods, with the whole surface reconstructed by your IDE as you type.
Who is it for?
AssertJ's design is one idea applied relentlessly: the assertion object should know the type of the thing it is inspecting, so the method names you need are already there. That is why `assertThat(underTest).` followed by code completion is the whole sales pitch, and why the library has held its position for over a decade in a category where the default choice is usually plain JUnit.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 17 days ago.
What is it written in?
Mainly Java, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 24, 2026, and from our analysis. They are not legal advice.

Editorial analysis

One entry point, and the type does the rest

The README states the ambition before anything else: a rich and intuitive set of strongly-typed assertions for unit testing, usable with JUnit, TestNG or any other test framework. The second paragraph is the argument, and it is short enough to quote. The idea is that disposal assertions should be specific to the type of the objects being checked, so if you are checking a `String` you use String-specific assertions, and if you are checking a `Map` you use Map-specific assertions to easily check the contents.

Everything else in the library follows from that. The usage pattern the README gives is a single expression, typed and then completed by the editor:

java
assertThat(underTest).

+ +There is nothing clever in that line and everything rides on it. A library that offered one generic assertion class would give you a method taking an `Object` and a string message, and you would be casting and formatting by hand. AssertJ instead returns a type-specific object, so the set of legal assertions after the dot is exactly the set that makes sense for that type, and the failure message comes from the same object that knew the type.

The README is explicit that the library does not own your test lifecycle. JUnit, TestNG or anything else runs the tests; AssertJ supplies the assertions. That separation is why it works as a drop-in for an existing suite, and also why adopting it does not mean arguing about runners.

Six modules, and the split tells you where the boundaries are

The composition section is the most useful part of the README for an adopter, because it shows that the library is not one artifact. A core module provides assertions for JDK types, with the README naming `String`, `Iterable`, `Stream`, `Path`, `File` and `Map` as examples. Around it sit separate modules for other ecosystems.

A Guava module covers Guava types, naming `Multimap` and `Optional`. A Joda Time module covers `DateTime` and `LocalDateTime`, and it lives in its own repository rather than this one. A Neo4J module covers graph types such as `Path`, `Node` and `Relationship`, also in a separate repository. A DB module provides assertions for relational database types, `Table`, `Row` and `Column`, again separately hosted. A Swing module provides a simple and intuitive API for functional testing of Swing user interfaces.

The pattern is consistent: everything about the JDK stays in `assertj-core`, and anything that depends on a third-party library is a separate module so that the core carries no dependency it does not need. That is the right architecture for a library this widely adopted, and it is also the practical advice. Most projects want `assertj-core` and possibly the Guava module. If you use Neo4J or Joda Time, expect to depend on a second artifact from a different repository.

The repository tree reflects the same division at build level. Alongside `assertj-core/` and `assertj-guava/` there are `assertj-bom/`, which is presumably a bill of materials for version alignment across the modules, `assertj-parent/` for the shared build configuration, and `assertj-tests/` as a separate test tree. There is also a `javadoc-theme/` directory and a `scripts/` directory, and the build is Maven with a wrapper checked in as `mvnw` and `mvnw.cmd` plus a `.mvn/` directory.

The badges are a maintenance policy in three lines

The top of the README carries four badges and each one is a statement about what the project considers its obligations.

The first is Maven Central, pointing at the `org.assertj:assertj-core` artifact on Sonatype's central portal. The second is Javadoc on javadoc.io. Those two together are the deployment contract: the library is on Maven Central and its API documentation is browsable outside the repository.

The third is CI on `main`. The fourth is more interesting: a workflow named Binary Compatibility. A library whose whole value is a large API surface cannot rename methods freely, because every rename breaks compilation in every project that uses it. A dedicated workflow that fails the build when the public API changes shape is how a library buys the freedom to add assertions without the fear of breaking its users, and running it on every push rather than on a release cadence means the check is cheap.

The fifth badge is a SonarCloud quality gate on the project `joel_costigliola_assertj-core`, reporting alert status. That is a maintained code quality metric wired into the build, and its presence alongside a `CLAUDE.md` at the repository root, an `.editorconfig`, a `CODE_OF_CONDUCT.md`, a `SECURITY.md` and a `PULL_REQUEST_TEMPLATE.md` describes a project with institutional habits rather than a hobby one.

There is also a `arcmutate-licence.txt` in the tree, which is a clue about how test quality is argued about internally. ARC stands for automatically generated mutants that should be caught by your tests. Shipping a licence for that tool alongside the code suggests the maintainers use mutation testing to check that the assertions library itself is well tested by its own suite, which is the right standard to hold yourself to when your product is test infrastructure. + +The contribution path is deliberately short in the README. You are encouraged to contribute any missing useful assertions, read the contributing section, and raise a PR. Missing assertions are treated as a normal contribution rather than a feature request, and the README offers the issue tracker as a discussion point when you want to argue for an assertion before writing it.

Three releases that show how the project absorbs change

The release list here is short, with three builds visible, and they happen to be a good sample of the three kinds of work this library does.

The most recent, `assertj-build-3.27.7` from 2026-01-24, is a security release. It fixes an XXE vulnerability in the `isXmlEqualTo` assertion, tracked as CVE-2026-24400 with a GHSA reference, and the notes thank two people by name for reporting it responsibly. It also deprecates `XmlStringPrettyFormatter` with no replacement, which is a more interesting signal than the fix: it says an API is being retired rather than fixed. The same release upgrades Byte Buddy to 1.18.3, the JUnit BOM to 5.14.1 and Guava to 33.5.0-jre, and fixes a Javadoc navigation issue in `assertj-guava` where links to `assertj-core` or Guava types carried an unnecessary header.

That last item deserves a moment. A library that mostly asserts on strings, numbers and collections has a large surface of XML assertions, and XML comparison is exactly the kind of feature that attracts parsers with a history of external entity problems. Any assertion library with `isXmlEqualTo` should be read as having an XML attack surface, and this one patched it. + +`assertj-build-3.27.6` from 2025-09-22 is a one-line fix, adding a missing export for `org.assertj.core.annotation` and crediting one contributor by handle. `assertj-build-3.27.5` from four days earlier is a compatibility release: Byte Buddy in 3.27.4 was not compatible with Java 25, so 3.27.5 upgrades to Byte Buddy 1.17.7, JUnit BOM 5.13.4 and Guava 33.4.8-jre. + +Put together, these three releases describe the maintenance rhythm precisely. A library this heavily used has to move with the JVM release cycle, because it depends on a bytecode generation library, and it has to respond to security reports in its less-traveled corners. The gap between the last release and the last push on 2026-09-23 suggests active work not yet cut as a build.

How this compares to what you are probably using now

The realistic comparison is against plain JUnit 5 assertions and against Hamcrest, and the difference is a difference of philosophy rather than of capability.

JUnit's built-in assertions are serviceable and they are everywhere. `assertEquals` takes two objects and an optional message, which means the failure message has to be built by you, and there is no notion of what type is being asserted beyond what the compiler infers from the overload. When two values differ, the message says they differ and shows the two values; it does not know that you were checking a collection's contents, so it cannot say which element was missing.

AssertJ inverts that. The assertion object holds the type, so it can produce a message that understands the domain, and it can offer assertions that would be nonsense for another type. The cost is that you depend on the library, and that you learn an API surface rather than a small fixed set of methods. For a team writing thousands of assertions, the completion-driven discovery is the argument; for a team that writes fifty tests, plain JUnit is fine and one fewer dependency is worth something.

Hamcrest sits in a third place. It is built around composable matchers rather than typed assertion objects, so the same matcher can be reused in a different framework or combined with `allOf` and `anyOf`. That is a different and also legitimate design, and migrating between the two is a real project rather than an import change.

There is a fourth option worth naming: `assertThat` exists in JUnit 4 as well, and the name collision causes real confusion in older codebases. If you are reading a legacy test and see a static import of `assertThat`, the first thing to check is whether it came from JUnit's `MatcherAssert` or from AssertJ, because the semantics differ enough to change what a test proves. + +What AssertJ does not do is provide a runner, a mocking library, or assertions for arbitrary third-party types without a dedicated module. Teams adopting it usually keep JUnit as the runner and add AssertJ for assertions, which is precisely the arrangement the README describes.

Reading the repository as evidence of intent

A few structural signals are worth pulling out, because for a mature project the layout says more than the marketing copy.

The presence of a dedicated binary compatibility workflow is the strongest one. It means the public API is treated as a contract, which in turn means the project can add assertions aggressively without asking permission. It also means you should expect deprecations to be announced in release notes and removed on a schedule rather than quietly.

The `assertj-bom/` module is the second. A bill of materials exists so that a project using core plus Guava plus one external module can align versions through a single import, which is the kind of small piece of infrastructure that only shows up once you maintain a library for a decade.

The `assertj-tests/` directory as a separate top-level module alongside `assertj-core/` is the third. Separating the project's own test suite from the shipped artifact is a packaging discipline signal, and combined with `arcmutate-licence.txt` it suggests the maintainers hold their own suite to a mutation-testing standard.

The remaining directories are ordinary and still informative. `assertj-parent/` holds shared Maven configuration, `eclipse/` and `.idea/` check in IDE settings so contributors get a consistent environment, `javadoc-theme/` customizes the generated documentation, `scripts/` holds maintenance tooling, and `.github/` holds the workflows whose badges you saw at the top.

On the numbers: 2,847 stars and 788 forks, with 251 open issues and a last push on 2026-09-23, Apache-2.0 licensed. The fork count is unusually high relative to stars, which usually means corporate or team adoption, and the issue count is high because an assertion library accumulates feature requests faster than anyone writes them. The topics list confirms the shape of the audience with `assertions`, `testing`, `typed-assertions`, `java`, `kotlin` and `groovy`, and the documentation homepage is `assertj.github.io` rather than a generated site.

Editorial conclusion

AssertJ's design is one idea applied relentlessly: the assertion object should know the type of the thing it is inspecting, so the method names you need are already there. That is why `assertThat(underTest).` followed by code completion is the whole sales pitch, and why the library has held its position for over a decade in a category where the default choice is usually plain JUnit. The tradeoffs are equally legible. AssertJ is assertion API only, so it does not replace a test runner, which is why the README says JUnit, TestNG or any other framework. Its core module targets JDK types specifically, so a Guava `Multimap` or a Neo4J `Path` needs a companion module rather than a workaround, and that module split is the main thing to evaluate before committing. The recent history also deserves a read. Release 3.27.7 fixed an XXE vulnerability in the `isXmlEqualTo` assertion tracked as CVE-2026-24400 and deprecated `XmlStringPrettyFormatter` with no replacement, and 3.27.5 was mostly spent making Byte Buddy compatible with Java 25, which tells you the library tracks the JVM release cycle closely. The 251 open issues against 2,847 stars are a function of assertion requests arriving faster than anyone can write them, not a sign of instability. Add `assertj-core` and pin the version, then rely on your editor's completion rather than memorizing method names.

Frequently asked questions

What is AssertJ used for?

AssertJ supplies the assertion calls in Java unit tests, replacing generic equality checks with type-specific ones. If the value under test is a `String` you get String assertions, if it is a `Map` you get assertions about its contents, and if it is an `Iterable` or a `Stream` you get assertions about ordering and elements. The entry point is `assertThat(underTest)`, and the returned object exposes only the assertions that make sense for that type, so failure messages can describe the domain rather than just printing two objects. AssertJ is assertion library only and runs under JUnit, TestNG or any other test framework.

What are the key differences between JUnit and AssertJ?

JUnit's built-in assertions are a small fixed set that works on any object, so the comparison is done for you and the failure message has to be constructed by you. AssertJ returns a type-specific assertion object from `assertThat`, so the available methods depend on the type of the value, the failure message is written for that domain, and assertions that make no sense for the type are not offered. JUnit also owns the test lifecycle, which AssertJ does not, so the usual adoption is to keep JUnit as the runner and use AssertJ for the assertions inside each test method.

Do I need a separate AssertJ module to assert on a Guava Multimap?

Yes. AssertJ is split into modules so that the core artifact carries no dependency it does not need. `assertj-core` covers JDK types such as `String`, `Iterable`, `Stream`, `Path`, `File` and `Map`. Assertions for Guava types like `Multimap` and `Optional` come from `assertj-guava`, while Joda Time, Neo4J, relational database and Swing assertions each live in their own module, several of them in separate repositories. Most projects need only the core module, occasionally plus the Guava one.

Does AssertJ replace JUnit or TestNG?

No. The README describes AssertJ as a set of strongly-typed assertions to use for unit testing with JUnit, TestNG or any other test framework, which makes the separation explicit. AssertJ provides the assertion API and JUnit or TestNG provides the runner, the lifecycle and the reporting. That is why dropping AssertJ into an existing suite is usually a matter of changing imports and static-importing `Assertions.assertThat` rather than restructuring the tests.

Official sources

  1. assertj/assertj on GitHub
  2. License: Apache-2.0
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/assertj-assertj.svg)](https://hysenlabs.com/projects/assertj-assertj)