Save 37% off PRO during our Black Friday Sale! »

Evidence

 Evidence

Tech talk on rannts#12 (Aug 12 2016) about problems, related to storing of test results.

A8d8ca813a744866b9f85ea1cefb5813?s=128

Sergey Arkhipov

August 13, 2016
Tweet

Transcript

  1. Вещественные доказательства Сергей Архипов 2016 

  2. Что такое «плохой» коммит?

  3. None
  4. None
  5. None
  6. None
  7. None
  8. None
  9. Метаинформация ― Автор; ― Коммиттер;

  10. Метаинформация ― Автор; ― Коммиттер; ― Основной тикет в багтрекере;

    ― Релевантные тикеты в багтрекере;
  11. Метаинформация ― Автор; ― Коммиттер; ― Основной тикет в багтрекере;

    ― Релевантные тикеты в багтрекере; ― Результаты юнит-тестирования; ― Результаты функционального тестирования; ― Результаты статического анализа;
  12. Метаинформация ― Автор; ― Коммиттер; ― Основной тикет в багтрекере;

    ― Релевантные тикеты в багтрекере; ― Результаты юнит-тестирования; ― Результаты функционального тестирования; ― Результаты статического анализа; ― Кто ревьюил код и что сказал; ― Бэкпортированное ли изменение;
  13. Метаинформация ― Автор; ― Коммиттер; ― Основной тикет в багтрекере;

    ― Релевантные тикеты в багтрекере; ― Результаты юнит-тестирования; ― Результаты функционального тестирования; ― Результаты статического анализа; ― Кто ревьюил код и что сказал; ― Бэкпортированное ли изменение; ― Версия софта, где изменения, вызванные коммитом есть as is; ― Версия софта, где кто-то подправил изменения этого коммита.
  14. «Плохой» коммит с точки зрения тестирования

  15. Плохой коммит ― Процент прошедших тестов; ― Покрытие кода тестами;

    ― Результаты статического анализа; ― Результаты измерения цикломатической сложности; ― Результаты соответствия стилю кодирования.
  16. Плохой коммит ― Как формировать результат прохождения тестов; ― Как

    хранить результаты.
  17. Как формировать результаты

  18. tests/api/test_pagination.py::test_sort_by_fail[value0] PASSED tests/api/test_pagination.py::test_sort_by_fail[value1] PASSED tests/api/test_pagination.py::test_sort_by_fail[] PASSED tests/api/test_pagination.py::test_sort_by_fail[1] PASSED tests/api/test_pagination.py::test_sort_by_fail[11] PASSED

    tests/api/test_pagination.py::test_sort_by_fail[value5] PASSED tests/api/test_pagination.py::test_sort_by_fail[value6] PASSED tests/api/test_pagination.py::test_sort_by_fail[value7] PASSED tests/api/test_pagination.py::test_sort_by_fail[value8] PASSED tests/api/test_pagination.py::test_sort_by_fail[value9] PASSED tests/api/test_pagination.py::test_sort_by_fail[value10] PASSED tests/api/test_pagination.py::test_sort_by_fail[value11] PASSED tests/api/test_pagination.py::test_sort_by_fail[value12] PASSED tests/api/test_pagination.py::test_sort_by_ok PASSED tests/api/test_validators.py::test_require_schema_positive_integer_fail[- 1] PASSED tests/api/test_validators.py::test_require_schema_positive_integer_fail[1] PASSED tests/api/test_validators.py::test_require_schema_positive_integer_fail[] PASSED tests/api/test_validators.py::test_require_schema_positive_integer_fail[va lue3] PASSED tests/api/test_validators.py::test_require_schema_positive_integer_fail[va lue4] PASSED tests/api/test_validators.py::test_require_schema_positive_integer_fail[No ne] PASSED
  19. <testsuite errors="0" failures="0" name="pytest" skips="0" tests="32" time="0.412"> <testcase classname="tests.api.views.v1.test_api_auth" file="tests/api/views/v1/test_api_auth.py"

    line="23" name="test_incorrect_login_data[None-text/html]" time="0.01604437828063965"> <system-err>2016-08-07 16:44:37 [ERROR ] ( generic.py:50 ): &lt;201d686e-ae96-4b8d-ae5f- f66b522b23fe&gt; | Cannot process user request: 400: Bad Request </system-err> </testcase> </testsuite>
  20. Subunit (rtcollins) ― https://github.com/testing-cabal/subunit ― Протокол предоставления тестовых результатов; ―

    Представляет собой стрим, не конечный результат; ― Позволяет прикреплять аттачи к тестам; ― Мультиплексированный поток.
  21. import os import subprocess import unittest class MyTestCase(unittest.TestCase): def test_ls_command(self):

    process = subprocess.Popen( "slq", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) process.wait() self.assertEqual( process.returncode, os.EX_OK)
  22. =========================================================== FAIL: test_ls_command (testcase.MyTestCase) ----------------------------------------------------------- Traceback (most recent call last):

    File "testcase.py", line 19, in test_ls_command process.returncode, os.EX_OK) AssertionError: 127 != 0 ----------------------------------------------------------- Ran 1 test in 0.002s FAILED (failures=1)
  23. import os import subprocess import testtools import testtools.content class MyTestCase(testtools.TestCase):

    def test_ls_command(self): process = subprocess.Popen( "slq", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) process.wait() self.addDetail( "ls-stdout", testtools.content.text_content(process.stdout.read())) self.addDetail( "ls-stderr", testtools.content.text_content(process.stderr.read())) self.assertEqual( process.returncode, os.EX_OK)
  24. ====================================================================== FAIL: test_ls_command (testcase.MyTestCase) testcase.MyTestCase.test_ls_command ---------------------------------------------------------------------- _StringException: Empty attachments: ls-stdout

    ls-stderr: {{{/bin/sh: 1: slq: not found}}} Traceback (most recent call last): File "testcase.py", line 28, in test_ls_command process.returncode, os.EX_OK) File "/home/nineseconds/.pyenv/versions/evidence/lib/python2.7/site- packages/testtools/testcase.py", line 411, in assertEqual self.assertThat(observed, matcher, message) File "/home/nineseconds/.pyenv/versions/evidence/lib/python2.7/site- packages/testtools/testcase.py", line 498, in assertThat raise mismatch_error testtools.matchers._impl.MismatchError: 127 != 0 ---------------------------------------------------------------------- Ran 1 test in 0.004s FAILED (failures=1)
  25. ➜ python -m subunit.run testcase.py �),#testcase.MyTestCase.test_ls_command +3W D `#testcase.MyTe ����

    � ��� stCase.test_ls_commandI +p@[W D r:0#testcase.MyTestCase.test_l ���� � �� s_commandtext/plain; charset="utf8" ls- stdout U +p@vW D r:0#testcase.MyTestCase.test_ls_commandtext/pla �� � � �� in; charset="utf8" ls-stderr/bin/sh: 1: slq: not found �` +`@ W D r:0#testcase.MyTestCase.test_ls_command3text/x- �� � � �� traceback; charset="utf8"; language="python" traceback#Traceback (most recent call last): !~ A +`@ W D r:0#testcase.MyTestCase.test_ls_command3text/x- � � � � �� traceback; charset="utf8"; language="python" traceback@T File "testcase.py", line 28, in test_ls_command process.returncode, os.EX_OK)
  26. ➜ python -m subunit.run testcase.py | subunit2pyunit testcase.MyTestCase.test_ls_command testcase.MyTestCase.test_ls_command ...

    FAIL ====================================================================== FAIL: testcase.MyTestCase.test_ls_command testcase.MyTestCase.test_ls_command ---------------------------------------------------------------------- _StringException: Empty attachments: ls-stdout ls-stderr: {{{/bin/sh: 1: slq: not found}}} Traceback (most recent call last): File "testcase.py", line 28, in test_ls_command process.returncode, os.EX_OK) File "/home/nineseconds/dev/experiments/evidence/.v/local/lib/python2.7/site- packages/testtools/testcase.py", line 411, in assertEqual self.assertThat(observed, matcher, message) File "/home/nineseconds/dev/experiments/evidence/.v/local/lib/python2.7/site- packages/testtools/testcase.py", line 498, in assertThat raise mismatch_error testtools.matchers._impl.MismatchError: 127 != 0 ---------------------------------------------------------------------- Ran 1 test in 0.001s FAILED (failures=1)
  27. ➜ python -m subunit.run testcase.py | subunit2junitxml <testsuite errors="0" failures="1"

    name="" tests="1" time="0.003"> <testcase classname="testcase.MyTestCase" name="test_ls_command" time="0.003"> <failure type="testtools.testresult.real._StringException">Traceback (most recent call last): testtools.testresult.real._StringException: Empty attachments: ls-stdout ls-stderr: {{{/bin/sh: 1: slq: not found}}} Traceback (most recent call last): File "testcase.py", line 28, in test_ls_command process.returncode, os.EX_OK) File "/home/nineseconds/dev/experiments/evidence/.v/local/lib/python2.7/site- packages/testtools/testcase.py", line 411, in assertEqual self.assertThat(observed, matcher, message) File "/home/nineseconds/dev/experiments/evidence/.v/local/lib/python2.7/site- packages/testtools/testcase.py", line 498, in assertThat raise mismatch_error testtools.matchers._impl.MismatchError: 127 != 0 </failure> </testcase> </testsuite>
  28. Как хранить результаты

  29. testrepository (rtcollins) ― https://github.com/testing-cabal/testreposit ory ― Представляет репозиторий как вход

    для теста; ― Хранит результаты предыдущих запусков тестов в отдельной директории; ― Дает возможность посмотреть статистику, единообразно запустить тесты.
  30. git-test ― git-test хранит результаты в самом Git; ― Хранит

    метаинформацию о тестовом запуске. Понимает про то, что запуски могут быть разными; ― Результат тестового прогона можно добавить когда угодно; ― Трекает не поваленные тесты, а результаты тестирования на определенном коммите.
  31. .gittest { "default": { "command": "tox -e py27" }, "tox":

    { "command": "tox" } }
  32. git test list tox # --- # tox # ---

    2016-07-06T16:21:15.930000 00999c29c0006d28964ed048c371a954ad5c9d30 passed 2016-07-07T20:54:58.690000 039c4364c5b45a6f27f6aa97de4b1a3a8e9e4a1f passed 2016-07-07T20:20:35.290000 5d2dfd6a8dfdcd42145245c80348677a8150caab passed 2016-07-06T21:46:54.060000 607efc68cc081e304ded888f9405a9a5f0e2594a failed 2016-07-06T18:16:59.890000 80b35b26729075c71de748810edd57ea86c850a8 passed 2016-07-06T22:00:59.860000 a66e31ad4b3780f14c3748bd9c33eb38ebc85373 passed 2016-07-06T10:51:21.210000 e6f5819d879c1c50d12fa919e13ef72c835d37a9 passed
  33. git test show # ------- # default # ------- Test

    command: tox -e py27 Exit code: 0 Start at: Thu Jul 7 20:54:52 2016 (1467914092.94) Finish at: Thu Jul 7 20:54:58 2016 (1467914098.68) Duration: 5 seconds Hostname: home-laptop User: nineseconds Environment: HISTTIMEFORMAT: %d.%m.%y %H:%M:%S LESS: -R … SHLVL: 2 PWD: /home/nineseconds/dev/pvt/git-test py27 recreate: /home/nineseconds/dev/pvt/git-test/.tox/py27 py27 installdeps: -r/home/nineseconds/dev/pvt/git-test/requirements.txt, -r/home/nineseconds/dev/pvt/git-test/test-requirements.txt py27 installed: colorama==0.3.7,coverage==4.1,flake8==2.6.2,flake8- coding==1.2.2,funcsigs==1.0.2,hacking==0.11.0,mando==0.3.3,mccabe==0.5.0,mock==2.0.0, pbr==1.10.0,pep8==1.5.7,py==1.4.31,pycodestyle==2.0.0,pyflakes==1.2.3,pytest==2.9.2,p ytest-cov==2.3.0,PyYAML==3.11,radon==1.4.0,requests==2.10.0,six==1.10.0,xenon==0.4.0 py27 runtests: PYTHONHASHSEED='0' py27 runtests: commands[0] | py.test --basetemp=/home/nineseconds/dev/pvt/git- test/.tox/py27/tmp -v ============================= test session starts ==============================
  34. git-test ― git-test хранит результаты рядом с коммитами, в git-notes;

    не требуется создания новых коммитов; ― Всегда известно, кто и при каких условиях протестировал коммит. Можно хранить subunit-стрим. ― Тестраны добавляются постфактум. ― Сделан одним скриптом без внешних зависимостей.
  35. Как обрабатывать результаты

  36. None
  37. @9seconds    https:/ /speakerdeck.com/9seconds/evidence