Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Evidence

 Evidence

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

Sergey Arkhipov

August 13, 2016
Tweet

More Decks by Sergey Arkhipov

Other Decks in Programming

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 [email protected][W D r:0#testcase.MyTestCase.test_l ���� � �� s_commandtext/plain; charset="utf8" ls- stdout U [email protected] 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" [email protected] 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