Skip to content

Automatic code rewriting for AssertJ using error-prone and refaster

License

Notifications You must be signed in to change notification settings

palantir/assertj-automation

Repository files navigation

Autorelease

assertj-automation Download

Automatic code rewriting for AssertJ using error-prone and refaster.

Why

(1) Improve failure messages in existing codebases

By making code changes like the following, we can drastically improve failure messages:

-assertTrue(continents.size() == 7);
+assertThat(continents).hasSize(7);

From this hard-to-debug message:

Expected :true
Actual   :false

To this clearer example. An engineer can now quickly diagnose what went wrong - perhaps they left a foo in recently:

java.lang.AssertionError:
Expected size:<7> but was:<8> in:
<["Africa", "Asia", "Europe", "North America", "South America", "Antarctica", "Australia", "foo"]>

There many more sub-optimal patterns that this automation can detect and fix. By codifying these patterns into tooling that can be run local and on CI, we can reduce the burden on code reviewers because all contributions will already comply with AssertJ best practises.

These fixes also help facilitate the move from JUnit 4 -> JUnit 5, as raw JUnit4 assertions can be eliminated (e.g. assertTrue and assertEquals) before actually doing the JUnit 4 -> 5 migration.

(2) Consolidate on a single assertions library

When codebases use a mixture of Hamcrest, AssertJ, raw JUnit assertions (both 4 and 5), Google Truth etc, contributors can be unsure which to use, leading to unnecessary back-and-forth during code review if they happened to pick the wrong one.

In practise, because many of these libraries are quite similar, just picking one and committing to it is a reasonable strategy. We picked AssertJ because its fluent API is has nice auto-completion properties and it's nicely extensible.

Usage: net.ltgt.errorprone

Error-prone is maintained by Google and can be used with Bazel, Maven, Gradle, Ant. Use the following Gradle:

plugins {
  // we assume you are already using the Java plugin
  id "net.ltgt.errorprone" version "0.6"
}

dependencies {
  annotationProcessor "com.palantir.assertj-automation:assertj-error-prone:<latest>" // see badge above

  errorprone "com.google.errorprone:error_prone_core:2.3.4"
  errorproneJavac "com.google.errorprone:javac:9+181-r4173-1"
}

tasks.withType(JavaCompile) {
  options.errorprone.errorproneArgs += [
    '-Xep:PreferAssertj:ERROR',
    // ... include other rules too
  ]
}

Note: refaster rules can't yet be applied from the net.ltgt.errorprone plugin, see the baseline plugin below.

Alternative usage: com.palantir.baseline

Palantir's Baseline family of plugins sets up error-prone and allows applying auto-fixes from both refaster and error-prone. Run ./gradlew compileTestJava -PerrorProneApply -PrefasterApply to apply the fixes.

plugins {
  id 'com.palantir.baseline` version '2.43.0'
}

To ensure the automatically refactored code remains readable by humans, we then run the ./gradlew formatDiff command provided by palantir-java-format. This surgically reformats the lines of code that were touched, while preserving the rest of the file.

buildscript {
  dependencies{
    classpath 'com.palantir.javaformat:gradle-palantir-java-format:0.3.9'
  }
}
apply plugin: 'com.palantir.java-format'