How to Run CI Tests with GitHub Actions ⚙️
Tags: github, workflows, ci, testing
Published: 2022-01-22 06:48:47

Thanks to [GitHub Actions](https://github.com/features/actions), we can now set up workflows for our git repository very quickly without any configuration from the GitHub platform. Indeed, we just need to create a specific .yml file in `.github/workflows` folder of your repository.

Having a CI running on every "git pull" (or scheduled with a cron setting in the GitHub Action) increases the quality of each pull request made against your project. 
You can run performance (PHPStan), code analysis tools (ESLint/Prettier), formatting scripts (PHP CS/CodeSniffer), unit tests, etc.

## Create your 1st GitHub Action 🎉

In this article, we will create a GitHub action that runs the unit tests of your application. However, you can literally create any kind of GitHub action for the workflows of your repository 😃 

First, go to your GitHub repository, and then create a `.github/workflows`. 

In your `workflows` folder, create a file called `test.yml` or `ci.yml`.

Inside it, you will start with the name of the CI Action (as it will be shown in the GitHub Actions tab). 

In our cast, let's do `name: Tests` 🙂

Then, you can mention what event you would like GitHub to listen to thanks to `on:`. I usually mention `on: [push,pull_request]`. That way, it will run on every `git push` and pull request.

Then, we can start declaring the jobs of our GitHub Action, `jobs: run:` where we mention each action we want to job to execute for us.

`runs-on` is on what OS you would like your job to be executed on.  In my case, it will just be Linux and Windows so that I can mention `runs-on: [ 'ubuntu-latest', 'windows-latest' ]`. `-latest` suffix means it will use the newest OS available.


So, so far, we have this 👇

```yaml
name: Tests

on: [push, pull_request]

jobs:
  run:
    runs-on: [ 'ubuntu-latest', 'windows-latest' ]
    strategy:
      matrix:
        php-versions: ['7.3', '7.4', '8.0', '8.1']
    name: PHP ${{ matrix.php-versions }} Test on ${{ matrix.os }}

    # The set of steps will be below
    steps:
```

Now, we can mention the steps for our GitHub Action's jobs. They are the steps that need to be executed each time our GitHub Action is running.

```yaml
- name: Checkout
  uses: actions/checkout@v2

- name: Setup PHP
  uses: shivammathur/setup-php@v2
  with:
    php-version: ${{ matrix.php-versions }}
    extensions: mbstring
    coverage: none
    tools: phpunit:${{ matrix.phpunit-version }}, composer:v2

- name: Check PHP Version
  run: php -v

- name: Check Composer Version
  run: composer -V

- name: Validate composer.json & composer.lock
  run: composer validate --strict

- name: Install PHP dependencies
  run: composer install --no-interaction --no-progress --no-suggest

- name: Run Test Suite
  run: vendor/bin/phpunit
```

The few job steps such as
```yaml
- name: Check PHP Version
  run: php -v
```
and

```yaml
- name: Check Composer Version
  run: composer -V
```
are just there to give you further information about the version running on your CI in case you need to investigate an issue coming from your GitHub Action.


---
And the following
```yaml
- name: Validate composer.json & composer.lock
  run: composer validate --strict
```
is used to validate the `composer.json` file before installing the dependencies.


Finally, the last step will execute the unit tests
```yaml
- name: Run Test Suite
  run: vendor/bin/phpunit
```

---

Once you are all done with the configuration of your CI file, GitHub will automatically run your new workflow action you just added. 

On new commit pushed or pull requests (depending on what event your configuration (`on: [push,pull_request]`), GitHub will show the status of each CI run.


![GitHub Action on Pull Requests](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/38k4p3nvpv5aeu2l6x1t.png)
*👆 There, the CI for the Pull Request shows that all steps are passing ✅*

---

![Commit status based on your GitHub Action](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fohd68zm5evekf5dtcbq.png)
*👆 There, the Action job passed ✅*

---

![Commit failing](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bxqkhq0odkf456xrx1ul.png)
*👆 There, the job failed due to an error with the unit test ❌ *

---

## Speed & Caching

With CI, it's always a good practice to cache the needed dependencies. Some CI (Travis, Circle CI or others) aren't free and you will need to pay per usage. When you are caching them, they get faster and consume fewer resources. That way, they will also be cheaper to be executed. 


If you use Composer, you can add those two steps in your GitHub Action's jobs.
```yaml
- name: Get Composer Cache Directory
  id: composer-cache
  run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
- name: Cache Composer dependencies
  uses: actions/cache@v2
  with:
  path: ${{ steps.composer-cache.outputs.dir }}
  key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
  restore-keys: ${{ runner.os }}-composer-
```

Note: You can also use the commit hash `key: mycache-${{ github.sha }}` as the cache key to update the cache when a new commit has been pushed. This is usually useful of your project is just a library and that your composer.lock doesn't get changed.

### With NPM
```yaml
- name: Cache NPM deps
  uses: actions/cache@v2
  with:
    path: ~/.npm
    key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}
    restore-keys: ${{ runner.os }}-npm-

```

### If you use Pipenv with Python,

```yaml
- name: Cache Pipenv
  uses: actions/cache@v2
  with:
    path: |
      ~/.cache/pip
      ~/.cache/pipenv
      ~/.local/share/virtualenvs/
    key: pip-pipenv-${{ hashFiles('Pipfile.lock') }}
    restore-keys: pip-pipenv-
- uses: actions/setup-python@v2
  with:
    python-version: 3.9
- run: pip install pipenv
- run: pipenv install --deploy --dev
```

Resource: [GitHub: Caching dependencies to speed up workflows](https://docs.github.com/en/actions/advanced-guides/caching-dependencies-to-speed-up-workflows).

Example 👉 [https://github.com/pH-7/pH2Gravatar/blob/master/.github/workflows/test.yml](https://github.com/pH-7/pH2Gravatar/blob/master/.github/workflows/test.yml)



## Beyond testing actions 🚀

Like I said previously, you have GitHub actions for many other purposes other than running tests.

Indeed, developers often find themselves doing repetitive tasks that can be easily automated by CI 🎉

You can use GitHub actions for much more than just running tests such as [deploying your changes to GitHub Pages](https://github.com/marketplace/actions/deploy-to-github-pages), [re-publishing an NPM packages](https://github.com/marketplace/actions/npm-publish) or even an action that [creates a PR with your DEV.to .md post file](https://github.com/marketplace/actions/dev-to-posts-to-markdown).

---
{% youtube 9v9g722dulE %}

---

And you? What's your most common usage of the GitHub Actions? 😄

👉 Finally, if you would like to go further with PHP or JavaScript/Node, don't forget to check out my Udemy courses https://www.udemy.com/user/pierresoria/