# PyTest With GitHub Actions

> Build off previous work on PyTest by adding a GitHub action to run PyTest in CI.

- Canonical: https://dennisokeeffe.com/blog/2021-08-08-pytest-with-github-actions
- Published: 2021-08-08
- Updated: 2021-08-08
- Tags: beginner, python, 100daysofpython

> This is Day 20 of the #100DaysOfPython challenge.

This post will build off previous work done in the [Python Unit Testing With PyTest](https://blog.dennisokeeffe.com/blog/2021-07-26-python-unit-testing-with-pytest) post to add a [GitHub Action](https://docs.github.com/en/actions) for running test runner jobs on a push event to the repo.

The final project code can be found on my [GitHub repo](https://github.com/okeeffed/hello-pytest-github-actions).

## Prerequisites

1. Familiarity with [Pipenv](https://pipenv.pypa.io/en/latest/). See [here](https://blog.dennisokeeffe.com/blog/2020-07-04-abcs-of-pipenv) for my post on Pipenv.
2. Read [Python Unit Testing With PyTest](https://blog.dennisokeeffe.com/blog/2021-07-26-python-unit-testing-with-pytest) if you are unfamiliar with Python.

## Getting started

Let's clone the original PyTest repo in `hello-pytest-github-actions`.

```s
$ git clone https://github.com/okeeffed/hello-pytest.git hello-pytest-github-actions
$ cd hello-pytest-github-actions

# Install the deps
$ pipenv install

# Prepare the file for the GitHub action
$ mkdir -p .github/workflows
$ touch .github/workflows/pytest.yml
```

At this stage, we can test that our repo is working correctly with PyTest locally:

```s
$ pipenv run pytest
============================== test session starts ==============================
platform darwin -- Python 3.9.6, pytest-6.2.4, py-1.10.0, pluggy-0.13.1
rootdir: /path/to/code/blog-projects/hello-pytest
collected 3 items

tests/test_math.py ...                                                    [100%]

=============================== 3 passed in 0.01s ===============================
```

At this stage, we are ready to set things up for our GitHub action!

## Adding the test script

In of the `Pipfile`, we want to add a `[scripts]` section to add a `test` script:

```s
[[source]]
url = "https://pypi.org/simple"
verify_ssl = true
name = "pypi"

[packages]

[dev-packages]
pytest = "*"

[requires]
python_version = "3.9"

[scripts]
test = "pytest"
```

That `test` script will simply call `pytest`. We will use this script within our GitHub action.

In order to also prepare our GitHub action to give us more vebose information, we will also be passing a `-v` flag to our call to `pytest`.

We can test things are working as expected by running `pipenv run test -v` from our terminal:

```s
$ pipenv run test -v
========================== test session starts ===========================
platform darwin -- Python 3.9.6, pytest-6.2.4, py-1.10.0, pluggy-0.13.1 -- /Users/dennisokeeffe/code/blog-projects/hello-pytest/.venv/bin/python
cachedir: .pytest_cache
rootdir: /Users/dennisokeeffe/code/blog-projects/hello-pytest-github-actions
collected 3 items

tests/test_math.py::test_add PASSED                                [ 33%]
tests/test_math.py::test_subtract PASSED                           [ 66%]
tests/test_math.py::test_multiply PASSED                           [100%]

=========================== 3 passed in 0.02s ============================
```

The verbose flag gives us more information about which test ran and passed. This can be helpful for debugging in CI.

## Adding the GitHub action

We are now ready to add the GitHub action. Within the `.github/workflows/pytest.yml` file that we created earlier, add the following:

```yml
# .github/workflows/app.yaml
name: PyTest
on: push

jobs:
  test:
    runs-on: ubuntu-latest
    timeout-minutes: 10

    steps:
      - name: Check out repository code
        uses: actions/checkout@v2

      # Setup Python (faster than using Python container)
      - name: Setup Python
        uses: actions/setup-python@v2
        with:
          python-version: "3.x"

      - name: Install pipenv
        run: |
          python -m pip install --upgrade pipenv wheel
      - id: cache-pipenv
        uses: actions/cache@v1
        with:
          path: ~/.local/share/virtualenvs
          key: ${{ runner.os }}-pipenv-${{ hashFiles('**/Pipfile.lock') }}

      - name: Install dependencies
        if: steps.cache-pipenv.outputs.cache-hit != 'true'
        run: |
          pipenv install --deploy --dev
      - name: Run test suite
        run: |
          pipenv run test -v
```

Here we are doing a couple of things:

1. Creating a job call `PyTest`.
2. Running this job on a `push` event to the repository.
3. Running the job on `ubuntu-latest`.
4. Setting a custom timeout of 10 minutes (albeit this is overkill, feel free to omit).
5. Setting up the Python environment for the latest in version `3.x`.
6. Install `pipenv` and `wheel`.
7. Install dependencies with a cache set to be the hash of the lockfile.
8. Running the test suite that we setup the command and tested before.

That is all that we need for this repo to be working!

> Be sure at this stage that you have set up your own origin remote for the repo.

At this stage, all we need to do is commit the code and push the repo and the job will be available under the actions tab in the GitHub UI.

![Action working as expected](https://cdn.dennisokeeffe.com/assets/2021-08-08-pytest-with-github-actions/1-pytest-github-action-complete.png)

## Summary

Today's post demonstrated how to use GitHub actions to test Python code on a push to the remote repository. We used the `pytest` testing framework to test our code.

## Resources and further reading

1. [The ABCs of Pipenv](https://blog.dennisokeeffe.com/blog/2020-07-04-abcs-of-pipenv)
2. [Pipenv](https://pipenv.pypa.io/en/latest/)
3. [GitHub Actions](https://docs.github.com/en/actions)
4. [Creating a Python GitHub Action](https://docs.github.com/en/actions/guides/building-and-testing-python)
5. [Final project](https://github.com/okeeffed/hello-pytest-github-actions)
6. [Python Unit Testing With PyTest](https://blog.dennisokeeffe.com/blog/2021-07-26-python-unit-testing-with-pytest)

_Photo credit: [`amseaman`](https://unsplash.com/@amseaman)_
