Kapt's Hidden Test Costs

My work laptop is a 13" MacBook Pro. I picked it partially because I dislike big laptops and partially to keep myself honest in an ongoing modularization project and improving build times.

My work laptop is slow.

As a sign that build times forcing function is working, I recently went through our codebase at work and removed annotation processing from all of our tests. There were only a handful of cases, usually using AutoValue to generate little builders for test fakes.

After removing them, I noticed that kapt (along with stub generation) was still running 🤨. That's weird. After digging around a bit, I found out that the kaptTest Gradle configuration extends from the standard kapt configuration, so it was inheriting all of its processors.

Sure, they didn't run for very long with no processor inputs, but they still ran and took anywhere from 2 to 10 seconds each. Kapt is notoriously sensitive to classpath changes and reruns often, potentially having cache-invalidating effects on downstream tasks with it. On top of this, they're likely invalidated every time you change main sources while iterating on tests.

So how can we fix?

Experiment 1: Awkward, but we can try just... removing that extension in Gradle.

configurations
  .getByName("kaptTest")
  .setExtendsFrom(emptyList())

Ran again - no dice. The kapt and stubs tasks ran even though they had no processors defined.

Experiment 2: So what about the nuclear option?

tasks
  .matching {
    it.name.startsWith("kapt") && it.name.endsWith("TestKotlin")
  }
  .configureEach { it.enabled = false }

Now we're in business. Fortunately, this doesn't seem to break downstream KotlinCompile tasks and gives us back those wasted kapt cycles by skipping them entirely.

Surprisingly, this behavior appears to be intentional. I've commented asking for reconsideration, but for now I'd recommend doing the above and disabling kapt in any tests that don't use annotation processing.