Being nice to colleagues with git pre-commit hooks
For my current contract I'm doing a huge amount of testing on a system that is very fun to hack on. I've also been given a lot of rope leeway in how I test. Thus, I use Test::Most quite heavily, but I've a tiny problem:
use Test::Most 'die';
That should halt the test file at first failure, a feature I use quite a bit when developing to ensure that test failures don't scroll past when I'm actively hacking on code. However, when I added subtests to Test::Builder
a few years ago, I made sure that a fatal failure in a subtest would cause the subtest to fail, but not the entire test program. Well, darn. That means use Test::Most 'die'
doesn't quite do what I want it to do in this case.
Being pragmatic, I did the simplest thing which can possibly work. I used the testing equivalent of weapons of mass destruction:
use Test::Most 'bail';
Thus, any test failure will cause the test suite to bail out, immediately, no questions asked! I don't mind this behavior at all when I'm hacking on a single test program, but I don't want to commit that, so I must remember to remove that before I commit.
Well, of course I committed that; the more manual steps you make anyone take, the more likely they are to forget one. Time to make sure I don't do that again.
One of the lovely features of git
is how easy it is to modify its behavior at various stages. In this case, I didn't want to commit my 'bail' hack, so I wrote the following and saved it as .git/hooks/pre-commit
:
#!/bin/bash
RESULT=$(git grep -l 'Test::Most.*bail' t | wc -l)
if [ $RESULT -ne 0 ]; then
echo -e "\e[01;31m$0 failed. Cannot commit:\e[0m" >&2
git grep 'Test::Most.*bail' t
exit 1
fi
exit 0
For this tiny hack, I check to see if I've committed my nuclear option and if I have, I get output similar to this:
$ git commit
.git/hooks/pre-commit failed. Cannot commit:
t/import/merger.t:use Test::Most 'bail';
t/import/parser_errors.t:use Test::Most 'bail';
I knew about parser_errors.t
having 'bail' because I added that for testing my pre-commit hook, but I didn't know that merger.t
had the same problem!
I'm sure my colleagues would have been most unhappy had I merged this to integration.
Next up is stopping myself from committing $DB::single = 1;
.
Thx a lot for the tip with "die" and "bail". Didn't know about those... I think they will help me a lot in a current project.
I misread the title as meaning "How you can be nice to your colleagues who are afflicted with git pre-commit hooks." lol.
+1 with Boris on die and bail.
Don't ever put 'use Test::Most' in a .t file - just invoke it from the command line, when you need it:
perl -Ilib -MTest::Most=die t/mytest.t
or
prove -lr -MTest::Most=die t
What's the nuance with that? I use Test::Most myself on a test script, and I read on the perldoc that I can invoke prove or the script itself with the {BAIL,DIE}ONFAIL=1 environment variable set to get similar behavior to the Test::Most explicit imports.
Why not use Test::Most in a .t file? You lose most of its benefits if you don't.
Aren't you using it to get the die-on-fail functionality? Perhaps I missed something.
Ether, if I was only using it for the die-on-fail, then your suggestion would be perfect, but there's a lot more than that. From the synopsis, instead of typing this at the beginning of your test files:
You type this:
Since I loathe boilerplate and repetitive code, I did a heuristic analysis of the CPAN and bundled all of the most common testing modules into one, along with several other goodies, including explain() and show(). This is why it's become one of the most popular testing modules on the CPAN.
You know, I always wondered why is that behaviour not controllable by environment variables: During development I'd always want bailonfail but that should not go out to production. Not even the repository.
Gabor, I'm wondering if I'm misunderstanding you, but that behavior is configurable via environment variables.
Ovid, so either I can't read or it was added after I read the docs :) Thanks for pointing those out.
I also use the
$BAIL_ON_FAIL
environment variable instead of the'bail'
module parameter. In fact, I have a script (namedt
, for brevity) that runsprove
with this variable set. That way, when I want to do TDD, I uset
, and when I'm ready for full unit testing, I can usemake test
or whatnot.And if you work with multiple git repos and want to always be using the same pre-commit safety hook, you can just do something like: