Skip to content

Testing⚓︎

PyPDFForm uses pytest for testing and coverage.py for measuring test coverage. To run the tests, execute:

coverage run -m pytest && coverage report --fail-under=100
test

Generate coverage report⚓︎

To generate a test coverage report, run:

coverage run -m pytest && coverage html

View the coverage report by opening htmlcov/index.html in a browser.

coverage

View the coverage report at http://localhost:8000/htmlcov/index.html.

Test breakdown⚓︎

Although each PyPDFForm test is unique, most follow a general paradigm.

Most library tests can be summarized in three steps:

  • Define the expected PDF file.
  • Use PyPDFForm to generate a PDF from the test inputs.
  • Compare the PDF generated by the test with the expected PDF file.

Consider this example test:

def test_fill(pdf_samples, request):
    expected_path = os.path.join(pdf_samples, "sample_filled.pdf")
    with open(expected_path, "rb+") as f:
        obj = PdfWrapper(
            os.path.join(pdf_samples, "sample_template.pdf")
        ).fill(
            {
                "test": "test_1",
                "check": True,
                "test_2": "test_2",
                "check_2": False,
                "test_3": "test_3",
                "check_3": True,
            },
        )

        request.config.results["expected_path"] = expected_path
        request.config.results["stream"] = obj.read()

        expected = f.read()

        assert len(obj.read()) == len(expected)
        assert obj.read() == expected

The test starts by defining an expected PDF sample_filled.pdf:

expected_path = os.path.join(pdf_samples, "sample_filled.pdf")

The test then fills sample_template.pdf with a data dictionary using PdfWrapper:

obj = PdfWrapper(
    os.path.join(pdf_samples, "sample_template.pdf")
).fill(
    {
        "test": "test_1",
        "check": True,
        "test_2": "test_2",
        "check_2": False,
        "test_3": "test_3",
        "check_3": True,
    },
)

Include these two lines in most tests to make expected PDF regeneration easier:

request.config.results["expected_path"] = expected_path
request.config.results["stream"] = obj.read()

Finally, the test compares the generated PDF stream with the expected PDF stream:

expected = f.read()

assert len(obj.read()) == len(expected)
assert obj.read() == expected

Most CLI tests can be summarized in four steps:

  • Define the expected PDF file.
  • Write any input data required by the command to tmp_path.
  • Invoke the CLI command to generate a PDF from the test inputs.
  • Compare the PDF generated by the test with the expected PDF file.

Consider this example test:

@pytest.mark.cli_test
def test_fill(pdf_samples, tmp_path):
    expected_path = os.path.join(pdf_samples, "docs", "test_fill_text_check.pdf")
    input_path = os.path.join(tmp_path, "input.json")
    output_path = os.path.join(tmp_path, "output.pdf")
    fill_data = {
        "test": "test_1",
        "check": True,
        "test_2": "test_2",
        "check_2": False,
        "test_3": "test_3",
        "check_3": True,
    }

    with open(input_path, "w") as f:
        json.dump(fill_data, f)

    result = runner.invoke(
        cli_app,
        [
            "fill",
            os.path.join(pdf_samples, "sample_template.pdf"),
            "-f",
            input_path,
            "-o",
            output_path,
        ],
    )
    assert result.exit_code == 0

    with open(expected_path, "rb") as f1, open(output_path, "rb") as f2:
        expected = f1.read()
        actual = f2.read()

        assert len(expected) == len(actual)
        assert expected == actual

Mark CLI tests with @pytest.mark.cli_test. The runner is a CliRunner from typer.testing, and the command under test is the PyPDFForm CLI app:

import pytest
from typer.testing import CliRunner

from PyPDFForm.cli.root import cli_app

runner = CliRunner()

The test starts by defining the expected PDF path, the temporary JSON input path, and the output path:

expected_path = os.path.join(pdf_samples, "docs", "test_fill_text_check.pdf")
input_path = os.path.join(tmp_path, "input.json")
output_path = os.path.join(tmp_path, "output.pdf")

The test then writes the fill dictionary to a JSON file in tmp_path:

fill_data = {
    "test": "test_1",
    "check": True,
    "test_2": "test_2",
    "check_2": False,
    "test_3": "test_3",
    "check_3": True,
}

with open(input_path, "w") as f:
    json.dump(fill_data, f)

Next, the test invokes the fill command against sample_template.pdf from pdf_samples:

result = runner.invoke(
    cli_app,
    [
        "fill",
        os.path.join(pdf_samples, "sample_template.pdf"),
        "-f",
        input_path,
        "-o",
        output_path,
    ],
)
assert result.exit_code == 0

Finally, the test compares the PDF written by the command with the expected PDF stream:

with open(expected_path, "rb") as f1, open(output_path, "rb") as f2:
    expected = f1.read()
    actual = f2.read()

    assert len(expected) == len(actual)
    assert expected == actual