hypothesis integration

hrefs uses the hypothesis library to testing internally, and includes a hypothesis plugin for generating hrefs.Href instances. Thanks to the entry points mechanism, you don’t need to do anything except import hypothesis and start generating hyperlinks:

from dataclasses import dataclass
from hrefs import Href, Referrable
from hypothesis import given, strategies as st

class Book(Referrable):
    id: int

    def get_key(self) -> int:
        return self.id

    def key_to_url(key: int) -> str:
        return f"/books/{key}"

    def url_to_key(url: str) -> int:
        return int(url.split("/")[-1])

def test_hrefs_with_hypothesis(href):
    assert isinstance(href, Href)
    assert href.url == f"/books/{href.key}"


Using hypothesis with FastAPI/Starlette

Generating URLs for hyperlinks in FastAPI/Starlette normally relies on hrefs.starlette.HrefMiddleware to expose the request context to the library. But the middleware doesn’t directly work with the @given decorator, since that would require hypothesis to run inside your Starlette application. But hypothesis already wants to generate the data before your test case even takes over.

In order to give hypothesis the context, you can use fixtures. Here is an example using pytest:

from pytest import fixture
from fastapi import FastAPI
from hrefs import Href, BaseReferrableModel
from hrefs.starlette import href_context
from hypothesis import given, strategies as st

app = FastAPI()

class Book(BaseReferrableModel):
    id: int

    class Config:
        details_view = "get_book"

def get_my_model(id: int) -> Book:

@fixture(scope="module", autouse=True)
def appcontext():
    with href_context(app, base_url="http://example.com"):

def test_hrefs_with_hypothesis_and_pytest(href):
    assert isinstance(href, Href)
    assert href.url == f"http://example.com/books/{href.key}"

The example uses a module-scoped fixture. There is nothing wrong in using function-scoped fixtures, but they are unnecessarily granular and hypothesis will warn against them by default.