Enforce coding style check and run tests before each code commit using Git hooks
Tags: git, node, codequality, typescript
Published: 2022-02-04 20:28:24

## Intro

Whenever we develop a new feature for our software, it's crucial to identify the problems in the code as soon as possible, ideally before committing the code to VCS. Because the later we discover bugs, the more expensive to fix them. 

We can easily detect common problems like failing unit tests and wrong coding styles locally. We can even go further by enforcing it before every commit so that the code is clean and passes the tests before going to the Pull Request phase.

## Sample project

Let's say I have a Node project with a function called `sum()` like below

```typescript
const sum = (a: number, b: number): number => a + b;

export { sum };
```

I wrote a unit test for this function using [Jest](https://jestjs.io/):

```typescript
import { sum } from './math';

test('sum of 2 + 3 is 5', () => {
  expect(sum(2, 3)).toBe(5);
});
```

Furthermore, I have also set up [ESLint](https://eslint.org/) and [Prettier](https://prettier.io/) to check for coding style. (I will write a separate post to show you how to enable ESLint and Prettier in the Node project. This post mainly focuses on how to set up Git hook).

I added some scripts to my `package.json` file to run unit tests and check coding style with ESLint and Prettier

```json
{
  "name": "sample",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "jest",
    "lint": "eslint ./src/**/*.ts",
    "prettier": " prettier --check ./src"
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@types/jest": "^27.4.0",
    "@typescript-eslint/eslint-plugin": "^5.10.2",
    "@typescript-eslint/parser": "^5.10.2",
    "eslint": "^8.8.0",
    "eslint-config-airbnb-base": "^15.0.0",
    "eslint-plugin-import": "^2.25.4",
    "husky": "^7.0.4",
    "jest": "^27.4.7",
    "prettier": "2.5.1",
    "ts-jest": "^27.1.3"
  }
}
```
The commands we want to use are:

To run unit tests:

```
> npm test 
```

Checking code with ESLint:

```
> npm run lint
```

Format code with Prettier

```
> npm run prettier
```

## Create Git hook

To enforce running the three commands above before every code commit, we will use [Husky](https://typicode.github.io/husky/#/) to set up a pre-commit hook:

```
> npm i -D husky
> npx husky install
> npx husky add .husky/pre-commit "npm run prettier"
```

After running the commands above, you will see file `.husky/pre-commit` with command `npm run prettier` at the end. It meant, `npm run prettier` will run before every code commit.

```
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

npm run prettier
```

Append `npm run lint` and `npm test` to the end of the file

```
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

npm run prettier
npm run lint
npm test
```

Now we are done! Let's test our setup by creating our first commit:

```
> git add .
> git commit -m "my first commit."
```

The output will look like below:

```
> sample@1.0.0 prettier
> prettier --check ./src

Checking formatting...
All matched files use Prettier code style!

> sample@1.0.0 lint
> eslint ./src/**/*.ts


> sample@1.0.0 test
> jest

 PASS  src/math.spec.ts
  ✓ sum of 2 + 3 is 5 (2 ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        1.526 s, estimated 2 s
Ran all test suites.
[branch_1 ae6dfd0] my first commit.
 1 file changed, 1 insertion(+), 1 deletion(-)
```


As you can see in the result above, the three commands are automatically run. If one of the commands fails, the code will not be committed.