Plugin testing docs now recommend datasette.client, closes #1102

This commit is contained in:
Simon Willison 2020-11-28 23:44:57 -08:00 committed by GitHub
commit 12877d7a48
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -11,24 +11,24 @@ If you use the template described in :ref:`writing_plugins_cookiecutter` your pl
from datasette.app import Datasette
import pytest
import httpx
@pytest.mark.asyncio
async def test_plugin_is_installed():
app = Datasette([], memory=True).app()
async with httpx.AsyncClient(app=app) as client:
response = await client.get("http://localhost/-/plugins.json")
assert 200 == response.status_code
installed_plugins = {p["name"] for p in response.json()}
assert "datasette-plugin-template-demo" in installed_plugins
datasette = Datasette([], memory=True)
response = await datasette.client.get("/-/plugins.json")
assert response.status_code == 200
installed_plugins = {p["name"] for p in response.json()}
assert "datasette-plugin-template-demo" in installed_plugins
This test uses the `HTTPX <https://www.python-httpx.org/>`__ Python library to run mock HTTP requests through a fresh instance of Datasette. This is the recommended way to write tests against a Datasette instance.
It also uses the `pytest-asyncio <https://pypi.org/project/pytest-asyncio/>`__ package to add support for ``async def`` test functions running under pytest.
This test uses the :ref:`internals_datasette_client` object to exercise a test instance of Datasette. ``datasette.client`` is a wrapper around the `HTTPX <https://www.python-httpx.org/>`__ Python library which can imitate HTTP requests using ASGI. This is the recommended way to write tests against a Datasette instance.
This test also uses the `pytest-asyncio <https://pypi.org/project/pytest-asyncio/>`__ package to add support for ``async def`` test functions running under pytest.
You can install these packages like so::
pip install pytest pytest-asyncio httpx
pip install pytest pytest-asyncio
If you are building an installable package you can add them as test dependencies to your ``setup.py`` module like this:
@ -38,7 +38,7 @@ If you are building an installable package you can add them as test dependencies
name="datasette-my-plugin",
# ...
extras_require={
"test": ["pytest", "pytest-asyncio", "httpx"]
"test": ["pytest", "pytest-asyncio"]
},
tests_require=["datasette-my-plugin[test]"],
)
@ -65,12 +65,11 @@ Here's an example that uses the `sqlite-utils library <https://sqlite-utils.read
.. code-block:: python
from datasette.app import Datasette
import httpx
import pytest
import sqlite_utils
@pytest.fixture(scope="session")
def ds(tmp_path_factory):
def datasette(tmp_path_factory):
db_directory = tmp_path_factory.mktemp("dbs")
db_path = db_directory / "test.db"
db = sqlite_utils.Database(db_path)
@ -78,7 +77,7 @@ Here's an example that uses the `sqlite-utils library <https://sqlite-utils.read
{"id": 1, "name": "Cleo", "age": 5},
{"id": 2, "name": "Pancakes", "age": 4}
], pk="id")
ds = Datasette(
datasette = Datasette(
[db_path],
metadata={
"databases": {
@ -92,25 +91,23 @@ Here's an example that uses the `sqlite-utils library <https://sqlite-utils.read
}
}
)
return ds
return datasette
@pytest.mark.asyncio
async def test_example_table_json(ds):
async with httpx.AsyncClient(app=ds.app()) as client:
response = await client.get("http://localhost/test/dogs.json?_shape=array")
assert 200 == response.status_code
assert [
{"id": 1, "name": "Cleo", "age": 5},
{"id": 2, "name": "Pancakes", "age": 4},
] == response.json()
async def test_example_table_json(datasette):
response = await datasette.client.get("/test/dogs.json?_shape=array")
assert response.status_code == 200
assert response.json() == [
{"id": 1, "name": "Cleo", "age": 5},
{"id": 2, "name": "Pancakes", "age": 4},
]
@pytest.mark.asyncio
async def test_example_table_html(ds):
async with httpx.AsyncClient(app=ds.app()) as client:
response = await client.get("http://localhost/test/dogs")
assert ">Some dogs</h1>" in response.text
async def test_example_table_html(datasette):
response = await datasette.client.get("/test/dogs")
assert ">Some dogs</h1>" in response.text
Here the ``ds()`` function defines the fixture, which is than automatically passed to the two test functions based on pytest automatically matching their ``ds`` function parameters.
Here the ``datasette()`` function defines the fixture, which is than automatically passed to the two test functions based on pytest automatically matching their ``datasette`` function parameters.
The ``@pytest.fixture(scope="session")`` line here ensures the fixture is reused for the full ``pytest`` execution session. This means that the temporary database file will be created once and reused for each test.
@ -119,5 +116,5 @@ If you want to create that test database repeatedly for every individual test fu
.. code-block:: python
@pytest.fixture
def ds(tmp_path_factory):
# ...
def datasette(tmp_path_factory):
# This fixture will be executed repeatedly for every test