Have you ever encounter situation that you, or any of your colleagues commit code, that was not working?
It could be a class with missing semicolon, that causes compile failure on deploy.
It could be small change that changed system behavior that made unit tests fail.
Or worse… it could be changed behavior that was not discovered until the user encounters it.
It happened to me several times that I or someone else did not check projects’ build after merge/rebase to master branch and just pushed changes to remote. It is not safe, and should be prevented.
How? Let me introduce you pre-commit git hook.
Git hooks are shell scripts which execute when some git actions occur. They can be found under projects’
.git/hooks path, for instance
.git/hooks/pre-commit.sample. To make the script working, we need to remove
.sample extension from script file.
Pre-commit git hook, which is the subject of current post, fires when we type commit command like
git commit -m "Initial commit". Script is being executed and while it won’t reach some error, and finish with success output code, then our commit is burnt on destination branch.
This is initial
pre-commit.sample script created by git:
Rejecting unwanted commits
With pre-commit git hook git helps us to prevent unwanted commits from our branches. When our script returns 1, it indicates that error occurred, and pre-commit script failed. In that situation commit won’t appear on desired branch.
We can use this hook, to create test harness script. This script should build our project, then run every test, to ensure that changes have not destroyed application behavior. It would be nice to also check whether application can be started or not.
I’ve recently created pre-commit script, that runs test harness script for my project:
Firstly it runs test harness script with
if [ $? -ne 0 ]; then statement it checks latest command output code. Success is indicated by 0 return code, error by 1.
My pre-commit hook looks simple. The whole magic happens in
Test harness script
In software testing, a test harness or automated test framework is a collection of software and test data configured to test a program unit by running it under varying conditions and monitoring its behavior and outputs.
I wanted to create script that prevents me from commit broken code. I’m only a human and I can forget to check everything before I commit my changes, so errors can occur.
I wrote five steps which help me to deliver clean, not broken code to my application repository:
- Compile application
- Run unit and integration tests
- Run web application
- Run automated tests on application that is running
- Shutdown web application
Instead of doing these steps each time manually I’ve created
Firstly I compile code.
Secondly I run unit and integration tests. This happens during
mvn clean package command in
Then, application is baked under
/target folder. So in the third step I run built application and I’m waiting for it’s running state –
The fourth step –
runAutomatedTests, invokes requests to my web application, started at third step. Automated tests may took a while – even a few minutes – so it is great opportunity to go to the kitchen and make some coffee. 🙂
Fifth step –
tearDown is just shutting down running application by killing process on specified port. Cleaning up port also occurs before third step or on every script failure after third step –
shutdownServerOnPort function – to kill the process when we exit the script.
Let’s try to commit some work which touch application behavior and brings new tests on board.
As you can see, script ends up with success state, so commit was added to my master branch.
Test harness script was executing more than one minute. If we do not have a time – for example we must fix production bug as quick as possible, we can skip all hooks using
--no-verify flag, like:
git commit -nm "Quick fix".
As you have seen, git hooks can be very useful. I’ve just presented one of them – pre-commit – which can play a big role by preventing repository contributors for doing unwanted commits.
Mixing test harness with pre-commit ensures you that your commit will not break down building artifact flow (compile errors, test failures) or application behavior (automated tests, end-to-end tests failures) after you push it to origin repository.
This strategy helps me a lot in everyday coding.