Chapter 6: Test on Commit With Github Actions
Tags: github, testing, python, git
Published: 2022-10-21 12:37:26

- This chapter is part of the series: {% embed https://dev.to/zim95/understanding-the-python-tox-ecosystem-3noi %}
- The chapter builds upon the project structure from the previous chapter (Chapter 5). I suggest reading the chapter to understand the project structure. {% embed https://dev.to/zim95/chapter-6-test-on-commit-with-github-actions-39po %}
- So this is the structure of our project so far.
    
    ```markdown
    - src/
    	- something/
    		- __init__.py
    		- py.typed
    		- app.py
    - tests/
    	- __init__.py
    	- conftest.py
    	- test_app.py
    - .gitignore
    - LICENSE
    - pyproject.toml
    - README.md
    - requirements.txt
    - setup.cfg
    - setup.py
    - tox.ini
    
    ```
- ### Table of Contents:
    - [6.1 - The Need for Github Actions](#part-6_1)
    - [6.2 - Configuring Github Actions](#part-6_2)
    - [6.3 - Understanding Tox GH Actions](#part-6_3)
    - [6.4 - Adding build results to README](#part-6_4)

### 6.1 The Need for Github Actions <a name="part-6_1"></a>
- Everything we did with `Tox` is only local to our machine. That is, the tests pass in all the version, type check passes in all the versions and linting works in all the versions as well. However, all of it happens on our machine. That is, on our operating system.
- How do we know that it passes on other machines? Other operating systems?
- Well, this is the “AUTOMATED” part in `Automated Testing`.
- Here we will configure `Github Actions` to automatically run `Tox` every time we make a push to the main branch across different machines (OS).

### 6.2 Configuring Github Actions <a name="part-6_2"></a>

- To configure `Github Actions` we need another configuration file. In this case, an entire directory.
- We need to create the following files and directories: `.github/workflows/<name>.yml`.
- In this case, name can be whatever we want. We will call it, `tests.yml`.
- Let’s look at the `tests.yml` file.
    
    ```yaml
    name: Tests
    
    on:
      - push
      - pull_request
    
    jobs:
      test:
        runs-on: ${{ matrix.os }}
        strategy:
          matrix:
            os: [ubuntu-latest, windows-latest]
            python-version: ['3.6', '3.7', '3.8', '3.9']
    
        steps:
        - uses: actions/checkout@v2
        - name: Set up Python ${{ matrix.python-version }}
          uses: actions/setup-python@v2
          with:
            python-version: ${{ matrix.python-version }}
        - name: Install dependencies
          run: |
            python -m pip install --upgrade pip
            pip install tox tox-gh-actions
        - name: Test with tox
          run: tox
    ```
    
- We have the `name` field. This is the `name` that will show up on our `badge` in `README.md`.
- We specify that we want to run our `workflow` to run on `push` or `pull_request`.
- We only have one job. We call it `test`.
- `strategy->matrix` is a syntax we use in order to create all combinations of environments that we want to use.
    - We have:
        
        ```yaml
        os: [ubuntu-latest, windows-latest]
        python-version: ['3.6', '3.7', '3.8', '3.9']
        ```
        
    - So there will be 8 virtual environments:
        - ubuntu-latest, 3.6
        - ubuntu-latest, 3.7
        - ubuntu-latest, 3.8
        - ubuntu-latest, 3.9
        - windows-latest, 3.6
        - windows-latest, 3.7
        - windows-latest, 3.8
        - windows-latest, 3.9
    - All of these values are preconfigured Github options. We cannot write custom names for these sections. We can find the link to all the supported options at the Github Actions documentation.
- Now, we just define what are all the `steps`.
    - We checkout the repository.
        
        ```yaml
        - uses: actions/checkout@v2
        - name: Set up Python ${{ matrix.python-version }}
        ```
        
    - We setup the version of Python.
        
        ```yaml
        uses: actions/setup-python@v2
          with:
            python-version: ${{ matrix.python-version }}
        ```
        
    - Install the dependencies.
        
        ```yaml
        - name: Install dependencies
        	  run: |
        	    python -m pip install --upgrade pip
        	    pip install tox tox-gh-actions
        ```
        
- Run `tox`.
        
    ```yaml
    - name: Test with tox
        run: tox
    ```
        
- After the following steps. Whenever we commit and push to Github, we will see a build getting triggered. Github will automatically detect our workflow.
- We can play around the Github UI from there.
- Finally this is the project structure we have.
    ```
    - .github/
    	- workflows/
    		- tests.yml
    - src/
    	- something/
    		- __init__.py
    		- py.typed
    		- app.py
    - tests/
    	- __init__.py
    	- conftest.py
    	- test_app.py
    - .gitignore
    - LICENSE
    - pyproject.toml
    - README.md
    - requirements.txt
    - setup.cfg
    - setup.py
    - tox.ini
    ```

### 6.3 Understanding Tox GH Actions <a name="part-6_3"></a>
- Notice in the `tests.yml` file that we install `tox-gh-actions` in the steps. We need to understand what this is.
    
    ```yaml
    - name: Install dependencies
    	  run: |
    	    python -m pip install --upgrade pip
    	    pip install tox tox-gh-actions
    ```
    
- Github already has its own set of actions that mimics what `tox` does.
- We can use `tox-gh-actions` alone in our `tox.ini` file, if all we wanted to do was push to Github.
- However, we are setting up `tox` with different blocks, because we want to run the tests locally and `tox-gh-actions` only runs on Github. We cannot run Github Actions locally.
- `tox-gh-actions` is a tool to make `tox` and Github Actions work together.
- We need to give Github a way to map our `Tox` environments with the github environments.
- If we notice, we already have a `gh-actions` section on the `tox.ini` file.
    
    ```
    [gh-actions]
    python =
        3.6: py36, mypy, flake8
        3.7: py37
        3.8: py38
        3.9: py39
    ```
    
- The syntax is as follows:
    
    ```
    [gh-actions]
    python =
        <github_python_version1>: <tox_env1>, <tox_env2>
    		<github_python_version1>: <tox_env3>, <tox_env4>
    		...
        and so on
    ```
    
- All we do is list out the name of the `github environment` and map it to the corresponding `tox` environments.
- Again, our `tox` environment looks something like this:
    ```
    [tox]
    minversion = 3.8.0
    envlist = py36, py37, py38, py39, flake8, mypy
    isolated_build = true
    ```
- So we are basically mapping:
        - `3.6` to `py36`, `flake8` and `mypy`. This is because if we look at the configuration of these environments in `tox.ini`. Their `basepython` field is only `3.6`. Therefore, we did not map `flake8` and `mypy` to the other versions.
        - `3.7` to `py37`.
        - `3.8` to `py38`.
        - `3.9` to `py39`.

### 6.3 Adding build results to README <a name="part-6_4"></a>

- We can add the results of the build in our `[README.md](http://README.md)` file.
- We can do that by adding the following line on our `README.md`: `![Tests](https://github.com/<Username>/<ProjectName>/actions/workflows/tests.yml/badge.svg)`.
- So our `[README.md](http://README.md)` file looks something like this.
    
    ```markdown
    # Something
    A starter project to show how to set up and use automated testing in Python
    
    ![Tests](https://github.com/DummyUser/DummyProject/actions/workflows/tests.yml/badge.svg)
    ```
    
- So thats our article. Thank you for making it this far. To understand this article visually, checkout the video mentioned in the credits section.