SlideShare une entreprise Scribd logo
1  sur  43
Télécharger pour lire hors ligne
Лекция 11: Тестирование
Сергей Лебедев
sergei.a.lebedev@gmail.com
23 ноября 2015 г.
Тестировать или не тестировать?1
• Тестировать:
• тесты проверяют корректность кода
• и позволяют бесстрашно изменять код даже в больших
проектах.
• Не тестировать:
• написание тестов требует времени,
• нередко в проекте тестов больше чем кода,
• работающие тесты не гарантируют корректность.
• Тем не менее, ответ очевиден: конечно же тестировать!
1
Здесь и далее под тестированием имеется в виду модульное (и иногда
интеграционное) тестирование aka unit testing.
1 / 35
Пример
>>> import itertools
>>> def rle(iterable):
... """Applies run-length encoding to an iterable."""
... for item, g in itertools.groupby(iterable):
... yield item, sum(1 for _ in g)
...
>>> list(rle("mississippi"))
[('m', 1), ('i', 1), ('s', 2), ('i', 1),
('s', 2), ('i', 1), ('p', 2), ('i', 1)]
2 / 35
Тестирование с помощью print
• Функция print позволяет организовать
полуавтоматическое тестирование вне интерактивной
оболочки.
• Интерпретатор печатает — вы проверяете:
>>> def test_rle():
... print(list(rle("mississippi")))
...
>>> test_rle()
[('m', 1), ('i', 1), ('s', 2), ('i', 1),
('s', 2), ('i', 1), ('p', 2), ('i', 1)]
3 / 35
Модуль doctest
Доктесты и модуль doctest2
import doctest
import itertools
def rle(iterable):
"""Applies run-length encoding to an iterable.
>>> list(rle(""))
[]
>>> list(rle("mississippi"))
[('m', 1), ('i', 1), ('s', 2), ('i', 1),
('s', 2), ('i', 1), ('p', 2), ('i', 1)]
"""
for item, g in itertools.groupby(iterable):
yield item, sum(1 for _ in g)
if __name__ == "__main__":
doctest.testmod()
2
https://docs.python.org/3/library/doctest
4 / 35
Модуль doctest и функция rle
$ python ./test_doctest.py # можно python -m doctest
*****************************************************
File "test_doctest.py", line 10, in __main__.rle
Failed example:
list(rle("mississippi"))
Expected:
[('m', 1), ('i', 1), ('s', 2), ('i', 1),
('s', 2), ('i', 1), ('p', 2), ('i', 1)]
Got:
[('m', 1), ('i', 1), ('s', 2), ('i', 1), ...]
******************************************************
1 items had failures:
1 of 2 in __main__.rle
***Test Failed*** 1 failures.
5 / 35
Модуль doctest и директивы
• Директивы позволяют изменить то, как doctest сравнивает
ожидаемый вывод интерпретатора с фактическим.
• Например, директива NORMALIZE_WHITESPACE нормализует
пробельные символы перед сравнением:
>>> list(rle("mississippi"))
... # doctest: +NORMALIZE_WHITESPACE
[('m', 1), ('i', 1), ('s', 2), ('i', 1),
('s', 2), ('i', 1), ('p', 2), ('i', 1)]
• А директива ELLIPSIS позволяет использовать символ ...,
который совпадает с любой строкой:
>>> list(rle("mississippi"))
... # doctest: +ELLIPSIS
[('m', 1), ('i', 1), ('s', 2), ('i', 1), ...]
6 / 35
Модуль doctest: резюме
• Модуль doctest позволяет проверить реализацию функции
на соответствие записанному сеансу интерпретатора.
• Плюсы:
• доступен в стандартной библиотеке,
• решает задачу тестирования для небольших проектов,
• доктесты их легко читать,
• примеры кода в документации всегда актуальны.
• Минусы:
• доктесты требуют, чтобы у результата было содержательное
строковое представление,
• длинные доктесты ухудшают читаемость документации,
• нет способа запустить подмножество доктестов,
• если в середине доктеста произошла ошибка, оставшаяся
часть не выполнится.
7 / 35
assert
Тестирование с помощью assert
• Напоминание:
• оператор assert принимает два аргумента: условие и
произвольное значение,
• если условие falsy, оператор поднимает исключение
AssertionError.
>>> assert [], 42
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AssertionError: 42
• Протестируем функцию rle:
>>> def test_rle():
... s = "mississippi"
... tmp = set(ch for ch, _count in rle(s))
... assert tmp == set(s[:-1] + s[1])
... assert not list(rle(""))
...
>>> test_rle()
• Вопрос: что вы думаете про такой тест?
8 / 35
Признаки хорошего теста
• Хороший тест:
• корректный,
• понятный читателю,
• конкретный, то есть проверяет что-то одно.
• Попробуем улучшить тест для функции rle:
>>> def test_rle():
... assert rle("mississippi") == [
... ('m', 1), ('i', 1), ('s', 2), ('i', 1),
... ('s', 2), ('i', 1), ('p', 2), ('i', 1)
... ]
...
>>> def test_rle_empty():
... assert not list(rle(""))
...
>>> test_rle_empty()
>>> test_rle()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 4, in test_rle
AssertionError
9 / 35
Оператор assert и сообщения об ошибках
Второй аргумент оператора assert используется для
сообщения об ошибке:
>>> def test_rle():
... actual = rle("mississippi")
... expected = [
... ('m', 1), ('i', 1), ('s', 2), ('i', 1),
... ('s', 2), ('i', 1), ('p', 2), ('i', 1)
... ]
... message = "{} != {}".format(actual, expected)
... assert actual == expected, message
...
>>> test_rle()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 8, in test_rle
AssertionError: <generator object rle at [...]> != 
[('m', 1), ('i', 1), ('s', 2), ...]
10 / 35
Функция assert_equal
• Добавим сообщение об ошибке к тесту test_rle_empty:
def test_rle_empty(): # ^C/^V
actual = list(rle(""))
expected = []
message = "{} != {}".format(actual, expected)
assert actual == expected, message
• Самое время вынести логику сравнения в отдельную
функцию:
def assert_equal(x, y):
assert x == y, "{} != {}".format(x, y)
• Вопрос: что делать, если мы также хотим проверять
утверждения вида ("a", 2) in rle(...)?
11 / 35
assert: резюме
• Оператор assert можно использовать для написания
тестов.
• Плюсы:
• тесты c assert легко читать,
• они не используют ничего кроме стандартных средств
языка,
• в отличие от доктестов это обычные функции.
• Минусы:
• запускать тесты нужно вручную,
• их сложно отлаживать, потому что
• для каждого типа утверждения приходится самостоятельно
конструировать сообщение об ошибке.
12 / 35
Модуль unittest
Модуль unittest3
• Модуль unittest реализует функциональность JUnit для
тестирования кода на Python.
• Наследие Java до сих пор в обилии присутствует в API.
• Перепишем имеющиеся тесты с использованием unittest:
import unittest
class TestHomework(unittest.TestCase):
def test_rle(self):
self.assertEqual(rle("mississippi"), [...])
def test_rle_empty(self):
self.assertEqual(list(rle("")), [])
if __name__ == "__main__":
unittest.main()
3
https://docs.python.org/3/library/unittest
13 / 35
Модуль unittest и функция rle
$ python ./test_homework.py
F.
=========================================================
FAIL: test_rle (__main__.TestHomework)
---------------------------------------------------------
Traceback (most recent call last):
File "./test_homework.py", line 12, in test_rle
self.assertEqual(rle("mississippi"), expected)
AssertionError: <generator object rle at [...]> != 
[('m', 1), ('i', 1), ('s', 2), ...]
---------------------------------------------------------
Ran 2 tests in 0.001s
FAILED (failures=1)
14 / 35
Функция unittest.main
• Функция unittest.main загружает все тесты текущего
модуля и запускает их.
• Тест — метод экземпляра unittest.TestCase,
начинающийся на test.
• При необходимости тесты можно объединять в группы с
помощью класса unittest.TestSuite:
suite = unittest.TestSuite([
TestHomework(),
TestSomethingElse()
])
• Указывать вручную, что нужно запустить довольно досадно.
• Вопрос: можно ли лучше?
15 / 35
Автоматический поиск тестов
# V в текущей директории
$ python -m unittest .
F.
=========================================================
FAIL: test_rle (test_homework.TestHomework)
---------------------------------------------------------
Traceback (most recent call last):
File "./test_homework.py", line 12, in test_rle
self.assertEqual(rle("mississippi"), expected)
AssertionError: <generator object rle at [...]> != 
[('m', 1), ('i', 1), ('s', 2), ...]
---------------------------------------------------------
Ran 2 tests in 0.001s
FAILED (failures=1)
16 / 35
Методы unittest.TestCase
assertEqual(a, b) a == b
assertNotEqual(a, b) a != b
assertTrue(x) bool(x) is True
assertFalse(x) bool(x) is False
assertIs(a, b) a is b
assertIsNot(a, b) a is not b
assertIsNone(x) x is None
assertIsNotNone(x) x is not None
assertIn(a, b) a in b
assertNotIn(a, b) a not in b
assertIsInstance(a, b) isinstance(a, b)
assertNotIsInstance(a, b) not isinstance(a, b)
assertRaises(exc_type) # ?
17 / 35
Модуль unittest и контекст
• ? aka fixtures — способ подготовить контекст, в котором
будут запускаться тесты.
• Это можно использовать, например, для работы с
ресурсами: сокетами, файлами, временными
директориями.
• Пример:
class TestHomeworkWithOracle(unittest.TestCase):
def setUp(self):
self.oracle = RleOracle("http://oracle.rle.com")
def test_rle_against_oracle(self):
s = "mississippi"
self.assertEqual(list(rle(s)), self.oracle(s))
def tearDown(self):
self.oracule.close()
18 / 35
Модуль unittest: резюме
• Модуль unittest — клон JUnit для Python.
• Мы обсудили
• основные сущности unittest,
• как писать и запускать тесты,
• какие полезные методы имеются в классе
unittest.TestCase.
• Плюсы:
• доступен в стандартной библиотеке,
• выводит понятные сообщения об ошибках,
• умеет автоматически находить тесты.
• Минусы:
• API унаследован от Java,
• заставляет писать много лишнего кода,
• читать unittest тесты сложнее, чем доктесты и тесты,
использующие assert.
19 / 35
Пакет py.test
Пакет py.test4
• Пакет py.test — популярная альтернатива unittest для
написания и запуска тестов.
• Отличительная особенность py.test — практически
полное отсутствие API: тесты можно писать, используя
стандартные средства языка.
def test_rle():
assert rle("mississippi") == [
('m', 1), ('i', 1), ('s', 2), ('i', 1),
('s', 2), ('i', 1), ('p', 2), ('i', 1)
]
def test_rle_empty():
assert not list(rle(""))
4
https://pytest.org
20 / 35
Пакет py.test и функция rle
$ python -m pytest -q test_pytest.py
F.
========================= FAILURES ======================
_________________________ test_rle ______________________
def test_rle():
> assert rle("mississippi") == [
('m', 1), ('i', 1), ('s', 2), ('i', 1),
('s', 2), ('i', 1), ('p', 2), ('i', 1)
]
E assert <generator ...> == [('m', 1), ..., ('i', 1)]
E Full diff:
E - <generator object rle at [...]>
E + [('m', 1),
E + ('i', 1),
E ...
E + ('i', 1)]
test_pytest.py:5: AssertionError
21 / 35
Пакет py.test: интерфейс командной строки
• Запустить py.test можно 1001 способом, например:
• найти тесты в текущей директории и во всех вложенных
директориях и запустить их:
$ python -m pytest
• найти и запустить тесты в указанном файле:
$ python -m pytest test_pytest.py
• запустить один тест в файле по имени:
$ python -m pytest test_pytest.py::test_rle
• Что такое тест для py.test5?
• функция test_*,
• метод test_* в классе Test* или в классе, наследующемся
от unittest.TestCase,
• доктест, если py.test был запущен с параметром
--doctest-modules.
5
https://pytest.org/latest/goodpractises.html
22 / 35
Возможности py.test: интроспекция
FF
========================= FAILURES ======================
_________________________ test_in _______________________
def test_in():
> assert 42 in range(0, 10)
E assert 42 in range(0, 10)
test_pytest_introspection.py:3: AssertionError
______________________ test_in_range_ ___________________
def test_in_range():
x = 42
> assert x > 10 and x < 15
E assert (42 > 10 and 42 < 15)
test_pytest_introspection.py:8: AssertionError
23 / 35
Возможности py.test: raises
def test_undo_dict_exceptions():
d = UndoDict()
with pytest.raises(KeyError):
d["foo"]
F
========================= FAILURES ======================
__________________ test_undo_dict_exceptions ____________
def test_undo_dict_exceptions():
d = UndoDict()
with pytest.raises(KeyError):
> d["foo"]
E Failed: DID NOT RAISE
test_assert_raises.py:15: Failed
24 / 35
Возможности py.test: параметрические тесты
def cut_suffix(s, suffix):
return s[:s.rfind(suffix)]
.F.
========================= FAILURES ======================
___________ test_cut_suffix[foobar-boo-foobar] __________
@pytest.mark.parametrize("s,suffix,expected", [
("foobar", "bar", "foo"),
("foobar", "boo", "foobar"),
("foobarbar", "bar", "foobar")
])
def test_cut_suffix(s, suffix, expected):
> assert cut_suffix(s, suffix) == expected
E assert 'fooba' == 'foobar'
E - fooba
E + foobar
E ? +
test_parametric_tests.py:14: AssertionError
25 / 35
Возможности py.test: контекст
• py.test реализует аналоги TestCase.setUp и
TestCase.tearDown,
• но это не самая интересная его возможность.
• Логику подготовки контекста конкретного типа можно
абстрагировать
@pytest.yield_fixture
def oracle(request):
oracle = RleOracle("http://oracle.rle.com")
yield oracle
oracle.close()
• чтобы потом неявно использовать в тестах:
def test_rle_against_oracle(oracle):
s = "mississippi"
assert list(rle(s)) == oracle(s)
26 / 35
Возможности py.test: встроенные контексты
import io
import gzip
import sys
def test_ook_eval(capsys, monkeypatch):
handle = io.StringIO("!")
monkeypatch.setattr(sys, "stdin", handle)
ook_eval("Ook. Ook! Ook! Ook.")
output, _err = capsys.readouterr()
assert output == "!"
def test_reader(tmpdir):
path = tmpdir.join("example.gz")
path.write("")
assert isinstance(reader(str(path)), gzip.GzipFile)
27 / 35
Пакет py.test: резюме
• Пакет py.test — швейцарский нож тестирования в мире
Python.
• Плюсы:
• практически нет API, тесты — обычные функции,
• удобный вывод,
• удобный механизм параметризации тестов,
• приспособления, которые можно переиспользовать,
• множество встроенных возможностей и впечатляющее
количество расширений6
.
• Минусы:
• магия-магия-магия,
• может быть сложнее для понимания, если вы привыкли к
JUnit.
6
https://pytest.org/latest/plugins_index
28 / 35
Пакет
hypothesis
Тестирование свойств
• До текущего момента мы обсуждали тесты, которые
проверяют тривиальные свойства кода:
• равенство ожидаемому значению,
• вхождение результата в коллекцию и так далее.
• Иногда можно формулировать и проверять менее
тривиальные свойства, например:
• если sorted возвращает список, отсортированный по
неубыванию,
• то для любого списка xs и индексов i < j справедливо,
что sorted(xs)[i] <= sorted(xs)[j].
• Вопрос: как проверить это свойство?
29 / 35
Пример: test_sort
import random
def random_array():
size = random.randint(0, 1024)
return [random.randint(-42, 42) for _ in range(size)]
def test_sort():
xs = random_array()
result = sorted(xs)
assert all(xi <= xj
for xi, xj in zip(result, result[1:]))
Такой подход сработает, но
• писать генераторы самостоятельно долго и бессмысленно,
• на практике нас, как правило, интересует минимальный
контрпример.
30 / 35
Пакет hypothesis7
• Пакет hypothesis реализует API для формулирования и
проверки свойств.
• Перепишем test_sort с использованием hypothesis:
import hypothesis.strategies as st
from hypothesis import given
@given(st.lists(st.integers()))
def test_sort(xs):
result = sorted(xs)
assert all(xi <= xj
for xi, xj in zip(result, result[1:]))
• И попробуем обмануть его:
def sorted(xs, f=sorted):
return xs if len(xs) == 8 else f(xs)
7
https://hypothesis.readthedocs.org
31 / 35
Пакет hypothesis и функция sorted
F
========================= FAILURES ======================
________________________ test_sort ______________________
assert all(xi <= xj
E assert all(<generator object <genexpr> at 0x103b81e10>)
----------------------- Hypothesis ----------------------
Falsifying example: test_sort(xs=[0, 0, 0, 1, 0, 0, 0])
32 / 35
Пакет hypothesis: что можно генерировать?
• Примитивные типы:
st.just(x) ==> x, x, x
st.none() ==> None, None, None
st.one_of(a, b, c) ==> a, a, b, c, a
st.booleans() ==> True, False, True
st.integers() ==> 1, -10, 2, 42
st.floats() ==> math.pi, 42.42
• Строки и байты:
st.text() ==> "abra", "cadabra"
st.binary() ==> b"xffxef", b"ascii"
• Коллекции:
st.sampled_from(iterable)
st.tuples(st.foo(), st.bar(), st.boo())
st.lists(st.foo())
st.sets(st.foo())
st.dictionaries(st.foo(), st.bar())
33 / 35
Пакет hypothesis и функция rle
from itertools import chain, repeat, tee
import hypothesis.strategies as st
from hypothesis import given
iterables = st.one_of(st.tuples(st.integers(0, 10)),
st.lists(st.integers(0, 10)),
st.text().map(iter))
@given(iterables)
def test_rle(it):
def encode_decode(it):
return chain.from_iterable(
repeat(item, count) for item, count in rle(it))
it, copy = tee(it)
expected = list(copy)
assert list(encode_decode(it)) == expected
34 / 35
Пакет hypothesis: резюме
• Пакет hypothesis позволяет удобно формулировать и
проверять свойства про Python код.
• Мы обсудили, что можно генерировать, и рассмотрели
несколько примеров.
• Львиная часть функциональности hypothesis осталась за
кадром:
• как сузить пространство поиска контпримеров?
• как написать свой генератор?
• как формулировать зависимые гипотезы?
35 / 35

Contenu connexe

Tendances

Лекция 7. Исключения и менеджеры контекста.
Лекция 7. Исключения и менеджеры контекста.Лекция 7. Исключения и менеджеры контекста.
Лекция 7. Исключения и менеджеры контекста.Roman Brovko
 
Лекция 8. Итераторы, генераторы и модуль itertools.
 Лекция 8. Итераторы, генераторы и модуль itertools. Лекция 8. Итераторы, генераторы и модуль itertools.
Лекция 8. Итераторы, генераторы и модуль itertools.Roman Brovko
 
Лекция 4. Строки, байты, файлы и ввод/вывод.
 Лекция 4. Строки, байты, файлы и ввод/вывод. Лекция 4. Строки, байты, файлы и ввод/вывод.
Лекция 4. Строки, байты, файлы и ввод/вывод.Roman Brovko
 
Лекция 5. Встроенные коллекции и модуль collections.
Лекция 5. Встроенные коллекции и модуль collections.Лекция 5. Встроенные коллекции и модуль collections.
Лекция 5. Встроенные коллекции и модуль collections.Roman Brovko
 
Лекция #5. Введение в язык программирования Python 3
Лекция #5. Введение в язык программирования Python 3Лекция #5. Введение в язык программирования Python 3
Лекция #5. Введение в язык программирования Python 3Яковенко Кирилл
 
Красота и изящность стандартной библиотеки Python
Красота и изящность стандартной библиотеки PythonКрасота и изящность стандартной библиотеки Python
Красота и изящность стандартной библиотеки PythonPython Meetup
 
Декораторы в Python и их практическое использование
Декораторы в Python и их практическое использование Декораторы в Python и их практическое использование
Декораторы в Python и их практическое использование Sergey Schetinin
 
Производительность в Django
Производительность в DjangoПроизводительность в Django
Производительность в DjangoMoscowDjango
 
Python и его тормоза
Python и его тормозаPython и его тормоза
Python и его тормозаAlexander Shigin
 
Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014
Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014
Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014Python Meetup
 
Магия в Python: Дескрипторы. Что это?
Магия в Python: Дескрипторы. Что это?Магия в Python: Дескрипторы. Что это?
Магия в Python: Дескрипторы. Что это?PyNSK
 
Оптимизация производительности Python
Оптимизация производительности PythonОптимизация производительности Python
Оптимизация производительности PythonPyNSK
 
Pyton – пробуем функциональный стиль
Pyton – пробуем функциональный стильPyton – пробуем функциональный стиль
Pyton – пробуем функциональный стильPython Meetup
 
Профилирование и отладка Django
Профилирование и отладка DjangoПрофилирование и отладка Django
Профилирование и отладка DjangoVladimir Rudnyh
 
9. java lecture library
9. java lecture library9. java lecture library
9. java lecture libraryMERA_school
 

Tendances (16)

Лекция 7. Исключения и менеджеры контекста.
Лекция 7. Исключения и менеджеры контекста.Лекция 7. Исключения и менеджеры контекста.
Лекция 7. Исключения и менеджеры контекста.
 
Лекция 8. Итераторы, генераторы и модуль itertools.
 Лекция 8. Итераторы, генераторы и модуль itertools. Лекция 8. Итераторы, генераторы и модуль itertools.
Лекция 8. Итераторы, генераторы и модуль itertools.
 
Лекция 4. Строки, байты, файлы и ввод/вывод.
 Лекция 4. Строки, байты, файлы и ввод/вывод. Лекция 4. Строки, байты, файлы и ввод/вывод.
Лекция 4. Строки, байты, файлы и ввод/вывод.
 
Лекция 5. Встроенные коллекции и модуль collections.
Лекция 5. Встроенные коллекции и модуль collections.Лекция 5. Встроенные коллекции и модуль collections.
Лекция 5. Встроенные коллекции и модуль collections.
 
Лекция #5. Введение в язык программирования Python 3
Лекция #5. Введение в язык программирования Python 3Лекция #5. Введение в язык программирования Python 3
Лекция #5. Введение в язык программирования Python 3
 
Красота и изящность стандартной библиотеки Python
Красота и изящность стандартной библиотеки PythonКрасота и изящность стандартной библиотеки Python
Красота и изящность стандартной библиотеки Python
 
Декораторы в Python и их практическое использование
Декораторы в Python и их практическое использование Декораторы в Python и их практическое использование
Декораторы в Python и их практическое использование
 
Производительность в Django
Производительность в DjangoПроизводительность в Django
Производительность в Django
 
Python и его тормоза
Python и его тормозаPython и его тормоза
Python и его тормоза
 
Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014
Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014
Быстрые конструкции в Python - Олег Шидловский, Python Meetup 26.09.2014
 
Магия в Python: Дескрипторы. Что это?
Магия в Python: Дескрипторы. Что это?Магия в Python: Дескрипторы. Что это?
Магия в Python: Дескрипторы. Что это?
 
Оптимизация производительности Python
Оптимизация производительности PythonОптимизация производительности Python
Оптимизация производительности Python
 
JRebel
JRebelJRebel
JRebel
 
Pyton – пробуем функциональный стиль
Pyton – пробуем функциональный стильPyton – пробуем функциональный стиль
Pyton – пробуем функциональный стиль
 
Профилирование и отладка Django
Профилирование и отладка DjangoПрофилирование и отладка Django
Профилирование и отладка Django
 
9. java lecture library
9. java lecture library9. java lecture library
9. java lecture library
 

En vedette

04 - Практика UML. Описание прецедентов
04 - Практика UML. Описание прецедентов04 - Практика UML. Описание прецедентов
04 - Практика UML. Описание прецедентовRoman Brovko
 
09 - Практика UML. Use Case диаграммы
09 - Практика UML. Use Case диаграммы09 - Практика UML. Use Case диаграммы
09 - Практика UML. Use Case диаграммыRoman Brovko
 
03 - Практика UML. Прецеденты
03 - Практика UML. Прецеденты03 - Практика UML. Прецеденты
03 - Практика UML. ПрецедентыRoman Brovko
 
01 - Практика UML. Нужен ли UML?
01 - Практика UML. Нужен ли UML?01 - Практика UML. Нужен ли UML?
01 - Практика UML. Нужен ли UML?Roman Brovko
 
12 - Практика UML. Создание wireframe
12 - Практика UML. Создание wireframe12 - Практика UML. Создание wireframe
12 - Практика UML. Создание wireframeRoman Brovko
 
02 - Практика UML. Уровни приложения
02 - Практика UML. Уровни приложения02 - Практика UML. Уровни приложения
02 - Практика UML. Уровни приложенияRoman Brovko
 
13 - Практика UML. Переход к разработке
13 - Практика UML. Переход к разработке13 - Практика UML. Переход к разработке
13 - Практика UML. Переход к разработкеRoman Brovko
 

En vedette (7)

04 - Практика UML. Описание прецедентов
04 - Практика UML. Описание прецедентов04 - Практика UML. Описание прецедентов
04 - Практика UML. Описание прецедентов
 
09 - Практика UML. Use Case диаграммы
09 - Практика UML. Use Case диаграммы09 - Практика UML. Use Case диаграммы
09 - Практика UML. Use Case диаграммы
 
03 - Практика UML. Прецеденты
03 - Практика UML. Прецеденты03 - Практика UML. Прецеденты
03 - Практика UML. Прецеденты
 
01 - Практика UML. Нужен ли UML?
01 - Практика UML. Нужен ли UML?01 - Практика UML. Нужен ли UML?
01 - Практика UML. Нужен ли UML?
 
12 - Практика UML. Создание wireframe
12 - Практика UML. Создание wireframe12 - Практика UML. Создание wireframe
12 - Практика UML. Создание wireframe
 
02 - Практика UML. Уровни приложения
02 - Практика UML. Уровни приложения02 - Практика UML. Уровни приложения
02 - Практика UML. Уровни приложения
 
13 - Практика UML. Переход к разработке
13 - Практика UML. Переход к разработке13 - Практика UML. Переход к разработке
13 - Практика UML. Переход к разработке
 

Similaire à Лекция 11. Тестирование.

C++ CoreHard Autumn 2018. Заглядываем под капот «Поясов по C++» - Илья Шишков
C++ CoreHard Autumn 2018. Заглядываем под капот «Поясов по C++» - Илья ШишковC++ CoreHard Autumn 2018. Заглядываем под капот «Поясов по C++» - Илья Шишков
C++ CoreHard Autumn 2018. Заглядываем под капот «Поясов по C++» - Илья Шишковcorehard_by
 
C++ STL & Qt. Занятие 10.
C++ STL & Qt. Занятие 10.C++ STL & Qt. Занятие 10.
C++ STL & Qt. Занятие 10.Igor Shkulipa
 
JS Lab2017_Евгений Сафронов_Тестирование Javascript кода. Инструменты, практи...
JS Lab2017_Евгений Сафронов_Тестирование Javascript кода. Инструменты, практи...JS Lab2017_Евгений Сафронов_Тестирование Javascript кода. Инструменты, практи...
JS Lab2017_Евгений Сафронов_Тестирование Javascript кода. Инструменты, практи...GeeksLab Odessa
 
kranonit S11E01 Андрей Пономарёв: Тренинг по TDD в Java
kranonit S11E01 Андрей Пономарёв: Тренинг по TDD в Javakranonit S11E01 Андрей Пономарёв: Тренинг по TDD в Java
kranonit S11E01 Андрей Пономарёв: Тренинг по TDD в JavaKrivoy Rog IT Community
 
TDD или как я стараюсь писать код
TDD или как я стараюсь писать кодTDD или как я стараюсь писать код
TDD или как я стараюсь писать кодMoscowDjango
 
Юлия Ковалёва. Fscheck — альтернативный путь для unit тестов
Юлия Ковалёва. Fscheck — альтернативный путь для unit тестовЮлия Ковалёва. Fscheck — альтернативный путь для unit тестов
Юлия Ковалёва. Fscheck — альтернативный путь для unit тестовMskDotNet Community
 
Victor Kuliamin.CSEDays
Victor Kuliamin.CSEDaysVictor Kuliamin.CSEDays
Victor Kuliamin.CSEDaysLiloSEA
 
Автотестирование АБС. Конвейер разработки, конвейер данных, конвейер выполнения
Автотестирование АБС. Конвейер разработки, конвейер данных, конвейер выполненияАвтотестирование АБС. Конвейер разработки, конвейер данных, конвейер выполнения
Автотестирование АБС. Конвейер разработки, конвейер данных, конвейер выполненияSQALab
 
TDD или как я стараюсь писать код
TDD или как я стараюсь писать кодTDD или как я стараюсь писать код
TDD или как я стараюсь писать кодVladimir Filonov
 
Евгений Сафронов "Тестирование. точка зрения разработчика"
Евгений Сафронов "Тестирование. точка зрения разработчика"Евгений Сафронов "Тестирование. точка зрения разработчика"
Евгений Сафронов "Тестирование. точка зрения разработчика"DataArt
 
Javascript testing
Javascript testingJavascript testing
Javascript testingTCS bank
 
Как сделать ваш JavaScript быстрее
Как сделать ваш JavaScript быстрееКак сделать ваш JavaScript быстрее
Как сделать ваш JavaScript быстрееRoman Dvornov
 
Testing RIA with Selenium
Testing RIA with SeleniumTesting RIA with Selenium
Testing RIA with SeleniumSergey Shvets
 
Модели в профессиональной инженерии и тестировании программ. Александр Петрен...
Модели в профессиональной инженерии и тестировании программ. Александр Петрен...Модели в профессиональной инженерии и тестировании программ. Александр Петрен...
Модели в профессиональной инженерии и тестировании программ. Александр Петрен...yaevents
 
Как сделать ваш JavaScript быстрее / Роман Дворнов (Авито)
Как сделать ваш JavaScript быстрее / Роман Дворнов (Авито)Как сделать ваш JavaScript быстрее / Роман Дворнов (Авито)
Как сделать ваш JavaScript быстрее / Роман Дворнов (Авито)Ontico
 
Метод No-Test-Cases: избавьтесь от тест-кейсов в тестировании
Метод No-Test-Cases: избавьтесь от тест-кейсов в тестированииМетод No-Test-Cases: избавьтесь от тест-кейсов в тестировании
Метод No-Test-Cases: избавьтесь от тест-кейсов в тестированииSQALab
 
Программирование как способ выражения мыслей.
Программирование как способ выражения мыслей. Программирование как способ выражения мыслей.
Программирование как способ выражения мыслей. Levon Avakyan
 

Similaire à Лекция 11. Тестирование. (20)

C++ CoreHard Autumn 2018. Заглядываем под капот «Поясов по C++» - Илья Шишков
C++ CoreHard Autumn 2018. Заглядываем под капот «Поясов по C++» - Илья ШишковC++ CoreHard Autumn 2018. Заглядываем под капот «Поясов по C++» - Илья Шишков
C++ CoreHard Autumn 2018. Заглядываем под капот «Поясов по C++» - Илья Шишков
 
C++ STL & Qt. Занятие 10.
C++ STL & Qt. Занятие 10.C++ STL & Qt. Занятие 10.
C++ STL & Qt. Занятие 10.
 
JS Lab2017_Евгений Сафронов_Тестирование Javascript кода. Инструменты, практи...
JS Lab2017_Евгений Сафронов_Тестирование Javascript кода. Инструменты, практи...JS Lab2017_Евгений Сафронов_Тестирование Javascript кода. Инструменты, практи...
JS Lab2017_Евгений Сафронов_Тестирование Javascript кода. Инструменты, практи...
 
kranonit S11E01 Андрей Пономарёв: Тренинг по TDD в Java
kranonit S11E01 Андрей Пономарёв: Тренинг по TDD в Javakranonit S11E01 Андрей Пономарёв: Тренинг по TDD в Java
kranonit S11E01 Андрей Пономарёв: Тренинг по TDD в Java
 
2014-11-01 03 Николай Линкер. Open your clojure
2014-11-01 03 Николай Линкер. Open your clojure2014-11-01 03 Николай Линкер. Open your clojure
2014-11-01 03 Николай Линкер. Open your clojure
 
TDD или как я стараюсь писать код
TDD или как я стараюсь писать кодTDD или как я стараюсь писать код
TDD или как я стараюсь писать код
 
Юлия Ковалёва. Fscheck — альтернативный путь для unit тестов
Юлия Ковалёва. Fscheck — альтернативный путь для unit тестовЮлия Ковалёва. Fscheck — альтернативный путь для unit тестов
Юлия Ковалёва. Fscheck — альтернативный путь для unit тестов
 
Victor Kuliamin.CSEDays
Victor Kuliamin.CSEDaysVictor Kuliamin.CSEDays
Victor Kuliamin.CSEDays
 
Автотестирование АБС. Конвейер разработки, конвейер данных, конвейер выполнения
Автотестирование АБС. Конвейер разработки, конвейер данных, конвейер выполненияАвтотестирование АБС. Конвейер разработки, конвейер данных, конвейер выполнения
Автотестирование АБС. Конвейер разработки, конвейер данных, конвейер выполнения
 
TDD или как я стараюсь писать код
TDD или как я стараюсь писать кодTDD или как я стараюсь писать код
TDD или как я стараюсь писать код
 
Евгений Сафронов "Тестирование. точка зрения разработчика"
Евгений Сафронов "Тестирование. точка зрения разработчика"Евгений Сафронов "Тестирование. точка зрения разработчика"
Евгений Сафронов "Тестирование. точка зрения разработчика"
 
UI+unit testing in iOS
UI+unit testing in iOSUI+unit testing in iOS
UI+unit testing in iOS
 
Javascript testing
Javascript testingJavascript testing
Javascript testing
 
Как сделать ваш JavaScript быстрее
Как сделать ваш JavaScript быстрееКак сделать ваш JavaScript быстрее
Как сделать ваш JavaScript быстрее
 
Testing RIA with Selenium
Testing RIA with SeleniumTesting RIA with Selenium
Testing RIA with Selenium
 
Модели в профессиональной инженерии и тестировании программ. Александр Петрен...
Модели в профессиональной инженерии и тестировании программ. Александр Петрен...Модели в профессиональной инженерии и тестировании программ. Александр Петрен...
Модели в профессиональной инженерии и тестировании программ. Александр Петрен...
 
Как сделать ваш JavaScript быстрее / Роман Дворнов (Авито)
Как сделать ваш JavaScript быстрее / Роман Дворнов (Авито)Как сделать ваш JavaScript быстрее / Роман Дворнов (Авито)
Как сделать ваш JavaScript быстрее / Роман Дворнов (Авито)
 
Метод No-Test-Cases: избавьтесь от тест-кейсов в тестировании
Метод No-Test-Cases: избавьтесь от тест-кейсов в тестированииМетод No-Test-Cases: избавьтесь от тест-кейсов в тестировании
Метод No-Test-Cases: избавьтесь от тест-кейсов в тестировании
 
BDD
BDDBDD
BDD
 
Программирование как способ выражения мыслей.
Программирование как способ выражения мыслей. Программирование как способ выражения мыслей.
Программирование как способ выражения мыслей.
 

Plus de Roman Brovko

Individual task Networking
Individual task NetworkingIndividual task Networking
Individual task NetworkingRoman Brovko
 
Networking essentials lect3
Networking essentials lect3Networking essentials lect3
Networking essentials lect3Roman Brovko
 
Gl embedded starterkit_ethernet
Gl embedded starterkit_ethernetGl embedded starterkit_ethernet
Gl embedded starterkit_ethernetRoman Brovko
 
Networking essentials lect2
Networking essentials lect2Networking essentials lect2
Networking essentials lect2Roman Brovko
 
Networking essentials lect1
Networking essentials lect1Networking essentials lect1
Networking essentials lect1Roman Brovko
 
Bare metal training_07_spi_flash
Bare metal training_07_spi_flashBare metal training_07_spi_flash
Bare metal training_07_spi_flashRoman Brovko
 
Bare metal training_06_I2C
Bare metal training_06_I2CBare metal training_06_I2C
Bare metal training_06_I2CRoman Brovko
 
Bare metal training_05_uart
Bare metal training_05_uartBare metal training_05_uart
Bare metal training_05_uartRoman Brovko
 
Bare metal training_04_adc_temp_sensor
Bare metal training_04_adc_temp_sensorBare metal training_04_adc_temp_sensor
Bare metal training_04_adc_temp_sensorRoman Brovko
 
Bare metal training_03_timers_pwm
Bare metal training_03_timers_pwmBare metal training_03_timers_pwm
Bare metal training_03_timers_pwmRoman Brovko
 
Bare metal training_02_le_ds_and_buttons
Bare metal training_02_le_ds_and_buttonsBare metal training_02_le_ds_and_buttons
Bare metal training_02_le_ds_and_buttonsRoman Brovko
 
Bare metal training_01_hello_world
Bare metal training_01_hello_worldBare metal training_01_hello_world
Bare metal training_01_hello_worldRoman Brovko
 
Bare metal training_00_prerequisites
Bare metal training_00_prerequisitesBare metal training_00_prerequisites
Bare metal training_00_prerequisitesRoman Brovko
 
C language lect_23_advanced
C language lect_23_advancedC language lect_23_advanced
C language lect_23_advancedRoman Brovko
 
C language lect_22_advanced
C language lect_22_advancedC language lect_22_advanced
C language lect_22_advancedRoman Brovko
 
C language lect_21_advanced
C language lect_21_advancedC language lect_21_advanced
C language lect_21_advancedRoman Brovko
 
подготовка рабочего окружения
подготовка рабочего окруженияподготовка рабочего окружения
подготовка рабочего окруженияRoman Brovko
 
C language lect_20_advanced
C language lect_20_advancedC language lect_20_advanced
C language lect_20_advancedRoman Brovko
 
C language lect_19_basics
C language lect_19_basicsC language lect_19_basics
C language lect_19_basicsRoman Brovko
 

Plus de Roman Brovko (20)

Individual task Networking
Individual task NetworkingIndividual task Networking
Individual task Networking
 
Networking essentials lect3
Networking essentials lect3Networking essentials lect3
Networking essentials lect3
 
Gl embedded starterkit_ethernet
Gl embedded starterkit_ethernetGl embedded starterkit_ethernet
Gl embedded starterkit_ethernet
 
Networking essentials lect2
Networking essentials lect2Networking essentials lect2
Networking essentials lect2
 
Networking essentials lect1
Networking essentials lect1Networking essentials lect1
Networking essentials lect1
 
Bare metal training_07_spi_flash
Bare metal training_07_spi_flashBare metal training_07_spi_flash
Bare metal training_07_spi_flash
 
Bare metal training_06_I2C
Bare metal training_06_I2CBare metal training_06_I2C
Bare metal training_06_I2C
 
Glesk worshop
Glesk worshopGlesk worshop
Glesk worshop
 
Bare metal training_05_uart
Bare metal training_05_uartBare metal training_05_uart
Bare metal training_05_uart
 
Bare metal training_04_adc_temp_sensor
Bare metal training_04_adc_temp_sensorBare metal training_04_adc_temp_sensor
Bare metal training_04_adc_temp_sensor
 
Bare metal training_03_timers_pwm
Bare metal training_03_timers_pwmBare metal training_03_timers_pwm
Bare metal training_03_timers_pwm
 
Bare metal training_02_le_ds_and_buttons
Bare metal training_02_le_ds_and_buttonsBare metal training_02_le_ds_and_buttons
Bare metal training_02_le_ds_and_buttons
 
Bare metal training_01_hello_world
Bare metal training_01_hello_worldBare metal training_01_hello_world
Bare metal training_01_hello_world
 
Bare metal training_00_prerequisites
Bare metal training_00_prerequisitesBare metal training_00_prerequisites
Bare metal training_00_prerequisites
 
C language lect_23_advanced
C language lect_23_advancedC language lect_23_advanced
C language lect_23_advanced
 
C language lect_22_advanced
C language lect_22_advancedC language lect_22_advanced
C language lect_22_advanced
 
C language lect_21_advanced
C language lect_21_advancedC language lect_21_advanced
C language lect_21_advanced
 
подготовка рабочего окружения
подготовка рабочего окруженияподготовка рабочего окружения
подготовка рабочего окружения
 
C language lect_20_advanced
C language lect_20_advancedC language lect_20_advanced
C language lect_20_advanced
 
C language lect_19_basics
C language lect_19_basicsC language lect_19_basics
C language lect_19_basics
 

Лекция 11. Тестирование.

  • 1. Лекция 11: Тестирование Сергей Лебедев sergei.a.lebedev@gmail.com 23 ноября 2015 г.
  • 2. Тестировать или не тестировать?1 • Тестировать: • тесты проверяют корректность кода • и позволяют бесстрашно изменять код даже в больших проектах. • Не тестировать: • написание тестов требует времени, • нередко в проекте тестов больше чем кода, • работающие тесты не гарантируют корректность. • Тем не менее, ответ очевиден: конечно же тестировать! 1 Здесь и далее под тестированием имеется в виду модульное (и иногда интеграционное) тестирование aka unit testing. 1 / 35
  • 3. Пример >>> import itertools >>> def rle(iterable): ... """Applies run-length encoding to an iterable.""" ... for item, g in itertools.groupby(iterable): ... yield item, sum(1 for _ in g) ... >>> list(rle("mississippi")) [('m', 1), ('i', 1), ('s', 2), ('i', 1), ('s', 2), ('i', 1), ('p', 2), ('i', 1)] 2 / 35
  • 4. Тестирование с помощью print • Функция print позволяет организовать полуавтоматическое тестирование вне интерактивной оболочки. • Интерпретатор печатает — вы проверяете: >>> def test_rle(): ... print(list(rle("mississippi"))) ... >>> test_rle() [('m', 1), ('i', 1), ('s', 2), ('i', 1), ('s', 2), ('i', 1), ('p', 2), ('i', 1)] 3 / 35
  • 5.
  • 7. Доктесты и модуль doctest2 import doctest import itertools def rle(iterable): """Applies run-length encoding to an iterable. >>> list(rle("")) [] >>> list(rle("mississippi")) [('m', 1), ('i', 1), ('s', 2), ('i', 1), ('s', 2), ('i', 1), ('p', 2), ('i', 1)] """ for item, g in itertools.groupby(iterable): yield item, sum(1 for _ in g) if __name__ == "__main__": doctest.testmod() 2 https://docs.python.org/3/library/doctest 4 / 35
  • 8. Модуль doctest и функция rle $ python ./test_doctest.py # можно python -m doctest ***************************************************** File "test_doctest.py", line 10, in __main__.rle Failed example: list(rle("mississippi")) Expected: [('m', 1), ('i', 1), ('s', 2), ('i', 1), ('s', 2), ('i', 1), ('p', 2), ('i', 1)] Got: [('m', 1), ('i', 1), ('s', 2), ('i', 1), ...] ****************************************************** 1 items had failures: 1 of 2 in __main__.rle ***Test Failed*** 1 failures. 5 / 35
  • 9. Модуль doctest и директивы • Директивы позволяют изменить то, как doctest сравнивает ожидаемый вывод интерпретатора с фактическим. • Например, директива NORMALIZE_WHITESPACE нормализует пробельные символы перед сравнением: >>> list(rle("mississippi")) ... # doctest: +NORMALIZE_WHITESPACE [('m', 1), ('i', 1), ('s', 2), ('i', 1), ('s', 2), ('i', 1), ('p', 2), ('i', 1)] • А директива ELLIPSIS позволяет использовать символ ..., который совпадает с любой строкой: >>> list(rle("mississippi")) ... # doctest: +ELLIPSIS [('m', 1), ('i', 1), ('s', 2), ('i', 1), ...] 6 / 35
  • 10. Модуль doctest: резюме • Модуль doctest позволяет проверить реализацию функции на соответствие записанному сеансу интерпретатора. • Плюсы: • доступен в стандартной библиотеке, • решает задачу тестирования для небольших проектов, • доктесты их легко читать, • примеры кода в документации всегда актуальны. • Минусы: • доктесты требуют, чтобы у результата было содержательное строковое представление, • длинные доктесты ухудшают читаемость документации, • нет способа запустить подмножество доктестов, • если в середине доктеста произошла ошибка, оставшаяся часть не выполнится. 7 / 35
  • 12. Тестирование с помощью assert • Напоминание: • оператор assert принимает два аргумента: условие и произвольное значение, • если условие falsy, оператор поднимает исключение AssertionError. >>> assert [], 42 Traceback (most recent call last): File "<stdin>", line 1, in <module> AssertionError: 42 • Протестируем функцию rle: >>> def test_rle(): ... s = "mississippi" ... tmp = set(ch for ch, _count in rle(s)) ... assert tmp == set(s[:-1] + s[1]) ... assert not list(rle("")) ... >>> test_rle() • Вопрос: что вы думаете про такой тест? 8 / 35
  • 13. Признаки хорошего теста • Хороший тест: • корректный, • понятный читателю, • конкретный, то есть проверяет что-то одно. • Попробуем улучшить тест для функции rle: >>> def test_rle(): ... assert rle("mississippi") == [ ... ('m', 1), ('i', 1), ('s', 2), ('i', 1), ... ('s', 2), ('i', 1), ('p', 2), ('i', 1) ... ] ... >>> def test_rle_empty(): ... assert not list(rle("")) ... >>> test_rle_empty() >>> test_rle() Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 4, in test_rle AssertionError 9 / 35
  • 14. Оператор assert и сообщения об ошибках Второй аргумент оператора assert используется для сообщения об ошибке: >>> def test_rle(): ... actual = rle("mississippi") ... expected = [ ... ('m', 1), ('i', 1), ('s', 2), ('i', 1), ... ('s', 2), ('i', 1), ('p', 2), ('i', 1) ... ] ... message = "{} != {}".format(actual, expected) ... assert actual == expected, message ... >>> test_rle() Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 8, in test_rle AssertionError: <generator object rle at [...]> != [('m', 1), ('i', 1), ('s', 2), ...] 10 / 35
  • 15. Функция assert_equal • Добавим сообщение об ошибке к тесту test_rle_empty: def test_rle_empty(): # ^C/^V actual = list(rle("")) expected = [] message = "{} != {}".format(actual, expected) assert actual == expected, message • Самое время вынести логику сравнения в отдельную функцию: def assert_equal(x, y): assert x == y, "{} != {}".format(x, y) • Вопрос: что делать, если мы также хотим проверять утверждения вида ("a", 2) in rle(...)? 11 / 35
  • 16. assert: резюме • Оператор assert можно использовать для написания тестов. • Плюсы: • тесты c assert легко читать, • они не используют ничего кроме стандартных средств языка, • в отличие от доктестов это обычные функции. • Минусы: • запускать тесты нужно вручную, • их сложно отлаживать, потому что • для каждого типа утверждения приходится самостоятельно конструировать сообщение об ошибке. 12 / 35
  • 18. Модуль unittest3 • Модуль unittest реализует функциональность JUnit для тестирования кода на Python. • Наследие Java до сих пор в обилии присутствует в API. • Перепишем имеющиеся тесты с использованием unittest: import unittest class TestHomework(unittest.TestCase): def test_rle(self): self.assertEqual(rle("mississippi"), [...]) def test_rle_empty(self): self.assertEqual(list(rle("")), []) if __name__ == "__main__": unittest.main() 3 https://docs.python.org/3/library/unittest 13 / 35
  • 19. Модуль unittest и функция rle $ python ./test_homework.py F. ========================================================= FAIL: test_rle (__main__.TestHomework) --------------------------------------------------------- Traceback (most recent call last): File "./test_homework.py", line 12, in test_rle self.assertEqual(rle("mississippi"), expected) AssertionError: <generator object rle at [...]> != [('m', 1), ('i', 1), ('s', 2), ...] --------------------------------------------------------- Ran 2 tests in 0.001s FAILED (failures=1) 14 / 35
  • 20. Функция unittest.main • Функция unittest.main загружает все тесты текущего модуля и запускает их. • Тест — метод экземпляра unittest.TestCase, начинающийся на test. • При необходимости тесты можно объединять в группы с помощью класса unittest.TestSuite: suite = unittest.TestSuite([ TestHomework(), TestSomethingElse() ]) • Указывать вручную, что нужно запустить довольно досадно. • Вопрос: можно ли лучше? 15 / 35
  • 21. Автоматический поиск тестов # V в текущей директории $ python -m unittest . F. ========================================================= FAIL: test_rle (test_homework.TestHomework) --------------------------------------------------------- Traceback (most recent call last): File "./test_homework.py", line 12, in test_rle self.assertEqual(rle("mississippi"), expected) AssertionError: <generator object rle at [...]> != [('m', 1), ('i', 1), ('s', 2), ...] --------------------------------------------------------- Ran 2 tests in 0.001s FAILED (failures=1) 16 / 35
  • 22. Методы unittest.TestCase assertEqual(a, b) a == b assertNotEqual(a, b) a != b assertTrue(x) bool(x) is True assertFalse(x) bool(x) is False assertIs(a, b) a is b assertIsNot(a, b) a is not b assertIsNone(x) x is None assertIsNotNone(x) x is not None assertIn(a, b) a in b assertNotIn(a, b) a not in b assertIsInstance(a, b) isinstance(a, b) assertNotIsInstance(a, b) not isinstance(a, b) assertRaises(exc_type) # ? 17 / 35
  • 23. Модуль unittest и контекст • ? aka fixtures — способ подготовить контекст, в котором будут запускаться тесты. • Это можно использовать, например, для работы с ресурсами: сокетами, файлами, временными директориями. • Пример: class TestHomeworkWithOracle(unittest.TestCase): def setUp(self): self.oracle = RleOracle("http://oracle.rle.com") def test_rle_against_oracle(self): s = "mississippi" self.assertEqual(list(rle(s)), self.oracle(s)) def tearDown(self): self.oracule.close() 18 / 35
  • 24. Модуль unittest: резюме • Модуль unittest — клон JUnit для Python. • Мы обсудили • основные сущности unittest, • как писать и запускать тесты, • какие полезные методы имеются в классе unittest.TestCase. • Плюсы: • доступен в стандартной библиотеке, • выводит понятные сообщения об ошибках, • умеет автоматически находить тесты. • Минусы: • API унаследован от Java, • заставляет писать много лишнего кода, • читать unittest тесты сложнее, чем доктесты и тесты, использующие assert. 19 / 35
  • 26. Пакет py.test4 • Пакет py.test — популярная альтернатива unittest для написания и запуска тестов. • Отличительная особенность py.test — практически полное отсутствие API: тесты можно писать, используя стандартные средства языка. def test_rle(): assert rle("mississippi") == [ ('m', 1), ('i', 1), ('s', 2), ('i', 1), ('s', 2), ('i', 1), ('p', 2), ('i', 1) ] def test_rle_empty(): assert not list(rle("")) 4 https://pytest.org 20 / 35
  • 27.
  • 28. Пакет py.test и функция rle $ python -m pytest -q test_pytest.py F. ========================= FAILURES ====================== _________________________ test_rle ______________________ def test_rle(): > assert rle("mississippi") == [ ('m', 1), ('i', 1), ('s', 2), ('i', 1), ('s', 2), ('i', 1), ('p', 2), ('i', 1) ] E assert <generator ...> == [('m', 1), ..., ('i', 1)] E Full diff: E - <generator object rle at [...]> E + [('m', 1), E + ('i', 1), E ... E + ('i', 1)] test_pytest.py:5: AssertionError 21 / 35
  • 29. Пакет py.test: интерфейс командной строки • Запустить py.test можно 1001 способом, например: • найти тесты в текущей директории и во всех вложенных директориях и запустить их: $ python -m pytest • найти и запустить тесты в указанном файле: $ python -m pytest test_pytest.py • запустить один тест в файле по имени: $ python -m pytest test_pytest.py::test_rle • Что такое тест для py.test5? • функция test_*, • метод test_* в классе Test* или в классе, наследующемся от unittest.TestCase, • доктест, если py.test был запущен с параметром --doctest-modules. 5 https://pytest.org/latest/goodpractises.html 22 / 35
  • 30. Возможности py.test: интроспекция FF ========================= FAILURES ====================== _________________________ test_in _______________________ def test_in(): > assert 42 in range(0, 10) E assert 42 in range(0, 10) test_pytest_introspection.py:3: AssertionError ______________________ test_in_range_ ___________________ def test_in_range(): x = 42 > assert x > 10 and x < 15 E assert (42 > 10 and 42 < 15) test_pytest_introspection.py:8: AssertionError 23 / 35
  • 31. Возможности py.test: raises def test_undo_dict_exceptions(): d = UndoDict() with pytest.raises(KeyError): d["foo"] F ========================= FAILURES ====================== __________________ test_undo_dict_exceptions ____________ def test_undo_dict_exceptions(): d = UndoDict() with pytest.raises(KeyError): > d["foo"] E Failed: DID NOT RAISE test_assert_raises.py:15: Failed 24 / 35
  • 32. Возможности py.test: параметрические тесты def cut_suffix(s, suffix): return s[:s.rfind(suffix)] .F. ========================= FAILURES ====================== ___________ test_cut_suffix[foobar-boo-foobar] __________ @pytest.mark.parametrize("s,suffix,expected", [ ("foobar", "bar", "foo"), ("foobar", "boo", "foobar"), ("foobarbar", "bar", "foobar") ]) def test_cut_suffix(s, suffix, expected): > assert cut_suffix(s, suffix) == expected E assert 'fooba' == 'foobar' E - fooba E + foobar E ? + test_parametric_tests.py:14: AssertionError 25 / 35
  • 33. Возможности py.test: контекст • py.test реализует аналоги TestCase.setUp и TestCase.tearDown, • но это не самая интересная его возможность. • Логику подготовки контекста конкретного типа можно абстрагировать @pytest.yield_fixture def oracle(request): oracle = RleOracle("http://oracle.rle.com") yield oracle oracle.close() • чтобы потом неявно использовать в тестах: def test_rle_against_oracle(oracle): s = "mississippi" assert list(rle(s)) == oracle(s) 26 / 35
  • 34. Возможности py.test: встроенные контексты import io import gzip import sys def test_ook_eval(capsys, monkeypatch): handle = io.StringIO("!") monkeypatch.setattr(sys, "stdin", handle) ook_eval("Ook. Ook! Ook! Ook.") output, _err = capsys.readouterr() assert output == "!" def test_reader(tmpdir): path = tmpdir.join("example.gz") path.write("") assert isinstance(reader(str(path)), gzip.GzipFile) 27 / 35
  • 35. Пакет py.test: резюме • Пакет py.test — швейцарский нож тестирования в мире Python. • Плюсы: • практически нет API, тесты — обычные функции, • удобный вывод, • удобный механизм параметризации тестов, • приспособления, которые можно переиспользовать, • множество встроенных возможностей и впечатляющее количество расширений6 . • Минусы: • магия-магия-магия, • может быть сложнее для понимания, если вы привыкли к JUnit. 6 https://pytest.org/latest/plugins_index 28 / 35
  • 37. Тестирование свойств • До текущего момента мы обсуждали тесты, которые проверяют тривиальные свойства кода: • равенство ожидаемому значению, • вхождение результата в коллекцию и так далее. • Иногда можно формулировать и проверять менее тривиальные свойства, например: • если sorted возвращает список, отсортированный по неубыванию, • то для любого списка xs и индексов i < j справедливо, что sorted(xs)[i] <= sorted(xs)[j]. • Вопрос: как проверить это свойство? 29 / 35
  • 38. Пример: test_sort import random def random_array(): size = random.randint(0, 1024) return [random.randint(-42, 42) for _ in range(size)] def test_sort(): xs = random_array() result = sorted(xs) assert all(xi <= xj for xi, xj in zip(result, result[1:])) Такой подход сработает, но • писать генераторы самостоятельно долго и бессмысленно, • на практике нас, как правило, интересует минимальный контрпример. 30 / 35
  • 39. Пакет hypothesis7 • Пакет hypothesis реализует API для формулирования и проверки свойств. • Перепишем test_sort с использованием hypothesis: import hypothesis.strategies as st from hypothesis import given @given(st.lists(st.integers())) def test_sort(xs): result = sorted(xs) assert all(xi <= xj for xi, xj in zip(result, result[1:])) • И попробуем обмануть его: def sorted(xs, f=sorted): return xs if len(xs) == 8 else f(xs) 7 https://hypothesis.readthedocs.org 31 / 35
  • 40. Пакет hypothesis и функция sorted F ========================= FAILURES ====================== ________________________ test_sort ______________________ assert all(xi <= xj E assert all(<generator object <genexpr> at 0x103b81e10>) ----------------------- Hypothesis ---------------------- Falsifying example: test_sort(xs=[0, 0, 0, 1, 0, 0, 0]) 32 / 35
  • 41. Пакет hypothesis: что можно генерировать? • Примитивные типы: st.just(x) ==> x, x, x st.none() ==> None, None, None st.one_of(a, b, c) ==> a, a, b, c, a st.booleans() ==> True, False, True st.integers() ==> 1, -10, 2, 42 st.floats() ==> math.pi, 42.42 • Строки и байты: st.text() ==> "abra", "cadabra" st.binary() ==> b"xffxef", b"ascii" • Коллекции: st.sampled_from(iterable) st.tuples(st.foo(), st.bar(), st.boo()) st.lists(st.foo()) st.sets(st.foo()) st.dictionaries(st.foo(), st.bar()) 33 / 35
  • 42. Пакет hypothesis и функция rle from itertools import chain, repeat, tee import hypothesis.strategies as st from hypothesis import given iterables = st.one_of(st.tuples(st.integers(0, 10)), st.lists(st.integers(0, 10)), st.text().map(iter)) @given(iterables) def test_rle(it): def encode_decode(it): return chain.from_iterable( repeat(item, count) for item, count in rle(it)) it, copy = tee(it) expected = list(copy) assert list(encode_decode(it)) == expected 34 / 35
  • 43. Пакет hypothesis: резюме • Пакет hypothesis позволяет удобно формулировать и проверять свойства про Python код. • Мы обсудили, что можно генерировать, и рассмотрели несколько примеров. • Львиная часть функциональности hypothesis осталась за кадром: • как сузить пространство поиска контпримеров? • как написать свой генератор? • как формулировать зависимые гипотезы? 35 / 35