Python 现代开发学习笔记

(十一)用临时文件检查格式往返

1. 从 Python 对象写出,再重新读取

JSON 和 CSV 共用同样的期望值。除了普通标题,再放进逗号、引号与换行,才能检查第五章引入 CSV 读写器的原因:

# 项目文件:quality_project/tests/integration/test_formats.py
# 两种格式分别走真实写入和读取,不靠重复编码逻辑计算期望值。
import pytest

from bookdata.csv_io import read_csv, write_csv
from bookdata.errors import RecordError
from bookdata.json_io import read_json, write_json
from bookdata.models import Book


@pytest.mark.parametrize(
    "writer, reader, suffix",
    [(write_json, read_json, ".json"), (write_csv, read_csv, ".csv")],
    ids=["json", "csv"],
)
def test_roundtrip(tmp_path, writer, reader, suffix):
    expected = [Book('Python, "入门"\n手册', True, ["基础", "带,逗号"])]
    path = tmp_path / ("books" + suffix)
    writer(path, expected)
    actual = reader(path)
    assert actual == expected
    assert actual[0] is not expected[0]
    assert actual[0].tags is not expected[0].tags


def test_broken_json_keeps_content(tmp_path):
    path = tmp_path / "broken.json"
    path.write_text('{"version":', encoding="utf-8")
    before = path.read_bytes()
    with pytest.raises(RecordError, match="JSON"):
        read_json(path)
    assert path.read_bytes() == before

往返后的值相等,同时 Book 和标签列表都是新对象,检查了“从文件恢复”与“仍引用旧对象”的区别。坏 JSON 则要保留错误并保持文件内容,不能悄悄当成空书目。

对象写入临时目录、重新解析、比较值并检查对象独立性的过程

2. 检查记录转换的失败反馈

# 项目文件:quality_project/tests/unit/test_records.py
# 用不同错误字段检查反馈,单独核对去空格、去重和记录位置。
import pytest

from bookdata.errors import RecordError
from bookdata.records import book_from_dict, validate_books


@pytest.mark.parametrize(
    "record, message",
    [
        ({"title": " "}, "书名"),
        ({"title": "Python", "finished": "false"}, "布尔值"),
        ({"title": "Python", "tags": "基础"}, "tags"),
    ],
)
def test_bad_record(record, message):
    with pytest.raises(RecordError, match=message):
        book_from_dict(record)


def test_normalize_tags():
    book = book_from_dict({"title": " Python ", "tags": ["基础", " 基础 "]})
    assert book.title == "Python"
    assert book.tags == ["基础"]


def test_duplicate_title_reports_position():
    with pytest.raises(RecordError, match="第 2 条记录.*标题重复") as error:
        validate_books([{"title": "Python"}, {"title": " Python "}])
    assert isinstance(error.value.__cause__, RecordError)

期望值直接来自任务:字符串 "false" 不是布尔状态;两个去掉首尾空格后相同的标题是重复记录。测试没有把实现中的判断再抄一遍,而是检查外部可观察的结果与反馈。

笔记正文与全部题解 · Ctrl / ⌘ K