Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enh: Add tests pages - run tests in ci and locally to guide #145

Merged
merged 58 commits into from
Jan 16, 2024
Merged
Show file tree
Hide file tree
Changes from 49 commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
16734fb
Fix: add initial tests content to guide
lwasser Sep 26, 2023
67351a1
ENH: fixes from Jonny's review
lwasser Sep 30, 2023
bc6a007
Fix: typos and cleanup
lwasser Oct 2, 2023
1b9d232
Fix: add example to tests ci page
lwasser Oct 3, 2023
5d843a4
Fix: edits from @namurphy
lwasser Nov 1, 2023
a55ab25
Update ci-tests-data/code-cov.md
lwasser Nov 1, 2023
cdd2c09
Fix: edits from @namurphy to run tests page
lwasser Nov 1, 2023
0e6f7d4
Fix: Rebase from main
lwasser Dec 15, 2023
10db00d
remove header file
lwasser Dec 15, 2023
c4b5422
Fix: conf.py cleanup
lwasser Dec 15, 2023
6aa3fd7
Fix: review fixes from Trevor
lwasser Dec 16, 2023
71a382e
Fix: edits from Nick
lwasser Dec 16, 2023
d41b4fe
Packaging image
lwasser Dec 16, 2023
8b28c84
Fix: remove unused block
lwasser Dec 16, 2023
53b1818
Update ci-tests-data/tests-ci.md
lwasser Dec 20, 2023
838fbd0
Fix: edits from @willingc to ci page
lwasser Dec 20, 2023
ace9515
Enh: Next two tests pages in guide
lwasser Dec 20, 2023
53dc089
Fix: spacing issue in conf.py during rebase
lwasser Jan 8, 2024
3d55eab
Fix: Edits from @ucodery
lwasser Jan 8, 2024
0455fa7
Fix: Edits from @midnighter
lwasser Jan 9, 2024
0960184
Fix: edits from review
lwasser Jan 9, 2024
2127510
Fix: circle ci redirect build update (#158)
lwasser Jan 9, 2024
7b7e3cc
Fix: missing quote
lwasser Jan 9, 2024
c877af0
Add: second lesson and images to pr
lwasser Dec 5, 2023
657784e
Fix: edits from @kierski
lwasser Dec 27, 2023
594cbeb
Fix: edits from @willingc
lwasser Dec 27, 2023
a419c08
Rename diagram
lwasser Dec 27, 2023
5cac943
Fix: edits from review
lwasser Dec 27, 2023
856dea6
Fix: edits from @ucodery
lwasser Dec 28, 2023
2c26e4b
Fix: Edits from @ucodery
lwasser Dec 28, 2023
8fa276f
Fix: cleanup spelling, flow, tox, etc
lwasser Dec 29, 2023
374f44a
Fix: botched rebase
lwasser Jan 3, 2024
9468b05
Fix: edits from trevor
lwasser Jan 8, 2024
19a75ae
Fix: edits from Moritz @midnighter
lwasser Jan 8, 2024
a28113b
Fix: edits from Trevor @Zeitsperre
lwasser Jan 8, 2024
9bf2be3
Fix: edit from review
lwasser Jan 8, 2024
2304afd
Fix: align tree dir @midnighter feedback
lwasser Jan 8, 2024
7adf1c4
Fix: edits from Moritz and Trevor
lwasser Jan 8, 2024
fa5a6ac
Fix: add footnotes to the install lesson
lwasser Jan 8, 2024
6146e7e
Fix: links in todos to be published once all lessons are live
lwasser Jan 8, 2024
460c3a2
Fix: update redirect
lwasser Jan 8, 2024
274723c
Fix: minor fixes in install code lesson
lwasser Jan 9, 2024
0e96047
Fix: social cards broken
lwasser Jan 9, 2024
794fa9d
Fix: style tables and remove old test table image
lwasser Jan 10, 2024
bf513c4
Merge branch 'main' into run-tests
lwasser Jan 10, 2024
ce9437f
Fix: open graph tests
lwasser Jan 10, 2024
116d01e
Fix: add random title
lwasser Jan 10, 2024
14deffc
Fix: updated card graphic for package guide
lwasser Jan 10, 2024
fb63a37
Still fighting with opengraph
lwasser Jan 10, 2024
c36e7f7
Fix: head metatags update
lwasser Jan 11, 2024
8764ba6
Merge branch 'main' into run-tests
lwasser Jan 11, 2024
84df946
Fix: card year should be https
lwasser Jan 11, 2024
9f75135
Fix: review edits from @willingc
lwasser Jan 12, 2024
0cef5df
Fix: guide review from @ucodery
lwasser Jan 12, 2024
4a99523
Fix: edits from @willingc , @ucodery
lwasser Jan 12, 2024
2442e98
Fix: edits from @willinc @ucodery
lwasser Jan 12, 2024
4152451
Fix: Edit from @batalex
lwasser Jan 16, 2024
0d28e06
Fix: a few last edits from the review
lwasser Jan 16, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added _static/pyopensci-logo-package-guide.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
51 changes: 51 additions & 0 deletions _static/pyos.css
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,13 @@
/* anything related to the dark theme */
html[data-theme="dark"] {
--pst-color-info-bg: #400f59!important;
--pst-color-tbl-row: #2E2E2E!important;
}

/* anything related to the light theme */
html[data-theme="light"] {
--pst-color-tbl-row: #E1DEE9!important;
}
}


Expand Down Expand Up @@ -331,3 +338,47 @@ aside.footnote {
background-color: var(--pst-color-target);
}
}


.fa-circle-check {
color: #49b7b9;
}

/* pyOpenSci table styles */

.pyos-table {
& th.head, .pyos-table th.head.stub {
background-color: #33205C!important;

& p {
color: #fff
}
}
& th.stub {
background-color: var(--pst-color-tbl-row);
font-weight: 500;
}
& td {
vertical-align: middle;
text-align: center;
}
}


/* Make the first column in a table a "header" like thing */


/* Dark mode fix for tables */
@media (prefers-color-scheme: dark) {
td:not(.row-header):nth-child(1) {
background-color: var(--pst-color-tbl-row); /* Adjust the dark mode color */
color: #ffffff; /* Adjust the text color for better contrast */
font-weight: 500;
}
}

td, th {
border: 1px solid #ccc; /* Light gray border */
padding: 8px; /* Add some padding for better readability */
}

8 changes: 5 additions & 3 deletions conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,6 @@
"show_toc_level": 1,
# "navbar_align": "left", # [left, content, right] For testing that the navbar items align properly
"github_url": "https://github.com/pyopensci/python-package-guide",

"footer_start": ["copyright"],
"footer_end": [],
}
Expand Down Expand Up @@ -134,5 +133,8 @@


# Social cards
ogp_site_url = "http://www.pyopensci.org/python-package-guide"
ogp_image = "https://www.pyopensci.org/python-package-guide/_static/logo-light-mode.png"
ogp_site_url = "http://www.pyopensci.org/python-package-guide/"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@lwasser Maybe use https

lwasser marked this conversation as resolved.
Show resolved Hide resolved
ogp_social_cards = {
"line_color": "#6D597A",
"image": "_static/pyopensci-logo-package-guide.png",
}
14 changes: 8 additions & 6 deletions index.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,24 +133,26 @@ Learn about best practices for:
::::

::::{grid-item}
:::{card} ✨ Tests ✨
:::{card} ✨ Tests for your Python package
:class-card: left-aligned

* [Intro to testing](tests/index.md)
* [Write tests](tests/write-tests.md)
* [Types of tests](tests/test-types.md)
* [Write tests](tests/write-tests)
* [Types of tests](tests/test-types)
* [Run tests locally](tests/run-tests)
* [Run tests in CI](tests/tests-ci)

*We are actively working on this section. [Follow development here.](https://github.com/pyOpenSci/python-package-guide)*
:::
::::

::::{grid-item}
:::{card} ✨ Code checks & clean code
:::{card} ✨ Code style & Format
:class-card: left-aligned
:link: CONTRIBUTING
:link-type: doc

* [Code style](package-structure-code/code-style-linting-format.md)

*We are actively working on this section. [Follow development here.](https://github.com/pyOpenSci/python-package-guide)*
:::
::::

Expand Down
2 changes: 2 additions & 0 deletions tests/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,6 @@ Graphic showing the elements of the packaging process.
Intro <self>
Write tests <write-tests>
Test types <test-types>
Run tests locally <run-tests>
Run tests online (using CI) <tests-ci>
```
261 changes: 261 additions & 0 deletions tests/run-tests.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,261 @@
---
:og:description: Learn how to setup and run tests for your Python package locally on your computer using automation tools such as Nox. Also learn about other tools that scientific Python community members use to run tests.
:og:title: A random title
---

# Run your tests

Running your tests is important to ensure that your package
is working as expected. However, it's also important to think about your code running, not only on your computer, but also on the computers of your users who may be running various Python versions and using various operating systems. Thus, you will want to consider the following when running your tests:
lwasser marked this conversation as resolved.
Show resolved Hide resolved

1. Run your test suite in a series of environments that represent the Python versions and operating systems your users are likely to have.
lwasser marked this conversation as resolved.
Show resolved Hide resolved
2. Running your tests in an isolated environment ensures that they do not pass randomly due to your computer's specific setup. For instance, you might have locally installed dependencies that are not declared in your package's dependency list. This oversight could lead to issues when others try to install or run your package on their computers.
lwasser marked this conversation as resolved.
Show resolved Hide resolved

On this page, you will learn about the tools that you can use to both run tests in isolated environments and across
Python versions.



### Tools to run your tests

There are three types of tools that will make is easier to setup and run your tests in various environments:
lwasser marked this conversation as resolved.
Show resolved Hide resolved

1. A **test framework**, is a package that provides a particular syntax and set of tools for _both writing and running your tests_. Some test frameworks also have plugins that add additional features such as evaluating how much of your code the tests cover. Below you will learn about the **pytest** framework which is one of the most commonly used Python testing frameworks in the scientific ecosystem. Testing frameworks are essential but they only serve to run your tests. They don't provide a way to easily run tests across Python versions without additional automation tools (see automation tools below).
lwasser marked this conversation as resolved.
Show resolved Hide resolved
2. **Automation tools** allow you to automate running workflows such as tests in specific ways using user-defined commands. For instance it's useful to be able to run tests across different Python versions with a single command. Tools such as [**nox**](https://nox.thea.codes/en/stable/index.html) and [**tox**](https://tox.wiki/en/latest/index.html) also allow you to run tests across Python versions. However, it will be difficult to test your build on different operating systems using only nox and tox - this is where continuous integration (CI) comes into play.
lwasser marked this conversation as resolved.
Show resolved Hide resolved
3. **Continuous Integration (CI):** is the last tool that you'll need to run your tests. CI will not only allow you to replicate any automated builds you create using nox or tox to run your package in different Python environments. It will also allow you to run your tests on different operating systems (Windows, Mac and Linux). [We discuss using CI to run tests here](tests-ci).

:::{list-table} Table: Testing & Automation Tool
:widths: 40 15 15 15 15
:header-rows: 1
:align: center
:stub-columns: 1
:class: pyos-table

* - Features
- Testing Framework (pytest)
- Test Runner (Tox)
- Automation Tools (Nox)
- Continuous Integration (GitHub Actions)
* - Run Tests Locally
- <i class="fa-solid fa-circle-check fa-xl"></i>
- <i class="fa-solid fa-circle-check fa-xl"></i>
- <i class="fa-solid fa-circle-check fa-xl"></i>
- <i class="fa-solid fa-xmark fa-xl" style="color: #afb3bb;"></i>
* - Run Tests Online
- <i class="fa-solid fa-xmark fa-xl" style="color: #afb3bb;"></i>
- <i class="fa-solid fa-xmark fa-xl" style="color: #afb3bb;"></i>
- <i class="fa-solid fa-xmark fa-xl" style="color: #afb3bb;"></i>
- <i class="fa-solid fa-circle-check fa-xl"></i>
* - Run Tests Across Python Versions
- <i class="fa-solid fa-xmark fa-xl" style="color: #afb3bb;"></i>
- <i class="fa-solid fa-circle-check fa-xl"></i>
- <i class="fa-solid fa-circle-check fa-xl"></i>
- <i class="fa-solid fa-circle-check fa-xl"></i>
* - Run Tests In Isolated Environments
- <i class="fa-solid fa-xmark fa-xl" style="color: #afb3bb;"></i>
- <i class="fa-solid fa-circle-check fa-xl"></i>
- <i class="fa-solid fa-circle-check fa-xl"></i>
- <i class="fa-solid fa-circle-check fa-xl"></i>
* - Run Tests Across Operating Systems (Windows, MacOS, Linux)
- <i class="fa-solid fa-xmark fa-xl" style="color: #afb3bb;"></i>
- <i class="fa-solid fa-xmark fa-xl" style="color: #afb3bb;"></i>
- <i class="fa-solid fa-xmark fa-xl" style="color: #afb3bb;"></i>
- <i class="fa-solid fa-circle-check fa-xl"></i>
* - Use for other automation tasks (e.g. building docs)
- <i class="fa-solid fa-xmark fa-xl" style="color: #afb3bb;"></i>
- <i class="fa-solid fa-xmark fa-xl" style="color: #afb3bb;"></i>
- <i class="fa-solid fa-circle-check fa-xl"></i>
- <i class="fa-solid fa-circle-check fa-xl"></i>
:::


## What testing framework / package should I use to run tests?

We recommend using `Pytest` to build and run your package tests. Pytest is the most common testing tool used in the Python ecosystem.

[The Pytest package](https://docs.pytest.org/en/latest/) also has a number of
extensions that can be used to add functionality such as:

- [pytest-cov](https://pytest-cov.readthedocs.io/en/latest/) allows you to analyze the code coverage of your package during your tests, and generates a report that you can [upload to codecov](https://codecov.io/).

:::{todo}
[Learn more about code coverage here.](code-cov)
:::

```{note}
TODO: add note about running tests in vscode, breakpoints and –no-cov flag. Then link to tutorial that explains how to deal with this.
lwasser marked this conversation as resolved.
Show resolved Hide resolved
```

## Run tests using pytest

If you are using **pytest**, you can run your tests locally by
calling:

`pytest`

Or if you want to run a specific test file - let's call this file "test_module.py" - you can run:
lwasser marked this conversation as resolved.
Show resolved Hide resolved

`pytest test_module.py`

Learn more from the [get started docs here](https://docs.pytest.org/en/7.1.x/getting-started.html).
lwasser marked this conversation as resolved.
Show resolved Hide resolved

Running pytest on your computer is going to run your tests in whatever
Python environment you currently have activated. This means that tests will be
run on a single version of Python and only on the operating system that you
are running locally.

This is a great start to making your Python package more robust! However, your users may be using your package on different
lwasser marked this conversation as resolved.
Show resolved Hide resolved
versions of Python. Or they also may use other operating systems.

An automation tool can simplify the process of running tests
in various Python environments.

### Tools to automate running your tests

To run tests on various Python versions or in various specific environments with a single command, you can use an automation tool such as `nox` or `tox`.
Both `nox` and `tox` can create an isolated virtual environment that you define. This allows you to easily run your tests in multiple environments and across Python versions.
lwasser marked this conversation as resolved.
Show resolved Hide resolved

We will focus on [Nox](https://nox.thea.codes/) in this guide. `nox` is a Python-based automation tool that builds upon the features of both `make` and `tox`. `nox` is designed to simplify and streamline testing and development workflows. Everything that you do with `nox` can be implemented using a Python-based interface.

```{admonition} Other automation tools you'll see in the wild
:class: note

- **[Tox](https://tox.wiki/en/latest/index.html#useful-links)** is an automation tool that supports common steps such as building documentation, running tests across various versions of Python, and more. You can find [a nice overview of tox in the plasmaPy documentation](https://docs.plasmapy.org/en/stable/contributing/testing_guide.html#using-tox).

- **[Hatch](https://github.com/ofek/hatch)** is a modern end-to-end packaging tool that works with the popular build backend called hatchling. `hatch` offers a `tox`-like setup where you can run tests locally using different Python versions. If you are using `hatch` to support your packaging workflow, you may want to also use its testing capabilities rather than using `nox`.

* [**make:**](https://www.gnu.org/software/make/manual/make.html) Some developers use Make, which is a build automation tool, for running tests
due to its versatility; it's not tied to a specific language and can be used
to run various build processes. However, Make's unique syntax and approach can
make it more challenging to learn, particularly if you're not already familiar
with it. Make also won't manage environments for you like **nox** will do.
```

## Run tests across Python versions with nox

**Nox** is a great automation tool to learn because it:

- Is Python-based making it accessible if you already know Python and
- Will create isolated environments to run workflows.

`nox` simplifies creating and managing testing environments. With `nox`, you can
set up virtual environments, and run tests across Python versions using the environment manager of your choice with a
single command.

lwasser marked this conversation as resolved.
Show resolved Hide resolved
:::{note} Nox Installations
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking about something to notify the reader that nox doesn't need to be installed for the target python versions in order to manage them (as opposed to the venv, which must be present for each python version under test)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good point. i'm not sure if a beginner would get confused with that additional information or whether it would be useful. i hadn't thought about that before because nox just always worked! if you have suggestions for how to word it in a way that it doesn't make the user more confused i'm open to that!


When you use nox to run tests across different Python versions, nox will create and manage individual `venv` environments for each Python version that you specify in the nox function. It will setup everything that you need to run tests in each environment for you.
:::

Nox can also be used for other development tasks such as building
documentation, creating your package distribution, and testing installations
across both PyPI related environments (e.g. venv, virtualenv) and `conda` (e.g. `conda-forge`).

## Test Environments

By default, `nox` uses the Python built in `venv` environment manager. A virtual environment (`venv`) is a self-contained Python environment that allows you to isolate and manage dependencies for different Python projects. It helps ensure that project-specific libraries and packages do not interfere with each other, promoting a clean and organized development environment.

An example of using nox to run tests in `venv` environments for Python versions 3.9, 3.10, 3.11 and 3.12 is below.

```{warning}
Note that for the code below to work, you need to have all 4 versions of Python installed on your computer for `nox` to find.
```

### Nox with venv environments

```{todo}
TODO: add some tests above and show what the output would look like in the examples below...
```

Below is an example of setting up nox to run tests using `venv` which is the built in environment manager that comes with base Python.

Note that the example below assumes that you have [setup your `pyproject.toml` to declare test dependencies in a way that pip
can understand](../package-structure-code/declare-dependencies.md). An example
of that setup is below.

```toml
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

[project]
lwasser marked this conversation as resolved.
Show resolved Hide resolved
name = "pyosPackage"
version = "0.1.0"
dependencies = [
"geopandas",
"xarray",
]

[project.optional-dependencies]
tests = ["pytest", "pytest-cov"]
```

If you have the above setup, then you can use `session.install(".[tests]")` to install your test dependencies.
Notice that below one single nox session allows you to run
your tests on 4 different Python environments (Python 3.9, 3.10, 3.11, and 3.12).

```python
lwasser marked this conversation as resolved.
Show resolved Hide resolved
import nox

# For this to run you will need to have python3.9, python3.10 and python3.11 installed on your computer. Otherwise nox will skip running tests for whatever versions are missing

@nox.session(python=["3.9", "3.10", "3.11", "3.12"])
def test(session):

# install
session.install(".[tests]")

# Run tests
session.run("pytest")

```

Above you create a nox session in the form of a function
with a `@nox.session` decorator. Notice that within the decorator you declare the versions of python that you
wish to run.

To run the above you'd use the command where `--session`. You may also see
people using the shortcut for session `-s`. Your function above
lwasser marked this conversation as resolved.
Show resolved Hide resolved
is called test, therefore the session name is test.

```
nox -s test
lwasser marked this conversation as resolved.
Show resolved Hide resolved
```

### Nox with conda / mamba

Below is an example for setting up nox to use mamba (or conda) for your
environment manager.
Note that unlike venv, conda can automatically install
the various versions of Python that you need. You won't need to install all three Python versions if you use conda/mamba, like you do with `venv`.
lwasser marked this conversation as resolved.
Show resolved Hide resolved

```{note}
For `conda` to work with `nox`, you will need to
install a conda-friendly version of Python. We suggest
the mamba-forge installation.
lwasser marked this conversation as resolved.
Show resolved Hide resolved

More on that here...<link to tutorial??>
```

```python
import nox

# The syntax below allows you to use mamba / conda as your environment manager, if you use this approach you don’t have to worry about installing different versions of Python

@nox.session(venv_backend='mamba', python=["3.9", "3.10", "3.11", "3.12"])
def test_mamba(session):
"""Nox function that installs dev requirements and runs
tests on Python 3.9 through 3.12
"""

# Install dev requirements
session.install(".[tests]")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

According to nox, session.install will always run pip. For conda it is recommended to use session.conda_install (there isn't a mamba_install, I assume conda_install works for both). I'm not sure what the best practice for installing a package under test in a conda environment is, but at least the package's dependencies should be installed via conda_install.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed! i missed the conda_install method in the docs!

# Run tests using any parameters that you need
session.run("pytest")
```

To run the above session you'd use:

```bash
nox -s test_mamba
lwasser marked this conversation as resolved.
Show resolved Hide resolved
```
Loading
Loading