How to use Git Hooks in Flutter to run your tests automatically

Flutter Aug 17, 2023

Automating software development processes can greatly improve efficiency and maintain code quality. Git hooks offer an effective way to automate various tasks within your development workflow. In this post, we will explore how to use Git hooks to automatically run tests in your Flutter project, ensuring that your codebase remains robust.

Installing requirements

Before we begin, ensure that you have Git installed. If not, you can easily set it up using this guide. Additionally, make sure you have Flutter installed on your machine. If Flutter is not already installed, you can do so using the instructions provided here.

Creating new project

Let us start by creating a fresh project. Open your preferred command-line interface (CLI). I will be using the command prompt on Windows. To access it, simply press the Windows button, type "cmd", and click on "Command Prompt".

In the CLI, navigate to the directory where you want to create your project. We can create a new project by executing the cd command followed by the desired path, and then hitting "Enter". In my case, the command is: cd Documents\Codeonwards\demo.

command_prompt_navigating_to_right_path

Now we can execute the following command to create a new project flutter create flutter_git_hooks.

command_prompt_creating_new_git_hooks_project

Creating a new Git project

Go to your GitHub, GitLab, Bitbucket, or other Git distributed control system and create a new project. I will be using GitHub.

git_hub_creating_new_git_hooks_project

Once you created a new Git project we need to execute the following commands to add our Flutter project to the Git project.

git_hub_command_list_for_adding_remote_repository

1. cd flutter_git_hooks: to navigate inside our project.

2. git init: to initialize Git.

3. git add .: to add all the files of our project.

command_prompt_navigating_to_project_initializing_and_adding_files_to_stagging

4. git commit -m "first commit": To capture a snapshot of the currently staged changes.

command_prompt_executing_git_commit

5. git branch -M main : To rename the default branch to main.

6. git remote add origin https://github.com/TijnvandenEijnde/flutter_git_hooks.git: To add a remote repository to our local project. Make sure that you use your own project here.

7. git push -u origin main: To upload the changes to the Git repository.

command_prompt_changing_default_branch_adding_remote_and_pushing_changes

Now you will see that our project is pushed to a Git distributed control system, in my case, GitHub.

git_hub_showing_that_the_files_are_pushed_to_the_remote_repository

Git hooks

Now with everything set up, we can start creating our Git hooks.

Creating the directory structure

Inside our project, we need to execute the following command: git config core.hooksPath .githooks/. This command is used to configure Git to look for the hooks in the given directory .githooks/.

Now it is time to open the new project inside your favorite IDE. I am using IntelliJ but this is not required any IDE should do fine. Once the project is opened we need to add the .githooks directory in the root directory.

Inside this directory, we need to add two files with the following names pre-commit and pre-push. These files should not have a file extension.

If you followed the instructions correctly your folder structure should look as follows:

|-- flutter_git_hooks/
  |-- .githooks/
    |-- pre-commit
    |-- pre-push 

To give you a more clear picture, here is a screenshot of the directory structure inside my IDE.

intellij_creating_githooks_directory_and_files

With the directory structure complete we can move on to writing the code for the files.

pre-commit

The pre-commit bash script we will create runs the Dart code formatter. The purpose of the script is to ensure that our codebase follows the Dart formatting rules. The pre-commit script will run every time you execute git commit. That is why we are not running any tests in this script, because running tests on every commit can become quite annoying, but know that it is possible. See the pre-commit script below:

#!/usr/bin/env bash

#dart format
printf "\e[33;1m%s\e[0m\n" 'Running dart format'

result=$(dart format .)
result_lines=$(echo "$result" | wc -l)

if [ "$result_lines" -gt 1 ]; then
  echo "$result"
  printf "\e[31;1m%s\e[0m\n" 'Dart format applied changes, please recommit'
  exit 1
fi

echo "$result"
printf "\e[32;1m%s\e[0m\n" 'Finished running dart format'

To make sure you understand the script we will break down the script step by step:

1.Shebang Line

#!/usr/bin/env bash

This line specifies the interpreter for the script, indicating that it should be executed using the Bash shell.

2.Comment

#dart format

This is a comment that indicates the purpose of the upcoming command: running the Dart code formatter.

3.Printf Command

printf "\e[33;1m%s\e[0m\n" 'Running dart format'

This line uses the printf command to print a colored message to the terminal. The color code \e[33;1m sets the text color to yellow, and \e[0m resets the text color. The message "Running dart format" is displayed in yellow.

4.Running dart format Command

result=$(dart format .)

This line runs the dart format command on the current directory (.), which attempts to format all Dart source files found in the directory. The output of the command (which includes any error messages) is captured and stored in the variable result.

5. Counting Result Lines

result_lines=$(echo "$result" | wc -l)

This line pipes the content of the result variable into the wc -l command, which counts the number of lines in the output. The resulting count is stored in the variable result_lines.

6. Checking for changes

if [ "$result_lines" -gt 1 ]; then
  echo "$result"
  printf "\e[31;1m%s\e[0m\n" 'Dart format applied changes, please recommit'
  exit 1
fi

This if block checks if the output of the dart format command covers multiple lines. If it does, it implies that file changes were made. The script then shows the formatted output, a red message indicating the need to recommit changes, and exits with code 1. Which typically indicates an error or non-successful execution.

7.Displaying a success message

echo "$result"
printf "\e[32;1m%s\e[0m\n" 'Finished running dart format'

If no changes were detected and the script has reached this point, the formatted output is displayed, and a success message is shown in green, indicating that the Dart formatting process has been completed successfully.

pre-push

The pre-push bash script will run Flutter's lint formatter and tests using the flutter analyze and flutter test commands. The script will run every time you execute git push. See the script below:

#!/usr/bin/env bash

#flutter analyze
printf "\e[33;1m%s\e[0m\n" 'Running flutter analyze'
result=$(flutter analyze)

if [ $? -ne 0 ]; then
  echo -e "$result \n"
  printf "\e[31;1m%s\e[0m\n" 'Flutter analyze found the above issues, please fix them before pushing'
  exit 1
fi

echo "$result"
printf "\e[32;1m%s\e[0m\n" 'Finished running Flutter analyze with 0 issues'

# flutter test
printf "\e[33;1m%s\e[0m\n" 'Running flutter test'
result=$(flutter test)

if [ $? -ne 0 ]; then
  echo -e "$result \n"
  printf "\e[31;1m%s\e[0m\n" '1 or more test(s) failed'
  exit 1
fi

echo "$result"
printf "\e[32;1m%s\e[0m\n" 'Finished running flutter test'

Let us break down the script step by step:

1.Shebang Line

#!/usr/bin/env bash

This line specifies the interpreter for the script, indicating that it should be executed using the Bash shell.

2.Comment

#flutter analyze

This comment indicates that the script is about to execute the flutter analyze command.

3.Printf Command

printf "\e[33;1m%s\e[0m\n" 'Running flutter analyze'

This line uses the printf command to print a colored message to the terminal. The color code \e[33;1m sets the text color to yellow, and \e[0m resets the text color. The message "Running flutter analyze" is displayed in yellow.

4.Running flutter analyze Command

result=$(flutter analyze)

This line runs the flutter analyze command, which checks if the code is following Flutter's linting rules. The output of the command (which includes any error or warning messages) is captured and stored in the variable result.

5.Checking for linting issues

if [ $? -ne 0 ]; then
  echo -e "$result \n"
  printf "\e[31;1m%s\e[0m\n" 'Flutter analyze found the above issues, please fix them before pushing'
  exit 1
fi

This block checks the exit status of the flutter analyze command. If the exit status is not equal to 0 (indicating any errors or issues were found during execution), the script proceeds to display the analysis output, a message in red indicating issues were found, and exits with an exit code of 1.

6.Displaying a success message

echo "$result"
printf "\e[32;1m%s\e[0m\n" 'Finished running Flutter analyze with 0 issues'

If no issues were found during static analysis and the script has reached this point, the analysis output is displayed, and a success message is shown in green, indicating that the analysis was completed successfully with no issues.

7.Comment

# flutter test

This comment indicates that the script is about to run the flutter test command for running tests in the Flutter project.

8.Printf Command

printf "\e[33;1m%s\e[0m\n" 'Running flutter test'

Similar to the previous cases, this line uses printf to print a yellow-colored message indicating that the script is running the tests.

9.Running Flutter Test Command

result=$(flutter test)

This line runs the flutter test command to execute tests for the Flutter project. The output of the command (which includes test results and any error messages) is captured and stored in the variable result.

10.Checking for failing tests

if [ $? -ne 0 ]; then
  echo -e "$result \n"
  printf "\e[31;1m%s\e[0m\n" '1 or more test(s) failed'
  exit 1
fi

This block checks the exit status of the previous test command. If the exit status is not equal to 0 (indicating test failures), the script proceeds to display the test results, a message in red indicating test failures, and exits with an exit code of 1.

11.Displaying a success message

echo "$result"
printf "\e[32;1m%s\e[0m\n" 'Finished running flutter test'

If all tests have passed and the script has reached this point, the test results are displayed, and a success message in green is shown, indicating that the tests were executed successfully.

Running the scripts

We have created the pre-commit and pre-push files. As mentioned earlier, the pre-commit file runs automatically when we use git commit, and the pre-push file runs automatically when we use git push.

Git commit with formatting errors

When we run git commit while we have formatting errors, the script will show the following:

executing_git_commit_while_having_formatting_errors

The script executes the dart format . command. Whenever it applies changes, the script notifies us to recommit the changes.

Git commit without format errors

When we run git commit without formatting errors, the script will show the following:

executing_git_commit_without_formatting_errors

If during the script the flutter format .  command did not apply any changes. The staged files will successfully be committed.

Git push with failing tests

When we run git push while we have failing test cases, the script will show the following:

executing_git_push_while_having_failing_test_cases

The script executes two commands: flutter analyze and flutter test. If any of these commands encounter issues/errors we will be notified and have to make the adjustments ourselves.

Git push without failing tests

When we run git push and all our tests succeed, the script will show the following:

executing_git_push_without_failing_test_cases

If both the flutters analyze and flutter test command do not find any issues/errors our stages changes will successfully be pushed to our remote repository.

Conclusion

Using Git hooks in your Flutter workflow can significantly boost efficiency and code quality. Automating tasks like formatting, analysis, and testing contributes to consistent and error-free code. In this post, we went over the essential steps to set up Git hooks, helping you to maintain a streamlined and reliable development process.

Tags