Introduction
Husky is a tool for easily managing Git hooks in web development projects. Git hooks are features of Git that automatically execute custom scripts when specific events occur, such as before a commit or before a push, allowing the execution of specific scripts.
By using Husky, these hooks can be configured in the project's package.json
file, making it easy to incorporate scripts that utilize hooks into the project. This facilitates efficient management of code quality maintenance tasks such as linting (checking syntax and adherence to style guides) and automated testing.
With Husky, it is possible to, for example, automatically execute code linting before commits or run tests before pushing. If any issues are found, the push can be aborted. This prevents low-quality code from being added to the repository and is particularly useful for preventing issues before they occur, especially in team development scenarios.
In this article, I will explain how to set up and use Husky in a React app, including practical examples. And this article aims to be an introductory step to learning Husky, focusing only on the basics and avoiding complex content.
Tip: What is Lint?
In programming, Lint refers to tools that analyze code to identify syntax errors, style issues, potential bugs, and inefficiencies without executing the code.
For JavaScript, ESLint and JSHint are popular options.
Lint contributes to development efficiency and quality improvement by detecting syntax errors and other issues before code execution. For example, it can detect various problems such as undeclared variables, missing semicolons, unused variables, and incorrect operators used in equality comparisons.
Setup
In this article, I will introduce specific examples of utilizing Git hooks with Husky. We'll set up hooks in the package.json
file to execute linting (static code analysis) before commits and run tests in the workflow.
First, run the following command in your terminal:
npx husky-init && npm install
And also, if you don't have eslint in your project, please install it.
Running this command generates the .husky
directory and adds the Husky configuration to your package.json
(the .husky
directory is added to your project's root directory).
Next, we will configure the Git hooks. In the package.json
file, we will set up scripts to be executed with Husky. In the example below, we configure a script to run linting on the pre-commit
hook and a script to run tests on the pre-push
hook.
{
"name": "your-project-name",
"version": "1.0.0",
"scripts": {
"lint": "eslint . --fix",
"test": "jest"
},
"husky": {
"hooks": {
"pre-commit": "npm run lint",
"pre-push": "npm run test"
}
}
}
With this setup, the following actions are performed:
- Before commit (pre-commit): The
eslint . --fix command
is executed to lint all files in the project. If issues can be automatically fixed, they are corrected (otherwise, errors would be reported). If linting detects errors, the commit is aborted. - Before pushing (pre-push): The Jest command is executed to run the project's test suite. If any tests fail, the push is aborted. By the way, I wrote a related article about testing with Jest and using
npm run test
, which you might find interesting. You can read it here.
Tip: .husky
I want to share a point where I stumbled personally. Depending on the environment, even if the pre-commit
is configured as described above, the git commit might trigger npm run test
instead of npm run lint
, which happened in my environment, resulting in unintended behavior. In such cases, you can check the pre-commit
configuration file in the .husky directory to see if it contains npm run test
. Changing this to npm run lint
might resolve the issue. Also, the behavior and configuration method may change depending on the version of Husky, so if things don't work as expected, it's advisable to do some research on your own.
Using Husky
npm run lint
command analyzes and checks your code against defined standards to determine if there are any issues, informing you of the results. The main purpose of this article is to automate this command to run every time you commit with Git.
After setting up as described above, executing the git commit
or git push
commands will automatically trigger the configured hooks. For example, if you attempt to commit code with inappropriate coding style, the pre-commit
hook will execute linting, and if there are issues, the commit will be aborted. This process helps maintain code quality while streamlining the development process.
// TextInput.js
import React, { useState } from 'react';
function TextInput({ onSave }) {
const [inputValue, setInputValue] = useState('');
const handleChange = (event) => {
setInputValue(event.target.value);
};
const handleSubmit = () => {
onSave(inputValue);
setInputValue('');
};
return (
<div>
<input
type="text"
value={inputValue}
onChange={handleChange}
placeholder="Enter text"
/>
<button onClick={handleSubmit}>Save</button>
</div>
);
}
export default TextInput;
For instance, let's see a js file above. Since the code is syntactically correct, performing git add
and git commit
will trigger a lint check, but since no errors are found, you can proceed to push the code.
Next, let's look at an example where it fails.
// TextInput.js
import React, { useState } from 'react';
function TextInput({ onSave }) {
const [inputValue, setInputValue] = useState('');
const handleChange = (event) => {
setInputValue(event.target.value);
// missing closing brace for handleChange
const handleSubmit = () => {
onSave(inputValue);
setInputValue('');
};
return (
<div>
<input
type="text"
value={inputValue}
onChange={handleChange}
placeholder="Enter text"
/>
<button onClick={handleSubmit}>Save</button>
</div>
);
}
export default TextInput;
In this code, I intentionally removed a closing brace, causing the code not to function as intended. When you attempt git add
and git commit
in this state, lint will notify you of the error, preventing the commit from being made. In this way, linting processes help identify violations of coding standards and potential errors, serving as a valuable tool in team-based environments like Git to inform of potential issues in advance and prevent problems before they occur.
Tip: npm run lint vs npm run test
If you're a beginner in testing, you might not initially grasp the clear distinction between npm run lint
and npm run test
(I certainly didn't).
npm run lint
and npm run test
target different aspects of the software development process, serving distinct purposes. Here's a casual summary of what they entail:
npm run lint
☕️ Purpose: Checks the style and syntax of your code. Linting points out violations of coding conventions and potential errors, helping maintain a consistent code style. In JS, primary tools include ESLint, JSHint, etc.
❓ What it does: Identifies unused variables, inconsistencies in indentation, presence or absence of trailing semicolons, use of undefined variables, etc. This enhances code readability and maintainability, establishing a consistent coding style across the project and preventing minor errors or deviations from best practices.
npm run test
☕️ Purpose: Ensures the application functions correctly. Testing verifies that the code works as intended and confirms that existing features remain unbroken during refactoring or adding new functionality. Popular options include Jest, Mocha, Jasmine, etc.
❓ What it does: Conducts unit tests, integration tests, end-to-end tests, etc, at various levels of the application to guarantee its quality, allowing early detection of bugs when adding new features or modifying existing code.
Husky can be used in combination with both commands, but they can be utilized differently depending on the goal. If the objective is to maintain code quality and style, you should run npm run lint
. If the goal is to verify the functionality and quality of the developing application, then npm run test
is appropriate.
Depending on the situation, you can choose between these two tests.
Conclusion
This article comes from what I've learned so far. I'm still learning, so sorry if some parts are unclear for you! But I hope it helps if you're studying testing.
Thank you for reading, happy coding!
Top comments (1)
thanks for good example