🏠 | 💻 PC | 🔨 Testing |

Using Python for Software Testing

Introduction

В этой статье Вы узнаете о том как можно тестировать ПО с помощью Python.

Я начну с подготовки рабочей среды, затем перейдём к простым заданиям, которые могут поручить начинающему тестировщику.

На следующем этапе рассмотрим библиотеку unittest и изучим немного теории.

Статья рассчитана на начинающих тестировщиков. Для лучшего понимания некоторых частей желательно базовое знание Python

Contents
Introduction
Документация
Подготовка рабочей среды
Учебный пример
Реальный пример 1
unittest
Теория unittest
pytest
Решение проблем
Другие статьи о Python
Другие статьи о Тестировании

Использование Python для тестирования ПО.

docs.python-requests

programcreek.com/python

Подготовка рабочей среды

To отправлять POST, GET и другие HTTP запросы с помощью python нужно в текст программы добавить строчку import requests. Request сперва нужно установить, для этого нужен pipenv, а для установки pipenv нужен pip

sudo apt install python-pip

Если нужно обновить pip

pip install --upgrade pip

Устанавливаем pipenv

pip install pipenv

Прежде чем устанавливать что-либо кроме pip убедитесь в том, что Вы знакомы с работой в virtual environment.

Устанавливаем requests

pipenv install requests

Теперь наше python окружение готово к тестированию интерфейсов.

На вашем компьютере может быть установлено несколько версий Python. To узнать какую версию используют ваши программы перейдтие по ссылке

Примеры программ

Создаем файл post.py и пишем следующий код

import requests r = requests.post('http://url.html', data = {'site':'aredel.com'})

Запускаем скрипт

python post.py

Реальный пример 1

Задача

Есть сервер, на котором отображаются подключённые устройства. Назовем его Менеджер_Устройств

Задача - заселить сервер новыми устройствами, количество - 1000 устройств.

Нужно, чтобы у каждого устройства был уникальный ID. Иначе получим ошибку.

Познакомившись с API этого сервера можно узнать, что запрос, которым добавляется новое устройство, выглядит следующим образом:

PUT to http://devm.com:4880/manager/rest/control/devices/Unique_ID/apps/Client_Name/status/$timestamp=Some_value

Также из API известно, что в этом запросе передаётся JSON

{"status": {"clientStatusData": {"message":"Everything is OK", "status":"OK" }, "itemsStatuses": [{ "message":"URN.SU Server simulated", "status":"ENABLED", "name":"Connection", "Number":"0" }, { "message":"TopBicycle.RU Power: 250[mW], Session: 1", "status":"OK", "name":"AndreyOlegovih.ru", "Number":"1" }], "runningStatus": { "restarted":"true", "uptimeMSec":10000} }, "clientInfo": { "applicationInfo":{"applicationVersion":"19.61.04.12" }, "deviceInfo": {"itemDescription": "Good Device Number 1", "itemModelId":"Model ID 1", "statusUpdateIntervalMSec":"30000" } }, }

По заданию: itemModelId , itemDescription и uptime нужно делать уникальными.

Таким образом уникальных величины должно быть четыре.

Можно, конечно, посылать запросы из SOAP UI по одному, вручную изменяя Unique_ID, но это будет очень долго.

Решение. Часть 1 - Комментарии

Пишем скрипт на Python, испльзуя знания, полученные в начале этой статьи и в статье Python. Сначала импортируем нужные библиотеки.

Затем делим URL на три части: первая и третья остаются неизменными, а между ними мы будем вставлять переменую.

После этого запускаем цикл от 1 до 1000. Значения переменной i мы будем использовать для создания уникальных ID. С этой целью создадим три переменные: для наглядности они все будут заканчиваться на _var а затем я выделю их зелёным цветом.

И, наконец, создаём переменную json_string, в которую запишем нужный нам JSON с небольшими изменениями. Вместо статичных itemModelId , itemDescription, uptime вставляем переменные заданные на предыдущем шаге.

Внимательно следим за отступами, т.к. отступ обозначает тело цикла.

Решение. Часть 2 - Код

import requests import json import urllib2 import uuid headers = {"Content-Type": "application/json"} base_url='http://device_manager.com:4880 / manager / rest /control / devices /' end_url=' / apps / Client_Name / status /$timestamp=1529086249' for i in range(1,1000): c=str(i) item_model_var="AASuperDevice"+c item_description_var="Andrei"+c uptime_var=i * 50 Unique_ID_var="Unique_ID"+c url=base_url+str(Unique_ID_var)+end_url json_string= {"status": {"clientStatusData": {"message":"Everything is OK", "status":"OK" }, "itemsStatuses": [{ "message":"URN.SU Server simulated", "status":"ENABLED", "name":"Connection", "Number":"0" }, { "message":"TopBicycle.RU Power: 250[mW], Session: 1", "status":"OK", "name":"AndreyOlegovih.ru", "Number":"1" }], "runningStatus": { "restarted":"true", "uptimeMSec":uptime_var} }, "clientInfo": { "applicationInfo":{"applicationVersion":"19.61.04.12" }, "deviceInfo": {"itemDescription": item_description_var, "itemModelId":item_model_var, "statusUpdateIntervalMSec":"30000" } }, } data_json=json.dumps(json_string) r=requests.put(url, data=data_json, headers=headers)

Debugging

Если что-то пошло не так можно попробовать простейший способ отследить в какой момент появляется ошибка - добавить в конец кода

print ("i =", i) print ("i string=", c) print (item_model_var) print (u) print (uptime_var) print(r.text)

Но имейте в виду, что в серьёзном софте лучше делать отладку дебаггером.

Unittest

unittest это библиотека для тестирования, которая входит в Python по умолчанию

Разберём простейший пример использования.

Создадим два файла calc.py и test_calc.py

#calc.py def add(x, y): """Add Function""" return x + y + 3 def subtract(x, y): """Subtract Function""" return x - y def multiply(x, y): """Multiply Function""" return x * y def divide(x, y): """Divide Function""" if y == 0: raise ValueError("Can not divide by zero!") return x / y

Как видите, в функции add специально допущена ошибка - вместо сложения двух переменных к ним ещё добавляется число 3

#test_calc.py import unittest import calc #https://docs.python.org/3/library/unittest.html#unittest.TestCase.debug class TestCalc(unittest.TestCase): def test_add(self): result = calc.add(10, 5) self.assertEqual(result, 15) #python3 -m unittest test_calc.py

Для запуска теста перейдём в директорию с файлами и в консоли выполним команду

python3 -m unittest test_calc.py

F ====================================================================== FAIL: test_add (test_calc.TestCalc) ---------------------------------------------------------------------- Traceback (most recent call last): File "/mnt/c/Users/username/python/unittest/test_calc.py", line 10, in test_add self.assertEqual(result, 15) AssertionError: 18 != 15 ---------------------------------------------------------------------- Ran 1 test in 0.001s FAILED (failures=1)

Исправим ошибку

#calc.py def add(x, y): """Add Function""" return x + y ...

python3 -m unittest test_calc.py

. ---------------------------------------------------------------------- Ran 1 test in 0.000s OK

To запустить этот тест в PyCharm или запускать его из консоли, но без дополнительного указания -m unittest добавим в конец файла test_calc.py две строчки:

#test_calc.py ... if __name__ == '__main__': unittest.main()

Теперь можно запускать тест командой

python3 test_calc.py

Напишем тесты для всех функций из calc.py

Снова специально допустим ошибку, например, в третьем тесте

import unittest import calc class TestCalc(unittest.TestCase): def test_add(self): self.assertEqual(calc.add(10, 5), 15) self.assertEqual(calc.add(-1, 1), 0) self.assertEqual(calc.add(-1, -1), -2) def test_subtract(self): self.assertEqual(calc.subtract(10, 5), 5) self.assertEqual(calc.subtract(-1, 1), -2) self.assertEqual(calc.subtract(-1, -1), 0) def test_multiply(self): self.assertEqual(calc.multiply(10, 5), 70) self.assertEqual(calc.multiply(-1, 1), -1) self.assertEqual(calc.multiply(-1, -1), 1) def test_divide(self): self.assertEqual(calc.divide(10, 5), 2) self.assertEqual(calc.divide(-1, 1), -1) self.assertEqual(calc.divide(-1, -1), 1) if __name__ == '__main__': unittest.main()

Запустим тест

python3 test_calc.py

..F. ====================================================================== FAIL: test_multiply (__main__.TestCalc) ---------------------------------------------------------------------- Traceback (most recent call last): File "C:/Users/username/.PyCharmCE2018.3/config/scratches/test_calc.py", line 17, in test_multiply self.assertEqual(calc.multiply(10, 5), 70) AssertionError: 50 != 70 ---------------------------------------------------------------------- Ran 4 tests in 0.001s FAILED (failures=1) Process finished with exit code 1

Обратите внимание на первую строчку, точки означают успешное выполнение теста. F - провал теста.

..F. означает, что первый, второй и четвёртый тесты прошли успешно, а в третьем ошибка

Assertion который вернул FALSE также виден self.assertEqual(calc.multiply(10, 5), 70)

И ошибка AssertionError: 50 != 70

Имея такой подробный результат мы легко исправляем ошибку

self.assertEqual(calc.multiply(10, 5), 70)

Меняем на

self.assertEqual(calc.multiply(10, 5), 50)

Запустим тест

python3 test_calc.py

.... ---------------------------------------------------------------------- Ran 4 tests in 0.001s OK Process finished with exit code 0

Теория для unittest

Для тех, кто интересуется устарел ил unittest или нет, моё скромное мнение состоит в том, что нет.

В мире Python более модным считается PyTest одна из причин - более простой синтаксис.

Тем не менее unittest удобен тем, что он встроен в стандартную библиотеку и тем, что в других языках программирования тоже есть похожие фреймворки.

Test Fixture

Единичный тест называется тест кейсом (Test Case).

Часто нужно запустить несколько тест-кейсов, которым требуется для запуска одно и то же.

Например, получить один и тот же объет или запустить Selenium Webdriver или что-то другое.

To не писать в каждом тест-кейсе одно и то же можно воспользоваться методами setUp и tearDown которые создаются один раз для каждого класса и будут запускаться перед каждым тест-кейсом.

Такая комбинация setUp + tearDown называется Test Fixture

Test Fixture = setUp + tearDown

Составляющие части любого теста

Порядок выполнения тестов обычно следующий:

Подготовка к тесту

Непосредственное действие, например, запуск определённой функции

Проверка результата на соответствие ожиданию.

Arrange → Act → Assert

Длинных тестов в которых происходит множество чередующихся действий и проверок следует по возможности избегать.

Лучше написать несколько небольших тестов чем один длинный. Тогда при фэйле какого-то из хорошо структурированных тестов будет легче понять, что именно не работает.

Pytest

Добавить Pytest в Pycharm

Settings (Ctrl + Alt + S) → Tools → Python Integrated Tools → Default test runner:

Выбрать pytest. Если он ещё не был добавлен появится предупреждение и кнопка Fix.

Нужно нажать кнопку Fix

Errors

NameError: name 'pytest' is not defined

Нужно импортировать pytest.

import pytest

Статьи о Python
Development на Python
Интерактивный режим
Виртуальное окружение
str: строки
\: перенос строки
Списки
Циклы
Методы
Функции
*args **kwargs
enum
Опеределить тип переменной Python
Работа с REST API на Python
Файлы: записать, прочитать, дописать, контекстный менеджер…
Скачать файл по сети
SQLite3: работа с БД
datetime: Date and Time в Python
Selenium + Python
Сложности при работе с Python
DJANGO
Flask
Скрипт для ZPL принтера
socket :Python Sockets
subprocess: выполнение bash команд из Python
multiprocessing: несколько процессов одновременно
psutil: cистемные ресурсы
sys.argv: аргументы командной строки
PyCharm: IDE
pydantic: валидация данных
paramiko: SSH из Python
enumerate
json.dumps
Articles about Testing
API testing lessons
API testing
Selenium + Python
SOAP UI
JMeter
Bash for QA Engineer
Clumsy 0.2
Python script for ZPL
Python Sockets
Integration Testing
Share in social media: