Enhancing Jest Test Execution Speed with GitHub Actions
Written on
Chapter 1: Introduction to Testing Methodologies
Many developers enjoy creating tests, and they often adopt methodologies like Test-Driven Development (TDD) or Behavior-Driven Development (BDD) to write even more tests before the actual implementation. The primary aim is to maximize test coverage, ensuring that the code remains robust against breaking changes and overall quality improves.
However, the pressing question of "how can we execute these tests more quickly in our pipelines?" arises daily. Waiting for upwards of 10 minutes for all tests to complete before merging pull requests can hinder productivity and delay important tasks. This issue is more than just a minor inconvenience; it affects metrics such as Mean Time to Repair (MTTR) and may even lead to violations of Service Level Agreements (SLAs) and Service Level Objectives (SLOs) due to a sluggish pipeline.
A well-functioning pipeline should operate as swiftly as a Ferrari. Let’s explore how we can enhance the speed of Jest test execution within GitHub Actions.
Section 1.1: Increasing Resources
The default GitHub Action runner is equipped with a 2-core CPU, which might lead one to consider self-hosted runners as an option. However, this introduces added complexity, requiring maintenance of operating systems, regular updates, and cloud configuration expenses. While this is a viable solution, it’s not necessarily the most straightforward.
To clarify further, let’s delve into the Jest Command Line Interface (CLI) and see how it can assist us in achieving our objectives.
Section 1.2: Selective Test Execution
A strategic approach is to only run tests that have been modified since the last commit. The rationale is straightforward: if all previous tests passed successfully, and the newly added tests also succeed, it stands to reason that everything is functioning correctly.
According to Jest's official documentation, you can use the --changed-since <commit_hash> option, which allows Jest to execute tests pertinent to changes since the specified commit. If the current branch diverges from the provided reference, only the local changes will be tested.
Your package.json would need to include the following:
And your workflow would execute the command as shown.
Nevertheless, this method has its drawbacks. For example, a change you make might inadvertently impact some hidden logic elsewhere, leading to tests that go unexecuted and potential failures.
This has been a rare occurrence over the years, but if you prefer to err on the side of caution, consider the next method.
Chapter 2: Parallel Test Execution
Why not split the tests into chunks and run them in parallel? This could potentially speed up the overall process.
To achieve this, we first need to familiarize ourselves with the Jest CLI. Utilizing the --listTests argument will output all tests, and adding the --json flag will format the output in JSON.
This will result in an array of strings that represent the paths to individual test files in JSON format. Next, we need to partition this array into N chunks. While one could write a script to iterate through and separate the array, I suggest utilizing JQ, a powerful tool for filtering and formatting.
JQ is a utility that processes input and generates output. It offers numerous built-in filters for extracting specific fields from objects or transforming data types.
Here’s how the command would look:
In summary, we take all tests formatted in JSON, pass them through JQ, which calculates the length of the array, divides it into 10 (the desired number of chunks), and uses the "_nwise" function to create separate arrays.
The outcome is an array of arrays.
To set up the job setup-test-chunks, we will use the command specified above, which will return the chunks along with their respective indexes.
Additionally, we create a job called tests-check that executes the tests using a strategy matrix, effectively cloning the job for each chunk. The fail-fast: false option means that if one job fails, the others will continue running. For more details on the strategy matrix, refer to the relevant documentation.
The action will cycle through the arrays of chunk IDs and execute each specific chunk. The end result will streamline your pipeline significantly.
In conclusion, while GitHub Actions is still evolving, it already offers a wealth of features. In our experience, employing the last approach has led to test execution speeds improving by approximately 3 to 5 times.
We invite you to share your thoughts and techniques in the comments.
Thank you for reading!
For additional insights, visit PlainEnglish.io. Subscribe to our free weekly newsletter, follow us on Twitter and LinkedIn, and join our Community Discord to connect with others.
This video provides a guide on running Jest automation tests on GitHub Actions with triggers for push, pull requests, and manual executions.
This video explores how to execute Jest tests on GitHub Actions, utilizing JS parallel jobs with the matrix feature in Node.js YAML configuration.