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

Update readme with Usage instructions #39

Merged
merged 13 commits into from
Apr 13, 2023
6 changes: 6 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,10 @@ you can follow the [Jupyter contributor guide](https://jupyter.readthedocs.io/en
Make sure to also follow [Project Jupyter's Code of Conduct](https://github.com/jupyter/governance/blob/HEAD/conduct/code_of_conduct.md)
for a friendly and welcoming collaborative environment.

Please see our documentation on:

- [Setting up a development install](https://pytest-jupyterhub.readthedocs.io/en/latest/contributing/setup.html)
- [Testing Pytest JupyterHub](https://pytest-jupyterhub.readthedocs.io/en/latest/contributing/tests.html)
- [Contributing Documentation](https://pytest-jupyterhub.readthedocs.io/en/latest/contributing/docs.html)

If you need some help, feel free to ask on [Gitter](https://gitter.im/jupyterhub/jupyterhub) or [Discourse](https://discourse.jupyter.org/).
75 changes: 69 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,23 @@
# Reusable JupyterHub Pytest Plugin
**[Description](#description)** |
**[Installation](#installation)** |
**[Usage](#usage)** |
**[Contributing](#contributing)**

---

# [Reusable JupyterHub Pytest Plugin](https://github.com/jupyterhub/pytest-jupyterhub)

[![Documentation status](https://img.shields.io/readthedocs/pytest-jupyterhub?logo=read-the-docs)](https://pytest-jupyterhub.readthedocs.io/en/latest/?badge=latest)
[![GitHub Workflow Status - Test](https://img.shields.io/github/actions/workflow/status/jupyterhub/pytest-jupyterhub/test.yml?logo=github&label=tests&branch=main)](https://github.com/jupyterhub/pytest-jupyterhub/actions)
[![GitHub](https://img.shields.io/badge/issue_tracking-github-blue?logo=github)](https://github.com/jupyterhub/pytest-jupyterhub/issues)

## Description

This is a reusable pytest plugin for testing JupyterHub's components

**JupyterHub** is a modular and extensible project, with components, like the **proxy**, **authenticator** and **spawner**, that can be easily replaced with alternate implementations. Testing the functionality of these components against JupyterHub is important and it requires various hub setups that can sometimes become complicated.
[**JupyterHub**](https://github.com/jupyterhub/jupyterhub) is a modular and extensible project, with components, like the **proxy**, **authenticator** and **spawner**, that can be easily replaced with alternate implementations. Testing the functionality of these components against JupyterHub is important and it requires various hub setups that can sometimes become complicated.

Each of these hub components and the hub itself define their own testing infrastructure, building everything from the ground up using the **pytest** framework. And some of this complex work is either repetitive across JupyterHub sub-projects, or under-specified for some of them. This has sparked a need to abstract these common parts into a separate testing framework.
Each of these hub components and the hub itself define their own testing infrastructure, building everything from the ground up using the [**pytest**](https://docs.pytest.org/en/stable/) framework. And some of this complex work is either repetitive across JupyterHub sub-projects, or under-specified for some of them. This has sparked a need to abstract these common parts into a separate testing framework.

The goal is to provide importable testing utilities to make it easier for contributors to write tests for the various hub components.
This will involve creating and using **fixtures** and **mocks**.
Expand All @@ -17,8 +28,60 @@ For more information on Fixtures, check out this [pytest documentation](https://
A **Mock** is an object that simulates the behavior of another object such as a class or function. They are used to simulate the behavior of real objects for testing purposes.
For more information on Mocks, check out this [unittest documentation](https://docs.python.org/3/library/unittest.mock.html) on the mock module.

**Example:**
## Installation

To use the **JupyterHub Pytest Plugin**, you will first need to install it using pip:

**Note**: Currently, Pytest JupyterHub can only be installed from GitHub since it is yet to be released on PyPI.

```bash
pip install pytest-jupyterhub
```

## Usage

To use a plugin, first register/load the plugin in your test module or `conftest.py` file:

```python
pytest_plugins = "jupyterhub-spawners-plugin"
```

To register multiple plugins:

```python
pytest_plugins = ["jupyterhub-spawners-plugin", "other-plugin"]
```

For more information, check out this document on [Requiring/Loading plugins in a test module or conftest file](https://docs.pytest.org/en/stable/how-to/writing_plugins.html#requiring-loading-plugins-in-a-test-module-or-conftest-file)

All fixtures and mocks inside the plugin will be available to all of your project's test suites. You can use a fixture by passing it as an argument to your test function.

### Example

The [`hub_app` fixture](https://github.com/jupyterhub/pytest-jupyterhub/blob/829aad654cb69de56b227c7177a844a0b5ea8485/pytest_jupyterhub/jupyterhub_spawners.py#L42) is a [factory fixture](https://docs.pytest.org/en/latest/how-to/fixtures.html#factories-as-fixtures) that creates a `MockHub` instance from a provided `config` dictionary defining spawner-specific attribute configurations. It then yields the mocked instance. After testing, the running hub instance is stopped and a cleanup is performed.

An illustration of its integration within the [`DockerSpawner` test suite](https://github.com/jupyterhub/dockerspawner/blob/af2da8d06898406816193f7a68b21b776fc909b6/tests/conftest.py#L71) is provided below:

```python
@pytest.fixture
async def app(hub_app):
config = {
"Dockerspawner": {
"prefix": "dockerspawner-test"
}
}

if len(jh_version_info) > 3 and jh_version_info[3]:
tag = jupyterhub.__version__
config["Dockerspawner"]["image"] = f"jupyterhub/singleuser:{tag}"

app = await hub_app(config=config)

return app
```

## Contributing

The [init_db](https://github.com/jupyterhub/jupyterhub/blob/336d7cfcfaf74087e4ee467d5e3d3bec0c25c3d0/jupyterhub/app.py#L1804) function in JupyterHub's `app` module initializes a connection to a database using SQLAlchemy's ORM (Object-Relational Mapper).
If you would like to contribute to the project, please read our [contributor documentation](https://pytest-jupyterhub.readthedocs.io/en/latest/contributing/index.html) and the [CONTRIBUTING.md](https://github.com/jupyterhub/pytest-jupyterhub/blob/main/CONTRIBUTING.md).

However, the mock [init_db](https://github.com/jupyterhub/jupyterhub/blob/336d7cfcfaf74087e4ee467d5e3d3bec0c25c3d0/jupyterhub/tests/mocking.py#L295) function in JupyterHub's `mocking` module initializes a database connection for the mocked JupyterHub application instance by calling the `init_db` function of the JupyterHub superclass but also has a `test_clean_db` attribute to ensure that the database is reset to a clean state before running tests.
The contributor documentation explains how to set up a development installation, how to run the test suite, and how to contribute to documentation.