Repository: MassimoCimmino/pygfunction (language: python)

Issue:
Automated testing using tox and continuous integration
This issue is to configure `tox` to automate the execution of tests into fresh Python environments, and to ~~configure github actions for continuous integration~~ re-configure continuous integration.

This issue follows #179.


Discussion / hints:
My only hesitation here is that Github is going to give you a set amount of included minutes, and after that it will be turned off. The goal is for Github to incentivize users to give them money. On private repositories I set up `runner`s and the testing is done on my own machines. For a repository that is open source, someone could place a bug on your machine by opening a pull request. I've considered setting up a `virtual environment` (possibly cross platform) so that on a rare occasion that some hacker does create a PR that it only corrupts the virtual environment. 
This is a very good point. GitHub actions seemed like a good idea but really any tool could do the job.

The Travis ci for pygfunction is currently inactive and that is what really needs to be fixed. I'll update the issue. Thanks.
I do like using GitHub actions because the tests are integrated into the pull request confirmation. I am only familiar with using runners (Github and Gitlab), and not familiar with Travis. Following public release of `ghedt` (currently expected early next year) I plan on setting up a hypervisor (specifically the open source [VirtualBox](https://www.virtualbox.org)) on my PC. With `VirtualBox` I will be able to allocate resources to virtual machines running Windows, Linux and possibly Mac OS. I plan for these virtual machines to have CI/CD runners installed as servers to perform tests on `ghedt` pull requests. Github only limits computing time if it is done on their machines, not if it is done locally with runners. 

Given that I can make what is described above work, I wouldn't mind also hosting the tests for `pygfunction` on those virtual machines. I believe I have adequate computing resources to handle tests for both `ghedt` and `pygfunction`.  We'd need to coordinate some things as that got closer, if you'd be interested in the option described here. 
It seems simple enough to configure github-actions that I am willing to try it out.

We can see how far 3000 min/month goes and reconsider later. With the current pace of development, pygfunction might not need much.

Fix patch:
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index ae75976f..00000000
--- a/.travis.yml
+++ /dev/null
@@ -1,16 +0,0 @@
-language: python
-
-python:
-  - "3.7"
-
-notifications:
-  email: false
-
-git:
-  depth: 3
-
-install:
-  - pip install -r requirements.txt
-
-script:
-  python -m unittest discover -s tests -p "*_test.py"
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1aa5581e..5182fe6c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,7 +9,7 @@
 ### Enhancements
 
 * [Issue 152](https://github.com/MassimoCimmino/pygfunction/issues/152) - Vectorized `coefficients_temperature` and `_general_solution` in `pipe` objects to accept depths `z` as an array. This speeds up calculations for `get_temperature` and `get_borehole_heat_extraction_rate` class methods.
-* [Issue 183](https://github.com/MassimoCimmino/pygfunction/issues/183) - Vectorized `pipes.multipole()` and `pipes._Fmk()` to decrease the calculation time of `pipes.thermal_resistances()`. A `memoization` technique is implemented to reduce computation time for repeated function calls to further speed-up the initialization of `Pipe` and `Network` objects.   
+* [Issue 183](https://github.com/MassimoCimmino/pygfunction/issues/183) - Vectorized `pipes.multipole()` and `pipes._Fmk()` to decrease the calculation time of `pipes.thermal_resistances()`. A `memoization` technique is implemented to reduce computation time for repeated function calls to further speed-up the initialization of `Pipe` and `Network` objects.
 
 ### Other changes
 
@@ -17,6 +17,7 @@
 * [Issue 172](https://github.com/MassimoCimmino/pygfunction/issues/172) - Refactored reports of calculation time to use `time.perf_counter()` instead of `time.time()`.
 * [Issue 173](https://github.com/MassimoCimmino/pygfunction/issues/173) - Refactored strings into f-strings instead of using `str.format()`.
 * [Issue 177](https://github.com/MassimoCimmino/pygfunction/issues/177) - Converted `setup.py` script to `setup.cfg` and `pyproject.toml` files. This is motivated by [PEP518](https://www.python.org/dev/peps/pep-0518/) and [PEP621](https://www.python.org/dev/peps/pep-0621/).
+* [Issue 180](https://github.com/MassimoCimmino/pygfunction/issues/180) - Configured `tox` and github actions for continuous integration.
 
 ## Version 2.1.0 (2021-11-12)
 
diff --git a/README.md b/README.md
index 1ad6591b..77cd1b10 100644
--- a/README.md
+++ b/README.md
@@ -1,9 +1,8 @@
 # pygfunction: A g-function calculator for Python
 
-[![Build Status](https://travis-ci.org/MassimoCimmino/pygfunction.svg?branch=master)](https://travis-ci.org/MassimoCimmino/pygfunction)
+![Tests](https://github.com/MassimoCimmino/pygfunction/actions/workflows/tests.yml/badge.svg)
 [![DOI](https://zenodo.org/badge/100305705.svg)](https://zenodo.org/badge/latestdoi/100305705)
 
-
 ## What is *pygfunction*?
 
 *pygfunction* is a Python module for the calculation of thermal response
diff --git a/pyproject.toml b/pyproject.toml
index 9787c3bd..0a9589bb 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,3 +1,9 @@
 [build-system]
 requires = ["setuptools", "wheel"]
 build-backend = "setuptools.build_meta"
+
+[tool.pytest.ini_options]
+addopts = "--cov=pygfunction"
+testpaths = [
+    "tests",
+]
diff --git a/requirements_dev.txt b/requirements_dev.txt
new file mode 100644
index 00000000..d4184520
--- /dev/null
+++ b/requirements_dev.txt
@@ -0,0 +1,5 @@
+numpydoc == 1.1.0
+sphinx == 4.0.1
+pytest == 6.2.3
+pytest-cov == 3.0.0
+tox == 3.24.5
\ No newline at end of file
diff --git a/setup.cfg b/setup.cfg
index 7833dacd..411649bd 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -31,6 +31,10 @@ install_requires =
 python_requires = >=3.7
 
 [options.extras_require]
-    doc =
-        numpydoc >= 1.1.0
-        sphinx >= 4.0.1
+doc =
+    numpydoc >= 1.1.0
+    sphinx >= 4.0.1
+testing =
+    pytest >= 6.2.3
+    pytest-cov >= 3.0.0
+    tox >= 3.24.5
diff --git a/tox.ini b/tox.ini
new file mode 100644
index 00000000..117a3594
--- /dev/null
+++ b/tox.ini
@@ -0,0 +1,18 @@
+[tox]
+minversion = 3.24.5
+envlist = py37, py38, py39
+isolated_build = true
+
+[gh-actions]
+python =
+    3.7: py37
+    3.8: py38
+    3.9: py39
+
+[testenv]
+setenv =
+    PYTHONPATH = {toxinidir}
+deps =
+    -r{toxinidir}/requirements_dev.txt
+commands =
+    pytest --basetemp={envtmpdir}


Test patch:
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
new file mode 100644
index 00000000..3fa120dc
--- /dev/null
+++ b/.github/workflows/test.yml
@@ -0,0 +1,31 @@
+name: Tests
+
+on:
+  push:
+    branches:
+      - master
+  pull_request:
+    branches:
+      - master
+    types: [opened, synchronize, reopened]
+
+jobs:
+  test:
+    runs-on: ${{ matrix.os }}
+    strategy:
+      matrix:
+        os: [ubuntu-latest]
+        python-version: ['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