Do you usually run your tests before you commit, to check your work?

Do your tests also get run by a pre-commit hook?

This is helpful in the worst case — the pre-commit hook has your back when you forget — but in the typical case it’s harmful:

  • Disrupts flow
  • Doubles cost
  • Adds no marginal benefit
  • Discourages frequent small well-tested commits
  • Engenders learned helplessness

We can fix this.

Enter Greencently

With all tests recently green, Greencently waves you through. No need to learn anything. Commit quickly and confidently, staying in flow. Now the pre-commit hook also has your back when you remember.

Integrate Greencently into your build, and maybe tomorrow you’ll commit more often. Maybe next week you’ll invest further in the cost-effectiveness of your tests. But most important in this decision-making moment: before the day is up, you’ll have saved more time than you spent.

Setup

Follow the JUnit 5 instructions on GitHub.

What about other test frameworks?

Yes please! Let’s teach every test framework to do this. Here’s the whole algorithm:

Given a completed test run
When it included all possible tests in the project
And all of them ran green
Then a particular file's timestamp gets updated

For JUnit 5, we do this with:

  1. JUnit5Planner to discover and enumerate all tests in a given project the same way the framework itself does (ideally via a public API call)
  2. JUnit5Summary to report results of a test run
  3. JUnit5Listener to observe what just ran and compare against our plan

Extensions for JUnit 4 and TestNG could presumably look similar and fit into the existing codebase, while non-JVM frameworks (such as Jest, RSpec, CMake, GoogleTest, Unity, or CppUTest) would look a bit different and might need their own homes. I’m very happy to pair.

For other ways to help, see the GitHub issues.

Isn’t this risky?

Compared to not trying new things: A hook with Greencently reduces the risk that working conditions will never improve. In software development, safety and speed are not inherently at odds. Faster feedback breaks the false dichotomy, but might feel out of reach. Greencently provides immediate relief, requires zero organizational support, and incentivizes behavioral change.

Compared to telling people to just make their tests faster: A hook with Greencently wins back some of your team’s time and energy. Funding test-design improvements by reinvesting a bonus is more likely to succeed than by trying to get priorities to change. (Also, are we worried that if tests are slow only once per commit, we won’t care enough to speed them up?)

Compared to no pre-commit hook whatsoever: A hook with Greencently reduces the risk that any given commit has not been well tested.

Compared to pre-commit hook sans Greencently: A hook with Greencently reduces the likely size and risk of any given commit, and stops discouraging good test-running habits. (Also, is there honor in waiting longer for no additional feedback? Or do we only trust our tests after they’ve been green more than once?)

Compared to git commit --no-verify: A hook with Greencently does not change the existing risk of folks committing code that has been modified (whether maliciously or unintentionally) since the tests last passed. If this is a concern for you, you’re already addressing it by failing your deployment pipeline when tests fail. With Greencently automatically handling one of the reasons developers sometimes decide to skip pre-commit checks, it’s less likely that other checks (such as linters) will be skipped by mistake.

For Gradle users, can the build cache cover this use case?

Yes: if you run ./gradlew test all green and then run it again without having changed anything, Gradle knows not to run the tests again.

But for our use case, the Gradle build cache is too slow (and also too Gradle-specific). In a race, IntelliJ might well finish running all the tests again before command-line Gradle from the pre-commit hook can even decide not to run them. Greencently’s timestamp file is faster and works with any build tool.

For IntelliJ users, can a Run Configuration cover this use case?

Yes (via @rradczewski):

  1. Create a Shell Script configuration “Clever Test Task”
  2. Execute script text touch .when-all-tests-were-green-intellij
  3. Add a “Before launch” task -> Run Another Configuration -> your existing all-tests task (with “Activate tool window” selected, so you see the output)
  4. Whenever you’d run all tests, run Clever Test Task instead

The timestamp file gets updated if — and only if — the “Before launch” task succeeds.

For our use case, this is a little fragile. The only way to get a commit speedup from having run tests is to run them within the IDE, in a very particular way.

Does Greencently play well with test && commit || revert?

Yes. Try it in pre-push instead of pre-commit.

Does Greencently play well with continuous testing?

Yes, they’re orthogonal. An IDE plugin like Infinitest will (probably) never run all the tests, but if it ever does, Greencently will (probably) save you a little time.

Endorsements

Completely antithetical to test-driven development.

Please do not use and do not advertise that extension. It’s going to cause damage to your teams and others’.
Andrea L. on LinkedIn

Greencently is intended to reduce barriers to TDD, so if you can figure out what he’s worried about, please do let us know.