Глава 23 · Часть V · Проверяем и автоматизируем
Проверяем поиск дубликатов
Пустые файлы и файл в несколько мегабайт проверяют два случая, которые легко пропустить в обычном тесте.
SafeSort · Часть 5 из 6Тесты и CI
Тесты для find_duplicates() проверяют не только «типичный»
случай двух одинаковых файлов, но и два крайних случая, которые легко упустить: файлы
нулевого размера и файл заметно больше одного блока чтения.
tests/test_duplicates.py
def test_identical_content_files_are_grouped(tmp_path):
a = tmp_path / "notes.txt"
b = tmp_path / "copy_of_notes.txt"
a.write_text("тот же текст")
b.write_text("тот же текст")
groups = find_duplicates(scan(tmp_path, Config()))
assert len(groups) == 1
assert len(groups[0].files) == 2
def test_empty_files_are_duplicates_of_each_other(tmp_path):
(tmp_path / "a.txt").write_text("")
(tmp_path / "b.txt").write_text("")
groups = find_duplicates(scan(tmp_path, Config()))
assert len(groups) == 1
assert groups[0].size == 0
Результат и способ чтения проверяются отдельно
Файл больше одного блока и правильный digest доказывают только результат функции. Такой тест не наблюдает вызовы
read(): реализация могла бы прочитать весь файл сразу и всё равно вернуть тот же SHA-256. Контракт bounded reads проверяется отдельным instrumented reader.tests/test_duplicates.py
def test_sha256_large_file_has_correct_result(tmp_path):
data = b"x" * (5 * 1024 * 1024) # 5 МБ — больше одного блока чтения
path = tmp_path / "bolshoj_fajl.bin"
path.write_bytes(data)
assert sha256_file(path) == hashlib.sha256(data).hexdigest()
tests/test_duplicates.py: interaction test
class RecordingReader(io.BytesIO):
def __init__(self, data: bytes) -> None:
super().__init__(data)
self.requested_sizes = []
def read(self, size: int = -1) -> bytes:
self.requested_sizes.append(size)
return super().read(size)
def test_sha256_stream_uses_multiple_bounded_reads():
reader = RecordingReader(b"abcdefghij")
digest = sha256_stream(reader, chunk_size=4)
assert digest == hashlib.sha256(b"abcdefghij").hexdigest()
assert reader.requested_sizes == [4, 4, 4, 4]
assert max(reader.requested_sizes) == 4
Первый тест относится к типу result test (black-box): он проверяет,
что вернулось.
Второй проверяет interaction/implementation contract: зависимость получила несколько
ограниченных read(4). Небольшой записывающий поток делает
это наблюдаемым без тяжёлой mocking-инфраструктуры.
Практика: тесты дубликатов и пустых файлов
Интерактивный ноутбук в браузере: Python 3.14 через Pyodide, без установки
Официальная документация