SlideShare a Scribd company logo
QuickCheck в PythonQuickCheck в Python
Проверка гипотез и поиск ошибокПроверка гипотез и поиск ошибок
Шорин Александр / @kxepal
О чем пойдет речь:О чем пойдет речь:
Что не так с нашими тестами
Что такое QuickCheck...
...и как его неправильно портировать
Знакомство с Hypothesis
Время офигенных историй
Что не так с нашими тестами?Что не так с нашими тестами?
Простейший примерПростейший пример
def qsort(list_):
if len(list_) <= 1:
return list_
head = qsort([x for x in list_[1:] if x < list_[0]])
tail = qsort([x for x in list_[1:] if x > list_[0]])
return head + [list_[0]] + tail
(да, в жизни все сложнее)(да, в жизни все сложнее)
ТестируемТестируем
def test_empty():
assert qsort([]) == []
def test_sort():
assert qsort([3, 2, 1]) == [1, 2, 3]
def test_sorted():
assert qsort([1, 2, 3]) == [1, 2, 3]
Шаблонная копипастаШаблонная копипаста
Мы можем решить проблему копипасты...Мы можем решить проблему копипасты...
@pytest.mark.parametrize(('value', 'result'), [
([], []),
([1, 2, 3], [1, 2, 3]),
([3, 2, 1], [1, 2, 3]),
])
def test_qsort(value, result):
assert qsort(value) == result
...хотя может показаться, что нет...хотя может показаться, что нет
@pytest.mark.parametrize(('value', 'result'), [
([], []),
([1, 2, 3], [1, 2, 3]),
([3, 2, 1], [1, 2, 3]),
(['a', 'c', 'b'], ['a', 'b', 'c']),
(['a', 'A'], ['A', 'a']),
([[0], [3], [1, 2, 3]], [[0], [1, 2, 3], [3]]),
...
])
def test_qsort(value, result):
assert qsort(value) == result
Можем ли мы проверить все допустимые случаи?Можем ли мы проверить все допустимые случаи?
+----------------------------------------------------------------------+
| ВЕЛИКОЕ ВСЁ |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| +---------------------+
| | Приемлемые данные |
| | Х |
+------------------------------------------------+-------------------^-+
|
Наши тесты -------+
Ошибку в коде мы так и неОшибку в коде мы так и не
нашлинашли
def test_qsort():
> assert qsort([1, 1, 1]) == [1, 1, 1]
E assert [1] == [1, 1, 1]
E Right contains more items, first extra item: 1
E Full diff:
E - [1]
E + [1, 1, 1]
Bug Driven DevelopmentBug Driven Development
Bug. Test. Fix. Repeat.Bug. Test. Fix. Repeat.
РезюмируемРезюмируем
Мы не тестируем наш код, мы закрепляем его
поведение
Мы не способны описать все пограничные
случаи
Баги всегда впереди нас
QuickCheckQuickCheck
Изначально написан на Haskell
Основывается на научно-исследовательских
работах
Позиционируется как библиотека для
автоматического тестирование функций на
основе спецификаций и свойств данных
Портирован на Scala, Erlang, Clojure, JavaScript...
Продвигается компанией Quviq
http://www.cse.chalmers.se/~rjmh/QuickCheck/
Как оно работает (примерно)Как оно работает (примерно)
passed
+-----------------------+
v |
+-----------+ sample +------+ failed +----------+ +--------+
| Generator | --------> | Test | --------> | Shrinker | --> | Report |
+-----------+ +------+ +----------+ +--------+
| ^ sample | ^
| +------------------+ |
| |
+-----------------------------------------------------------+
success
Выглядит просто?Выглядит просто?
Нельзя просто взять и написатьНельзя просто взять и написать
правильный QuickCheckправильный QuickCheck
https://github.com/agrif/pyquickcheck
https://github.com/Cue/qc
https://github.com/dbravender/qc
https://github.com/futoase/PyQCheck
https://github.com/JesseBuesking/pythoncheck
https://github.com/markchadwick/paycheck
https://github.com/msoedov/quick.py
https://github.com/npryce/python-factcheck
https://github.com/Xion/pyqcy
https://github.com/zombiecalypse/qcc
https://pypi.python.org/pypi/pytest-quickcheck
...
Типичные ошибкиТипичные ошибки
Слепое копирование Haskell реализации
Использование типов в качестве генераторов
Отсутствующий или же "глупый" shrinking
Заброшенные или же в зачаточном состоянии
Hypothesis [haɪˈpɒθɪsɪs]Hypothesis [haɪˈpɒθɪsɪs]
Автор: David R. MacIver
https://github.com/DRMacIver/hypothesis
Что умеетЧто умеет
Генерация всех основных типов данных
Рекурсивные типы
State Machine, N-ary деревья
Позволяет создавать свои стратегии без
погружения в детали
Интегрирован с Fake Factory, Django, pytest
Следит за качеством тестов
Запоминает найденные баги
Хорошо настраивается
http://hypothesis.readthedocs.org/en/master/data.htmlhttp://hypothesis.readthedocs.org/en/master/data.html
Генерация данныхГенерация данных
from hypothesis import strategies as st
def nulls(): return st.none()
def booleans(): return st.booleans()
def numbers(): return st.integers() | st.floats()
def strings(): return st.text()
def arrays(elements): return st.lists(elements)
def objects(elements): return st.dictionaries(strings(), elements)
def values():
simple_values = nulls() | booleans() | numbers() | strings()
return (simple_values
| st.recursive(simple_values,
lambda children: arrays(children)
| objects(children)))
Генерация данныхГенерация данных
>>> doc = json_st.objects(json_st.values())
>>> doc.example()
{'G Gu202fn G𝞠u202f_n( n(nn_ n': 9.943339378805967e-309}
>>> doc.example()
{'': None, 'x85': '', 'U00014481': None,
'u3000': -2.45410883359415e-309, ' x85': 1.5564453946197205e-308,
'I': ' ',
'u3000u2029': -9.05230966734913e-309, 'U00014481U00014481 ⁱ': None,
'Nj': -1.80149788818e-311, ' ': -1.414261190585428e+202,
'u2029ⁱNj': ' ', ' u2029': inf, ' ': ' ',
'u3000 ': -1.0065151140507456e+206, 'Nj ': None, ' ⁱ': None,
'IU00014481': -1.2296585975031088e+145, 'x80': ' ',
'x85ⁱ x80x80Nj': -6.438869672267061e+116, ' ': None,
'u3000x80': None, 'u2029x80Nj': -698356955173.6532, ' ': ' ',
' x85': None, 'x85ⁱU00014481': None, ' ': None, 'ⁱ': None,
' u3000 ': ' '}
Поиск ошибокПоиск ошибок
>>> @given(json_st.objects(json_st.values()))
... def test_json(doc):
... assert json.loads(json.dumps(doc)) == doc
>>> test_json()
Falsifying example: test_json(doc={'': nan})
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in test_json
File "./hypothesis/core.py", line 583, in wrapped_test
print_example=True, is_final=True
File "./hypothesis/executors/executors.py", line 25, in default_executor
return function()
File "./hypothesis/core.py", line 365, in run
return test(*args, **kwargs)
File "<stdin>", line 3, in test_json
AssertionError
Поиск ошибокПоиск ошибок
>>> @given(json_st.objects(json_st.values()))
... @settings(verbosity=hypothesis.Verbosity.verbose)
... def test_json(doc):
... assert json.loads(json.dumps(doc)) == doc
>>> test_json()
Поиск ошибокПоиск ошибок
Trying example: test_json(doc={})
Trying example: test_json(doc={'': True})
Trying example: test_json(doc={'': None})
Trying example: test_json(doc={'': False})
Trying example: test_json(doc={'': -43203256341979896423482879160843})
Trying example: test_json(doc={'': 24})
...
Trying example: test_json(doc={'': 100440})
Trying example: test_json(doc={'': 30323947834323202215971170911015})
Trying example: test_json(doc={'': 0.0})
Trying example: test_json(doc={'': inf})
Trying example: test_json(doc={'': -inf})
Successfully shrunk example 27 times
Falsifying example: test_json(doc={'': nan})
Исправление ошибокИсправление ошибок
import math
from hypothesis import strategies as st
def numbers():
return st.integers() | st.floats().filter(math.isfinite)
Property-тестированиеProperty-тестирование
@hypothesis.given(st.lists(st.integers()))
def test_qsort(l):
ls = qsort(l)
for i in ls:
assert i in l
l.remove(i)
assert len(l) == 0
assert all(ls[i - 1] <= ls[i] for i in range(1, len(ls)))
Property-тестированиеProperty-тестирование
l = [0]
@hypothesis.given(st.lists(st.integers()))
def test_qsort(l):
ls = qsort(l)
for i in ls:
assert i in l
l.remove(i)
> assert len(l) == 0
E assert 1 == 0
E + where 1 = len([0])
foo.py:18: AssertionError
---------------- Hypothesis ----------------
Falsifying example: test_qsort(l=[0, 0])
База примеровБаза примеров
Hypothesis сохраняет найденные ошибки в SQLite
базу для последующего воспроизведения.
Health CheckHealth Check
Генерируемые данные слишком большие
Слишком строгая фильтрация
Слишком медленная стратегия
Использование random вызовов в коде
Тест возвращает результат
Гибкая настройкаГибкая настройка
Количество найденных успехных примеров
(example)
Количество итераций на тест
Глубина поиска минимального примера
Слишком медленная стратегия
Время выполнения теста
QA контроль
Профили
ПрофилиПрофили
import os
from hypothesis import settings
from hypothesis import Verbosity
settings.register_profile("ci", settings(max_examples=1000))
settings.register_profile("dev", settings(max_examples=10))
settings.register_profile("debug", settings(max_examples=10,
verbosity=Verbosity.verbose))
settings.load_profile(os.getenv('HYPOTHESIS_PROFILE', 'default'))
Что хорошо тестируетсяЧто хорошо тестируется
Алгоритмы
Структуры данных любой сложности
Реализации протоколов, парсеров, базы данных
Любые чистые функции
Детерминированные API
«QuickCheck в Python: проверка гипотез и поиск ошибок», Александр Шорин, Rambler&Co
«QuickCheck в Python: проверка гипотез и поиск ошибок», Александр Шорин, Rambler&Co
«QuickCheck в Python: проверка гипотез и поиск ошибок», Александр Шорин, Rambler&Co
Полезные ссылкиПолезные ссылки
Hypothesis
https://github.com/DRMacIver/hypothesis
Hypothesis Talks
https://github.com/DRMacIver/hypothesis-talks
Conjecture
https://github.com/DRMacIver/conjecture
PYCON UK 2015: Finding more bugs with less work
https://www.youtube.com/watch?v=62ubHXzD8tM
How I handled Erlang R18 Maps with QuickCheck
https://vimeo.com/143849945
СurEr - Concolic Testing
https://www.youtube.com/watch?v=XVOV0KQAf-8
Спасибо за внимание!Спасибо за внимание!

More Related Content

PDF
Монады для барабанщиков. Антон Холомьёв
PDF
2.4 Использование указателей
PDF
Очень вкусный фрукт Guava
PDF
Магия в Python: Дескрипторы. Что это?
PPT
Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014
PDF
Об особенностях использования значимых типов в .NET
PDF
Pyton – пробуем функциональный стиль
PDF
Красота и изящность стандартной библиотеки Python
Монады для барабанщиков. Антон Холомьёв
2.4 Использование указателей
Очень вкусный фрукт Guava
Магия в Python: Дескрипторы. Что это?
Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014
Об особенностях использования значимых типов в .NET
Pyton – пробуем функциональный стиль
Красота и изящность стандартной библиотеки Python

What's hot (20)

PDF
Sergii Tsypanov "Performance 1001 Tips"
ODP
Отладка в Erlang, trace/dbg
ODP
Charming python sc2-8
PDF
8 встреча — Язык программирования Python (В. Ананьев)
PDF
Оптимизация производительности Python
PDF
Язык программирования Go для Perl-программистов
PPT
Производительность в Django
PDF
Распространённые ошибки оценки производительности .NET-приложений
PDF
Интерпретирование языков с помощью Free-монад
PDF
Очередной скучный доклад про логгирование
PDF
Профилирование и отладка Django
ODP
XML Magic
PDF
«Память и Python. Что надо знать для счастья?» Алексей Кузьмин, ЦНС
PDF
Python dict: прошлое, настоящее, будущее
PDF
Java 8 puzzlers
PPTX
Мифы и легенды Java Stream API
PPTX
Programming Java - Lecture 02 - Objects - Lavrentyev Fedor
PDF
Yield at me 'cause I'm awaiting: асинхронные итераторы в C# 8
PDF
Лекция 1. Начало.
PPTX
Повседневный С++: алгоритмы и итераторы @ C++ Russia 2017
Sergii Tsypanov "Performance 1001 Tips"
Отладка в Erlang, trace/dbg
Charming python sc2-8
8 встреча — Язык программирования Python (В. Ананьев)
Оптимизация производительности Python
Язык программирования Go для Perl-программистов
Производительность в Django
Распространённые ошибки оценки производительности .NET-приложений
Интерпретирование языков с помощью Free-монад
Очередной скучный доклад про логгирование
Профилирование и отладка Django
XML Magic
«Память и Python. Что надо знать для счастья?» Алексей Кузьмин, ЦНС
Python dict: прошлое, настоящее, будущее
Java 8 puzzlers
Мифы и легенды Java Stream API
Programming Java - Lecture 02 - Objects - Lavrentyev Fedor
Yield at me 'cause I'm awaiting: асинхронные итераторы в C# 8
Лекция 1. Начало.
Повседневный С++: алгоритмы и итераторы @ C++ Russia 2017
Ad

Viewers also liked (20)

PDF
«iPython & Jupyter: 4 fun & profit», Лев Тонких, Rambler&Co
PDF
«Advanced {product_name} configuring», Алексей Макеев, Mail.Ru Group
PDF
Александр Лисаченко, Alpari, «Решение вопросов сквозной функциональности в пр...
PDF
«Пиринговый веб на JavaScript», Денис Глазков
PDF
Максим Попов, Mail.Ru Group, «Асинхронные запросы в MySQL или когда PDO стано...
PDF
Руслан Ханов, «Контейнер сервисов — Что? Где? Когда?»
PDF
«Свой PhoneGap за 15 минут», Алексей Охрименко (IPONWEB)
PPTX
«Компонентная верстка с AngularJS», Андрей Яманов (CTO TeamHunt)
PDF
Что надо знать о HTTP/2
PDF
Сергей Николенко, Deloitte Analytics Institute, Высшая Школа Экономики, «От н...
PDF
Сергей Герасимов (ВМК МГУ), Александр Мещеряков (Институт космических исследо...
PDF
Profiling and optimizing go programs
PDF
Ростислав Яворский, Высшая Школа Экономики, «Как использовать анализ сетевых ...
PDF
Парсим CSS
PPTX
Александр Щусь, Mail.Ru Group, Детектирование взломов почтовых аккаунтов
PDF
Иван Лобов, Data-Centric Alliance, «Текущие тенденции в сфере исследования гл...
PDF
Александр Семёнов, МТС, Высшая Школа Экономики, «Анализ социальных сетей в те...
PDF
Роман Чеботарёв, КРОК, «Выбираем метрику оценки качества модели»
PDF
«Pocker - GUI для Docker», Владимир Василькин (ALMWorks, Санкт-Петербург)
PDF
Определение качества сетевого соединения в iOS-почте, Даниил Румянцев, разраб...
«iPython & Jupyter: 4 fun & profit», Лев Тонких, Rambler&Co
«Advanced {product_name} configuring», Алексей Макеев, Mail.Ru Group
Александр Лисаченко, Alpari, «Решение вопросов сквозной функциональности в пр...
«Пиринговый веб на JavaScript», Денис Глазков
Максим Попов, Mail.Ru Group, «Асинхронные запросы в MySQL или когда PDO стано...
Руслан Ханов, «Контейнер сервисов — Что? Где? Когда?»
«Свой PhoneGap за 15 минут», Алексей Охрименко (IPONWEB)
«Компонентная верстка с AngularJS», Андрей Яманов (CTO TeamHunt)
Что надо знать о HTTP/2
Сергей Николенко, Deloitte Analytics Institute, Высшая Школа Экономики, «От н...
Сергей Герасимов (ВМК МГУ), Александр Мещеряков (Институт космических исследо...
Profiling and optimizing go programs
Ростислав Яворский, Высшая Школа Экономики, «Как использовать анализ сетевых ...
Парсим CSS
Александр Щусь, Mail.Ru Group, Детектирование взломов почтовых аккаунтов
Иван Лобов, Data-Centric Alliance, «Текущие тенденции в сфере исследования гл...
Александр Семёнов, МТС, Высшая Школа Экономики, «Анализ социальных сетей в те...
Роман Чеботарёв, КРОК, «Выбираем метрику оценки качества модели»
«Pocker - GUI для Docker», Владимир Василькин (ALMWorks, Санкт-Петербург)
Определение качества сетевого соединения в iOS-почте, Даниил Румянцев, разраб...
Ad

More from Mail.ru Group (20)

PDF
Автоматизация без тест-инженеров по автоматизации, Мария Терехина и Владислав...
PDF
BDD для фронтенда. Автоматизация тестирования с Cucumber, Cypress и Jenkins, ...
PDF
Другая сторона баг-баунти-программ: как это выглядит изнутри, Владимир Дубровин
PDF
Использование Fiddler и Charles при тестировании фронтенда проекта pulse.mail...
PDF
Управление инцидентами в Почте Mail.ru, Антон Викторов
PDF
DAST в CI/CD, Ольга Свиридова
PDF
Почему вам стоит использовать свой велосипед и почему не стоит Александр Бел...
PDF
CV в пайплайне распознавания ценников товаров: трюки и хитрости Николай Масл...
PDF
RAPIDS: ускоряем Pandas и scikit-learn на GPU Павел Клеменков, NVidia
PDF
WebAuthn в реальной жизни, Анатолий Остапенко
PDF
AMP для электронной почты, Сергей Пешков
PDF
Как мы захотели TWA и сделали его без мобильных разработчиков, Данила Стрелков
PDF
Кейсы использования PWA для партнерских предложений в Delivery Club, Никита Б...
PDF
Метапрограммирование: строим конечный автомат, Сергей Федоров, Яндекс.Такси
PDF
Как не сделать врагами архитектуру и оптимизацию, Кирилл Березин, Mail.ru Group
PDF
Этика искусственного интеллекта, Александр Кармаев (AI Journey)
PDF
Нейро-машинный перевод в вопросно-ответных системах, Федор Федоренко (AI Jour...
PDF
Конвергенция технологий как тренд развития искусственного интеллекта, Владими...
PDF
Обзор трендов рекомендательных систем от Пульса, Андрей Мурашев (AI Journey)
PDF
Мир глазами нейросетей, Данила Байгушев, Александр Сноркин ()
Автоматизация без тест-инженеров по автоматизации, Мария Терехина и Владислав...
BDD для фронтенда. Автоматизация тестирования с Cucumber, Cypress и Jenkins, ...
Другая сторона баг-баунти-программ: как это выглядит изнутри, Владимир Дубровин
Использование Fiddler и Charles при тестировании фронтенда проекта pulse.mail...
Управление инцидентами в Почте Mail.ru, Антон Викторов
DAST в CI/CD, Ольга Свиридова
Почему вам стоит использовать свой велосипед и почему не стоит Александр Бел...
CV в пайплайне распознавания ценников товаров: трюки и хитрости Николай Масл...
RAPIDS: ускоряем Pandas и scikit-learn на GPU Павел Клеменков, NVidia
WebAuthn в реальной жизни, Анатолий Остапенко
AMP для электронной почты, Сергей Пешков
Как мы захотели TWA и сделали его без мобильных разработчиков, Данила Стрелков
Кейсы использования PWA для партнерских предложений в Delivery Club, Никита Б...
Метапрограммирование: строим конечный автомат, Сергей Федоров, Яндекс.Такси
Как не сделать врагами архитектуру и оптимизацию, Кирилл Березин, Mail.ru Group
Этика искусственного интеллекта, Александр Кармаев (AI Journey)
Нейро-машинный перевод в вопросно-ответных системах, Федор Федоренко (AI Jour...
Конвергенция технологий как тренд развития искусственного интеллекта, Владими...
Обзор трендов рекомендательных систем от Пульса, Андрей Мурашев (AI Journey)
Мир глазами нейросетей, Данила Байгушев, Александр Сноркин ()

«QuickCheck в Python: проверка гипотез и поиск ошибок», Александр Шорин, Rambler&Co

  • 1. QuickCheck в PythonQuickCheck в Python Проверка гипотез и поиск ошибокПроверка гипотез и поиск ошибок Шорин Александр / @kxepal
  • 2. О чем пойдет речь:О чем пойдет речь: Что не так с нашими тестами Что такое QuickCheck... ...и как его неправильно портировать Знакомство с Hypothesis Время офигенных историй
  • 3. Что не так с нашими тестами?Что не так с нашими тестами?
  • 4. Простейший примерПростейший пример def qsort(list_): if len(list_) <= 1: return list_ head = qsort([x for x in list_[1:] if x < list_[0]]) tail = qsort([x for x in list_[1:] if x > list_[0]]) return head + [list_[0]] + tail (да, в жизни все сложнее)(да, в жизни все сложнее)
  • 5. ТестируемТестируем def test_empty(): assert qsort([]) == [] def test_sort(): assert qsort([3, 2, 1]) == [1, 2, 3] def test_sorted(): assert qsort([1, 2, 3]) == [1, 2, 3] Шаблонная копипастаШаблонная копипаста
  • 6. Мы можем решить проблему копипасты...Мы можем решить проблему копипасты... @pytest.mark.parametrize(('value', 'result'), [ ([], []), ([1, 2, 3], [1, 2, 3]), ([3, 2, 1], [1, 2, 3]), ]) def test_qsort(value, result): assert qsort(value) == result
  • 7. ...хотя может показаться, что нет...хотя может показаться, что нет @pytest.mark.parametrize(('value', 'result'), [ ([], []), ([1, 2, 3], [1, 2, 3]), ([3, 2, 1], [1, 2, 3]), (['a', 'c', 'b'], ['a', 'b', 'c']), (['a', 'A'], ['A', 'a']), ([[0], [3], [1, 2, 3]], [[0], [1, 2, 3], [3]]), ... ]) def test_qsort(value, result): assert qsort(value) == result
  • 8. Можем ли мы проверить все допустимые случаи?Можем ли мы проверить все допустимые случаи? +----------------------------------------------------------------------+ | ВЕЛИКОЕ ВСЁ | | | | | | | | | | | | | | | | | | | | +---------------------+ | | Приемлемые данные | | | Х | +------------------------------------------------+-------------------^-+ | Наши тесты -------+
  • 9. Ошибку в коде мы так и неОшибку в коде мы так и не нашлинашли def test_qsort(): > assert qsort([1, 1, 1]) == [1, 1, 1] E assert [1] == [1, 1, 1] E Right contains more items, first extra item: 1 E Full diff: E - [1] E + [1, 1, 1]
  • 10. Bug Driven DevelopmentBug Driven Development Bug. Test. Fix. Repeat.Bug. Test. Fix. Repeat.
  • 11. РезюмируемРезюмируем Мы не тестируем наш код, мы закрепляем его поведение Мы не способны описать все пограничные случаи Баги всегда впереди нас
  • 12. QuickCheckQuickCheck Изначально написан на Haskell Основывается на научно-исследовательских работах Позиционируется как библиотека для автоматического тестирование функций на основе спецификаций и свойств данных Портирован на Scala, Erlang, Clojure, JavaScript... Продвигается компанией Quviq http://www.cse.chalmers.se/~rjmh/QuickCheck/
  • 13. Как оно работает (примерно)Как оно работает (примерно) passed +-----------------------+ v | +-----------+ sample +------+ failed +----------+ +--------+ | Generator | --------> | Test | --------> | Shrinker | --> | Report | +-----------+ +------+ +----------+ +--------+ | ^ sample | ^ | +------------------+ | | | +-----------------------------------------------------------+ success
  • 15. Нельзя просто взять и написатьНельзя просто взять и написать правильный QuickCheckправильный QuickCheck https://github.com/agrif/pyquickcheck https://github.com/Cue/qc https://github.com/dbravender/qc https://github.com/futoase/PyQCheck https://github.com/JesseBuesking/pythoncheck https://github.com/markchadwick/paycheck https://github.com/msoedov/quick.py https://github.com/npryce/python-factcheck https://github.com/Xion/pyqcy https://github.com/zombiecalypse/qcc https://pypi.python.org/pypi/pytest-quickcheck ...
  • 16. Типичные ошибкиТипичные ошибки Слепое копирование Haskell реализации Использование типов в качестве генераторов Отсутствующий или же "глупый" shrinking Заброшенные или же в зачаточном состоянии
  • 17. Hypothesis [haɪˈpɒθɪsɪs]Hypothesis [haɪˈpɒθɪsɪs] Автор: David R. MacIver https://github.com/DRMacIver/hypothesis
  • 18. Что умеетЧто умеет Генерация всех основных типов данных Рекурсивные типы State Machine, N-ary деревья Позволяет создавать свои стратегии без погружения в детали Интегрирован с Fake Factory, Django, pytest Следит за качеством тестов Запоминает найденные баги Хорошо настраивается http://hypothesis.readthedocs.org/en/master/data.htmlhttp://hypothesis.readthedocs.org/en/master/data.html
  • 19. Генерация данныхГенерация данных from hypothesis import strategies as st def nulls(): return st.none() def booleans(): return st.booleans() def numbers(): return st.integers() | st.floats() def strings(): return st.text() def arrays(elements): return st.lists(elements) def objects(elements): return st.dictionaries(strings(), elements) def values(): simple_values = nulls() | booleans() | numbers() | strings() return (simple_values | st.recursive(simple_values, lambda children: arrays(children) | objects(children)))
  • 20. Генерация данныхГенерация данных >>> doc = json_st.objects(json_st.values()) >>> doc.example() {'G Gu202fn G𝞠u202f_n( n(nn_ n': 9.943339378805967e-309} >>> doc.example() {'': None, 'x85': '', 'U00014481': None, 'u3000': -2.45410883359415e-309, ' x85': 1.5564453946197205e-308, 'I': ' ', 'u3000u2029': -9.05230966734913e-309, 'U00014481U00014481 ⁱ': None, 'Nj': -1.80149788818e-311, ' ': -1.414261190585428e+202, 'u2029ⁱNj': ' ', ' u2029': inf, ' ': ' ', 'u3000 ': -1.0065151140507456e+206, 'Nj ': None, ' ⁱ': None, 'IU00014481': -1.2296585975031088e+145, 'x80': ' ', 'x85ⁱ x80x80Nj': -6.438869672267061e+116, ' ': None, 'u3000x80': None, 'u2029x80Nj': -698356955173.6532, ' ': ' ', ' x85': None, 'x85ⁱU00014481': None, ' ': None, 'ⁱ': None, ' u3000 ': ' '}
  • 21. Поиск ошибокПоиск ошибок >>> @given(json_st.objects(json_st.values())) ... def test_json(doc): ... assert json.loads(json.dumps(doc)) == doc >>> test_json() Falsifying example: test_json(doc={'': nan}) Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 2, in test_json File "./hypothesis/core.py", line 583, in wrapped_test print_example=True, is_final=True File "./hypothesis/executors/executors.py", line 25, in default_executor return function() File "./hypothesis/core.py", line 365, in run return test(*args, **kwargs) File "<stdin>", line 3, in test_json AssertionError
  • 22. Поиск ошибокПоиск ошибок >>> @given(json_st.objects(json_st.values())) ... @settings(verbosity=hypothesis.Verbosity.verbose) ... def test_json(doc): ... assert json.loads(json.dumps(doc)) == doc >>> test_json()
  • 23. Поиск ошибокПоиск ошибок Trying example: test_json(doc={}) Trying example: test_json(doc={'': True}) Trying example: test_json(doc={'': None}) Trying example: test_json(doc={'': False}) Trying example: test_json(doc={'': -43203256341979896423482879160843}) Trying example: test_json(doc={'': 24}) ... Trying example: test_json(doc={'': 100440}) Trying example: test_json(doc={'': 30323947834323202215971170911015}) Trying example: test_json(doc={'': 0.0}) Trying example: test_json(doc={'': inf}) Trying example: test_json(doc={'': -inf}) Successfully shrunk example 27 times Falsifying example: test_json(doc={'': nan})
  • 24. Исправление ошибокИсправление ошибок import math from hypothesis import strategies as st def numbers(): return st.integers() | st.floats().filter(math.isfinite)
  • 25. Property-тестированиеProperty-тестирование @hypothesis.given(st.lists(st.integers())) def test_qsort(l): ls = qsort(l) for i in ls: assert i in l l.remove(i) assert len(l) == 0 assert all(ls[i - 1] <= ls[i] for i in range(1, len(ls)))
  • 26. Property-тестированиеProperty-тестирование l = [0] @hypothesis.given(st.lists(st.integers())) def test_qsort(l): ls = qsort(l) for i in ls: assert i in l l.remove(i) > assert len(l) == 0 E assert 1 == 0 E + where 1 = len([0]) foo.py:18: AssertionError ---------------- Hypothesis ---------------- Falsifying example: test_qsort(l=[0, 0])
  • 27. База примеровБаза примеров Hypothesis сохраняет найденные ошибки в SQLite базу для последующего воспроизведения.
  • 28. Health CheckHealth Check Генерируемые данные слишком большие Слишком строгая фильтрация Слишком медленная стратегия Использование random вызовов в коде Тест возвращает результат
  • 29. Гибкая настройкаГибкая настройка Количество найденных успехных примеров (example) Количество итераций на тест Глубина поиска минимального примера Слишком медленная стратегия Время выполнения теста QA контроль Профили
  • 30. ПрофилиПрофили import os from hypothesis import settings from hypothesis import Verbosity settings.register_profile("ci", settings(max_examples=1000)) settings.register_profile("dev", settings(max_examples=10)) settings.register_profile("debug", settings(max_examples=10, verbosity=Verbosity.verbose)) settings.load_profile(os.getenv('HYPOTHESIS_PROFILE', 'default'))
  • 31. Что хорошо тестируетсяЧто хорошо тестируется Алгоритмы Структуры данных любой сложности Реализации протоколов, парсеров, базы данных Любые чистые функции Детерминированные API
  • 35. Полезные ссылкиПолезные ссылки Hypothesis https://github.com/DRMacIver/hypothesis Hypothesis Talks https://github.com/DRMacIver/hypothesis-talks Conjecture https://github.com/DRMacIver/conjecture PYCON UK 2015: Finding more bugs with less work https://www.youtube.com/watch?v=62ubHXzD8tM How I handled Erlang R18 Maps with QuickCheck https://vimeo.com/143849945 СurEr - Concolic Testing https://www.youtube.com/watch?v=XVOV0KQAf-8