In this article, we want to explore how to automatically test our code with GitHub Actions when pushing updates to our remote repository. We will look at a minimal example to discover the main building blocks of GitHub Actions and learn how to set it up. Prerequisites Some basic knowledge of programming is assumed, as well as an understanding of the concept of Continuous Integration and Continuous Delivery ( CI/CD). The code examples are in JavaScript but should be understandable without JS knowledge. The concepts and ideas are transferrable to other programming languages and frameworks. What is GitHub Actions? GitHub Actions is a CI/CD platform that automates building, testing, and deploying code. It allows us to create that are triggered by An example is a workflow that deploys the code to production when a pull request is merged into the main branch. workflows events. Another example would be a workflow to run the test suite when a pull request is opened or updated which is what we will look at in this article. The repository can be set up to only allow certain activities after a workflow passes successfully, e.g., merging a pull request could be only allowed after workflows for testing, lining, and formatting the code have passed. GitHub Actions even goes one step further and allow for the creation of workflows that extend beyond the code. It is possible, for example, to create a workflow that automatically adds an appropriate label to issues that are being created in the repository. Example Project To play around with GitHub Actions, we will look at a very simple example in Node.js. We will write a basic function that adds two numbers and implement a unit test for that function using Jest. We will then create a GitHub Actions workflow to automatically run the test suite on every PR and every push to the main branch. You can head over to the on GitHub to see the code and the repository setup. demo repository Since this is a very hands-on demo, it could be worth it to fork the repository, try the steps below for yourself and extend the code for more complex scenarios. However, just reading the article should give you a good overview and provide you with a basic understanding of GitHub Actions. Our setup is quite simple, a Node.js project with Jest as a dev dependency for unit testing. We will start by creating a file with a simple function: app.js sum function sum(a, b) { return a + b; } module.exports = { sum }; Next, we will create a file with one test case for the function: app.test.js sum const sum = require("./app").sum; test("add two numbers", () => { expect(sum(3, 5)).toBe(8); }); Finally, let’s add a script to our : test package.json { ... "scripts": { ... "test": "jest" }, ... } We can run our test suite from the command line via . npm test While most developers probably run the test suite before pushing any changes to the code base, that step can be easily forgotten. Furthermore, running the tests in larger projects can take quite a long time and we might be tempted to skip the tests when having to push something urgently. Therefore, we want to make sure that the tests run automatically with every change to the codebase. Let us look at how GitHub Actions can help achieve that. Workflow for Automated Testing We have a very basic app and a test suite that we can run from the command line. Now, let’s automate that step and create a GitHub Actions workflow for testing the code. The goal is to have the test suite run automatically every time we push a change to the branch or create a pull request. main Building the workflow To add workflows to a GitHub repository, we create a top-level folder with a subfolder . In there, we add a YAML file for every workflow we want to have. In our case, that will be only one file : .github workflows test.yml name: test on: push: branches: - main pull_request: branches: - main jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - uses: actions/setup-node@v3 with: node-version: "16" - run: npm ci - run: npm test Let’s look at the building blocks of that file in more detail. name: test 👉 Quite straightforward: The name of our . workflow on: push: branches: - main pull_request: branches: - main 👉 The block describes which trigger the workflow. In this case, we want our workflow to run on any pushes to the branch and when a pull request into is created or updated. on events test main main jobs: test: runs-on: ubuntu-latest 👉 The block lists all the workflow consists of. A job is executed on a and consists of one or more . In our case, we have only one job called which runs on an Ubuntu runner. jobs jobs runner steps test GitHub provides a set of runners to choose from, e.g. Ubuntu, macOS and Windows Server. More complex workflows consist of multiple jobs, e.g. a build-and-deploy workflow could contain jobs to first validate the code, then build it and then deploy it. When there are multiple jobs in a workflow, they are executed in parallel by default but it is possible to run jobs sequentially by defining dependencies. steps: - uses: actions/checkout@v3 - uses: actions/setup-node@v3 with: node-version: "16" - run: npm ci - run: npm test 👉 The block describes exactly what our job does. steps test Each step can either run a reusable or a custom script. action The provides numerous actions for different environments and use cases. GitHub marketplace To not reinvent the wheel, we use two of those actions in our first two steps to checkout the code and install Node v16 on our Ubuntu runner which we specified in the block. jobs In the next two steps, we run two commands to install the npm dependencies and execute the command from our . This is only possible because we set up the environment in steps 1 and 2 with the pre-built actions. test package.json These steps could easily be adapted for other environments and programming languages. That’s it, we are done building our workflow. 🥳 Adding the workflow to the repository To activate the workflow we just created, all we have to do is push our code with the new folder to our GitHub repository. The workflow will then automatically run on the specified events. .github/workflows We can also go to the repository settings and set up branch protection rules for the branch, so that pull requests into can only be merged when the workflow runs successfully. main main test If we now create a new PR into , we can see that GitHub automatically runs our workflow and that merging is not possible before the checks pass. main test is a PR where the tests pass and merging is allowed. Here To see an example of a failing pipeline, have a look at . this PR ( You need to be logged in to a GitHub account to inspect those PRs.) Note: Next Steps We have seen how to set up automated testing with just a few lines of code using GitHub Actions, pretty cool. 😎 Of course, our example was very basic and we barely scratched the surface of what GitHub Actions is capable of. Therefore, here are some tips and resources for digging deeper: peruse the documentation extend our example project with some more workflows, e.g. for linting and formatting the code add GitHub Actions to more complex projects Thanks for reading and have fun building things! 🛠️