Testing your renderer#
A renderer is a function from source code to output, which makes it easy to test: render a known calculation and compare the result.
Use the full pipeline#
Test through HandCalcs, with an explicit namespace so the test does not depend on
running the code:
from handcalcs import HandCalcs
from my_renderers import UnicodeRenderer
def render(source, **values):
return HandCalcs(UnicodeRenderer())(source, values)
def test_multiplication_uses_centred_dot():
out = render("b = a * 3", a=2, b=6)
assert out.strip() == "b = a · 3 = 2 · 3 = 6"
def test_subscripts():
assert render("x_1 = 2", x_1=2).strip() == "x₁ = 2"
Passing the namespace also lets you test values that are awkward to produce by running
code, such as large or negative numbers, or NoValue (leave the name out).
Isolate the configuration#
The global configuration seeds every render, and a developer’s saved config would change
your results. Point HANDCALCS_CONFIG at a file that does not exist and reset the
in-memory config around each test:
import pytest
from handcalcs import config
@pytest.fixture(autouse=True)
def isolated_config(tmp_path, monkeypatch):
monkeypatch.setenv("HANDCALCS_CONFIG", str(tmp_path / "config.json"))
config.reset_config()
yield
config.reset_config()
handcalcs’ own tests do the same (see tests/test_jupyter.py for the pattern).
Golden files#
For a renderer with a lot of layout, compare against stored output (“golden” files) rather than writing every expected string by hand. Render the built-in demo, which exercises headings, prose, grids, long equations and nested conditionals:
import pathlib
from handcalcs import HandCalcs
GOLDEN = pathlib.Path(__file__).parent / "golden" / "demo.rst"
def test_demo_matches_golden():
out = HandCalcs(RstRenderer()).demo()
assert out == GOLDEN.read_text()
When the output changes on purpose, regenerate the file, read the diff, and commit it.
Validate the format#
Where a validator exists, use it: render, then parse the output with the format’s own tools so a structural mistake fails the test.
Format |
Check with |
|---|---|
HTML |
|
reST |
|
LaTeX |
compile with |
JSON |
|
Test rules on their own#
A rule is a plain function, so you can call it with a node and a context:
from handcalcs.parsing.nodes import Name
from handcalcs.renderers.base import BaseRenderContext, RenderContext
def test_subscript_rule():
ctx = BaseRenderContext(RenderContext(), RenderContext.sparse())
node = unicode_subscripts(None, Name(identifier="x_1"), ctx)
assert node.identifier == "x₁"
Next: Publishing your renderer.