How to test a REST API endpoint#

This guide shows you how to test a plone.restapi service with real HTTP requests.

REST API tests need the functional layer. A real request arrives over the network in a different transaction, so it cannot see anything your test has not committed—which is exactly what the integration layer never does.

Make an anonymous request#

anon_request is a session bound to the portal with no authentication. Relative URLs resolve against the portal’s REST API root, so you can ask for / and mean the site.

def test_root_is_public(anon_request):
    response = anon_request.get("/")

    assert response.status_code == 200
    assert response.json()["@type"] == "Plone Site"

Make an authenticated request#

manager_request is authenticated as the site owner.

def test_controlpanels_require_auth(manager_request):
    response = manager_request.get("/@controlpanels")

    assert response.status_code == 200

Use other credentials#

For any identity other than the two shorthands, build a session yourself with request_factory.

def test_as_specific_user(request_factory):
    session = request_factory(basic_auth=("editor", "secret"))

    response = session.get("/")

    assert response.status_code == 200

To talk to the site outside the REST API traverser, pass api=False:

def test_html_view(request_factory):
    session = request_factory(role="Manager", api=False)

    response = session.get("/")

    assert "<html" in response.text

Create content to test against#

The functional portal is a normal portal. Put content on it with the marker, and it is there when the request arrives.

import pytest


@pytest.mark.portal(
    content=[{"type": "Document", "id": "doc1", "title": "A document"}],
)
def test_get_document(functional_portal, anon_request):
    response = anon_request.get("/doc1")

    assert response.status_code == 200
    assert response.json()["title"] == "A document"

Important

Request the functional_portal fixture in the test even if you do not use the object. It is what builds the portal the marker acts on, and what the session’s base URL points at.

Share a portal across a REST API suite#

REST API suites are usually read-only, which makes them a good fit for the class-scoped fixture—one portal, built once, for the whole class.

import pytest


@pytest.mark.portal(roles=["Manager"])
class TestSearchService:
    def test_search_returns_results(self, functional_portal_class, manager_request):
        response = manager_request.get("/@search")

        assert response.status_code == 200

See How to speed up a slow test suite for the trade-off this makes.