|
|
||
Одна из самых больших проблем, с которой сталкивается инженер-электрик, компьютерщик или инженер-конструктор - это преодоление разрыва между аппаратным и программным обеспечением. Задача написания программного обеспечения для работы с аппаратными средствами всегда оказывалась сложной. Это связано в первую очередь с тем, что основное внимание уделяется изучению синтаксиса языка и недостаточно времени уделяется отладке и тестированию программного обеспечения. Разрыв увеличивается еще больше, когда требуется доказать, что программное обеспечение не создает проблем при работе с аппаратным обеспечением. Это, как правило, приводит к многочасовому тестированию всей системы для обеспечения повторяемости, что не является масштабируемым решением при использовании аппаратного обеспечения из-за медленной реакции. Для разработчиков на Python решением является написание модульных тестов тестового кода с использованием pytest и плагина pytest-mock для имитации аппаратных ответов.
ПРИМЕЧАНИЕ: Хотя этот пост написан для разработчика аппаратного обеспечения, эта концепция также распространяется на любой внешний процесс или систему, например, базу данных.
from time import sleep
from fabric import Connection
class Driver:
def __init__(self, hostname):
self.connection = Connection(hostname)
def run(self, command):
sleep(5)
return self.connection.run(command, hide=True).stdout.strip()
def disk_free(self):
return self.run("df -h").
@staticmethod
def extract_percent(output):
free_line = output.split("\n")[1]
percent = free_line.split()[4]
return percent
Приведенный выше драйвер является простым примером. Он использует библиотеку Fabric для установления SSH-соединения. Есть два метода:
import socket
from mock_tutorial.driver import Driver
def test_driver_integrated():
d = Driver(socket.gethostname())
result = d.disk_free()
percent = d.extract_percent(result)
assert percent == "75%"
Приведенный выше тестовый код обычно пишется в стиле тестирования "черного ящика" для проверки этого драйвера. Сначала инстанцируется объект драйвера, вызывается функция disk_free(), разбирается вывод, а затем сравнивается с ожидаемым результатом.
$ pytest -s
======================== начинается сеанс тестирования =======================
платформа linux -- Python 3.9.4, pytest-6.2.3, py-1.10.0, pluggy-0.13.1 rootdir: /home/chinghwa/projects/mock_tutorial
плагины: mock-3.5.1
собран 1 элемент
tests/test_driver.py . [100%]
========================= 1 пройден за 5.51с ========================
В дополнение к медленному выполнению в 5.51 секунды, есть еще одна проблема. Вывод "df -h", скорее всего, будет меняться со временем, а не оставаться на уровне 75%. Хотя этот пример надуманный, он иллюстрирует необходимость проверки вывода другим способом.
import socket
import pytest
from mock_tutorial.driver import Driver
def test_driver_unit(mocker):
output_list = [
"Filesystem Size Used Avail Use% Mounted on",
"rootfs 472G 128G 344G 28% /",
"none 472G 128G 344G 28% /dev", ]
output = "\n".join(output_list)
mock_run = mocker.patch( "mock_tutorial.driver.Driver.run", return_value=output )
d = Driver(socket.gethostname())
result = d.disk_free()
percent = d.extract_percent(result)
mock_run.assert_called_with("df -h") assert percent == "28%"
Приведенный выше код был переписан для использования объекта mock для мокирования метода run(). Сначала нужно импортировать pytest (строка 2) и вызвать фикстуру mocker из pytest-mock (строка 5).
В строках 12-14 метод run() класса Driver был исправлен заранее заданным ответом, чтобы имитировать реальный ответ. Это означает, что любой вызов run() будет возвращать строковое значение вывода.
В строке 18 будет проверена команда, которая была отправлена методу run(). При вызове метода disk_free() будет сгенерирована команда "df -h" и вызов run() с этой командой.
В строке 19 будет проверена функция синтаксического анализа, которая извлекает проценты из вывода. Если значение Use% в строке 8 будет изменено, то произойдет сбой, так как именно это значение будет извлечено.
$ pytest -s
======================== начинается сеанс тестирования =======================
платформа linux -- Python 3.9.4, pytest-6.2.3, py-1.10.0, pluggy-0.13.1
rootdir: /home/chinghwa/projects/mock_tutorial
плагины: mock-3.5.1
собран 1 элемент
tests/test_driver.py . [100%]
========================= 1 пройден за 0.36с ========================
После добавления mock-объектов время теста сократилось до 0,36 секунды. Метод slow run() был пропатчен для более быстрого выполнения, а также был проверен код для разбора симулированного вывода.
Это должно послужить хорошей отправной точкой для разработки быстродействующих модульных тестов на Python путем исправления медленных ответов с помощью объектов-моков. В дополнение к модульным тестам следует также писать интеграционные тесты, однако они могут выполняться реже.
Этот пост - первый, который я написал на эту тему в Python, и я надеюсь в будущем углубиться в другие методы pytest-mock.
|
Новые книги авторов СИ, вышедшие из печати:
О.Болдырева "Крадуш. Чужие души"
М.Николаев "Вторжение на Землю"