One claim per function
The convention is one test function per promise. The function name is the sentence, and the body is the check. This is why the name matters: the runner prints it, so test_rejects_empty_input is a report a reader can understand without opening the file.
The runner is not magic
pytest collects the test_ functions, calls each, and catches the raise. A raise becomes a failure line, a clean return becomes a pass. You could write that loop yourself, the runner just saves you the bookkeeping and gives you the summary line.