├── .editorconfig ├── .gitignore ├── CODEOWNERS ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── Makefile ├── README.md ├── requirements.txt └── src ├── _static └── .gitkeep ├── annotations.rst ├── assertions.rst ├── bibliography.rst ├── code-coverage-analysis.rst ├── conf.py ├── configuration.rst ├── copyright.rst ├── database.rst ├── extending-phpunit.rst ├── fixtures.rst ├── incomplete-and-skipped-tests.rst ├── index.rst ├── installation.rst ├── logging.rst ├── organizing-tests.rst ├── risky-tests.rst ├── test-doubles.rst ├── textui.rst └── writing-tests-for-phpunit.rst /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # http://editorconfig.org 4 | 5 | root = true 6 | 7 | [*] 8 | charset = utf-8 9 | end_of_line = lf 10 | indent_style = space 11 | indent_size = 2 12 | insert_final_newline = true 13 | trim_trailing_whitespace = true 14 | 15 | [*.md] 16 | trim_trailing_whitespace = false 17 | 18 | [*.php] 19 | indent_size = 4 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea 2 | /build 3 | /node_modules 4 | /package-lock.json 5 | -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @lex111 2 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Code of Conduct 2 | 3 | As contributors and maintainers of this project, and in the interest of fostering an open and welcoming community, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities. 4 | 5 | We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, religion, or nationality. 6 | 7 | Examples of unacceptable behavior by participants include: 8 | 9 | * The use of sexualized language or imagery 10 | * Personal attacks 11 | * Trolling or insulting/derogatory comments 12 | * Public or private harassment 13 | * Publishing other's private information, such as physical or electronic 14 | addresses, without explicit permission 15 | * Other unethical or unprofessional conduct 16 | 17 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 18 | 19 | By adopting this Code of Conduct, project maintainers commit themselves to fairly and consistently applying these principles to every aspect of managing this project. Project maintainers who do not follow or enforce the Code of Conduct may be permanently removed from the project team. 20 | 21 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. 22 | 23 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project maintainer at sebastian@phpunit.de. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. Maintainers are obligated to maintain confidentiality with regard to the reporter of an incident. 24 | 25 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.3.0, available at [https://contributor-covenant.org/version/1/3/0/][version] 26 | 27 | [homepage]: https://contributor-covenant.org 28 | [version]: https://contributor-covenant.org/version/1/3/0/ 29 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to the PHPUnit Documentation 2 | 3 | ## Contributor Code of Conduct 4 | 5 | Please note that this project is released with a 6 | [Contributor Code of Conduct](CODE_OF_CONDUCT.md). By participating in this 7 | project you agree to abide by its terms. 8 | 9 | ## Workflow 10 | 11 | * Fork the project. 12 | * Make your changes. 13 | * Run `make html` to make sure that the documentation can be built and looks as 14 | expected 15 | * Send a pull request. Bonus points for topic branches. 16 | 17 | Please make sure that you have 18 | [set up your user name and email address](https://git-scm.com/book/en/v2/Getting-Started-First-Time-Git-Setup) 19 | for use with Git. Strings such as `silly nick name ` look really 20 | stupid in the commit history of a project. 21 | 22 | Pull requests for changes relating to a specific PHPUnit version of the 23 | documentation must be based on that version's branch whereas changes for new 24 | versions of PHPUnit must be based on the `master` branch. 25 | 26 | Due to time constraints, we are not always able to respond as quickly as we 27 | would like. Please do not take delays personal and feel free to remind us if 28 | you feel that we forgot to respond. 29 | 30 | ## Reporting issues 31 | 32 | Please use the issue tracker of the affected language to create new tickets. 33 | General issues should always be reported in the issue tracker of the English 34 | documentation. 35 | 36 | * [English Documentation](https://github.com/sebastianbergmann/phpunit-documentation-english/issues) 37 | * [Spanish Documentation](https://github.com/sebastianbergmann/phpunit-documentation-spanish/issues) 38 | * [French Documentation](https://github.com/sebastianbergmann/phpunit-documentation-french/issues) 39 | * [Brazilian Portuguese Documentation](https://github.com/sebastianbergmann/phpunit-documentation-brazilian-portuguese/issues) 40 | * [Japanese Documentation](https://github.com/sebastianbergmann/phpunit-documentation-japanese/issues) 41 | * [Simplified Chinese Documentation](https://github.com/sebastianbergmann/phpunit-documentation-chinese/issues) 42 | 43 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | PAPER = 8 | BUILDDIR = build 9 | 10 | # User-friendly check for sphinx-build 11 | ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) 12 | $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) 13 | endif 14 | 15 | # Internal variables. 16 | PAPEROPT_a4 = -D latex_paper_size=a4 17 | PAPEROPT_letter = -D latex_paper_size=letter 18 | ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) src 19 | # the i18n builder cannot share the environment and doctrees with the others 20 | I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) src 21 | 22 | .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest coverage gettext 23 | 24 | help: 25 | @echo "Please use \`make ' where is one of" 26 | @echo " html to make standalone HTML files" 27 | @echo " dirhtml to make HTML files named index.html in directories" 28 | @echo " singlehtml to make a single large HTML file" 29 | @echo " pickle to make pickle files" 30 | @echo " json to make JSON files" 31 | @echo " htmlhelp to make HTML files and a HTML help project" 32 | @echo " qthelp to make HTML files and a qthelp project" 33 | @echo " applehelp to make an Apple Help Book" 34 | @echo " devhelp to make HTML files and a Devhelp project" 35 | @echo " epub to make an epub" 36 | @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" 37 | @echo " latexpdf to make LaTeX files and run them through pdflatex" 38 | @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" 39 | @echo " text to make text files" 40 | @echo " man to make manual pages" 41 | @echo " texinfo to make Texinfo files" 42 | @echo " info to make Texinfo files and run them through makeinfo" 43 | @echo " gettext to make PO message catalogs" 44 | @echo " changes to make an overview of all changed/added/deprecated items" 45 | @echo " xml to make Docutils-native XML files" 46 | @echo " pseudoxml to make pseudoxml-XML files for display purposes" 47 | @echo " linkcheck to check all external links for integrity" 48 | @echo " doctest to run all doctests embedded in the documentation (if enabled)" 49 | @echo " coverage to run coverage check of the documentation (if enabled)" 50 | 51 | clean: 52 | rm -rf $(BUILDDIR)/* 53 | 54 | html: 55 | $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html 56 | @echo 57 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." 58 | 59 | dirhtml: 60 | $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml 61 | @echo 62 | @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." 63 | 64 | singlehtml: 65 | $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml 66 | @echo 67 | @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." 68 | 69 | pickle: 70 | $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle 71 | @echo 72 | @echo "Build finished; now you can process the pickle files." 73 | 74 | json: 75 | $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json 76 | @echo 77 | @echo "Build finished; now you can process the JSON files." 78 | 79 | htmlhelp: 80 | $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp 81 | @echo 82 | @echo "Build finished; now you can run HTML Help Workshop with the" \ 83 | ".hhp project file in $(BUILDDIR)/htmlhelp." 84 | 85 | qthelp: 86 | $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp 87 | @echo 88 | @echo "Build finished; now you can run "qcollectiongenerator" with the" \ 89 | ".qhcp project file in $(BUILDDIR)/qthelp, like this:" 90 | @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/PHPUnit.qhcp" 91 | @echo "To view the help file:" 92 | @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/PHPUnit.qhc" 93 | 94 | applehelp: 95 | $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp 96 | @echo 97 | @echo "Build finished. The help book is in $(BUILDDIR)/applehelp." 98 | @echo "N.B. You won't be able to view it unless you put it in" \ 99 | "~/Library/Documentation/Help or install it in your application" \ 100 | "bundle." 101 | 102 | devhelp: 103 | $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp 104 | @echo 105 | @echo "Build finished." 106 | @echo "To view the help file:" 107 | @echo "# mkdir -p $$HOME/.local/share/devhelp/PHPUnit" 108 | @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/PHPUnit" 109 | @echo "# devhelp" 110 | 111 | epub: 112 | $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub 113 | @echo 114 | @echo "Build finished. The epub file is in $(BUILDDIR)/epub." 115 | 116 | latex: 117 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 118 | @echo 119 | @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." 120 | @echo "Run \`make' in that directory to run these through (pdf)latex" \ 121 | "(use \`make latexpdf' here to do that automatically)." 122 | 123 | latexpdf: 124 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 125 | @echo "Running LaTeX files through pdflatex..." 126 | $(MAKE) -C $(BUILDDIR)/latex all-pdf 127 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 128 | 129 | latexpdfja: 130 | $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex 131 | @echo "Running LaTeX files through platex and dvipdfmx..." 132 | $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja 133 | @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." 134 | 135 | text: 136 | $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text 137 | @echo 138 | @echo "Build finished. The text files are in $(BUILDDIR)/text." 139 | 140 | man: 141 | $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man 142 | @echo 143 | @echo "Build finished. The manual pages are in $(BUILDDIR)/man." 144 | 145 | texinfo: 146 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 147 | @echo 148 | @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." 149 | @echo "Run \`make' in that directory to run these through makeinfo" \ 150 | "(use \`make info' here to do that automatically)." 151 | 152 | info: 153 | $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo 154 | @echo "Running Texinfo files through makeinfo..." 155 | make -C $(BUILDDIR)/texinfo info 156 | @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." 157 | 158 | gettext: 159 | $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale 160 | @echo 161 | @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." 162 | 163 | changes: 164 | $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes 165 | @echo 166 | @echo "The overview file is in $(BUILDDIR)/changes." 167 | 168 | linkcheck: 169 | $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck 170 | @echo 171 | @echo "Link check complete; look for any errors in the above output " \ 172 | "or in $(BUILDDIR)/linkcheck/output.txt." 173 | 174 | doctest: 175 | $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest 176 | @echo "Testing of doctests in the sources finished, look at the " \ 177 | "results in $(BUILDDIR)/doctest/output.txt." 178 | 179 | coverage: 180 | $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage 181 | @echo "Testing of coverage in the sources finished, look at the " \ 182 | "results in $(BUILDDIR)/coverage/python.txt." 183 | 184 | xml: 185 | $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml 186 | @echo 187 | @echo "Build finished. The XML files are in $(BUILDDIR)/xml." 188 | 189 | pseudoxml: 190 | $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml 191 | @echo 192 | @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." 193 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | **The PHPUnit project is no longer involved with the translation of its documentation to other languages. This repository is now archived.** 2 | 3 | # Документация PHPUnit на русском языке 4 | 5 | > **(!)** Поддерживается только перевод последней версии из ветки [master](https://github.com/sebastianbergmann/phpunit-documentation-english/tree/master). 6 | 7 | Прямая ссылка на перевод — https://phpunit.readthedocs.io/ru/latest/ 8 | 9 | ## Переводы 10 | 11 | Каждый перевод документации поддерживается в отдельном репозитории: 12 | 13 | * [Документация на английском языке](https://github.com/sebastianbergmann/phpunit-documentation-english) 14 | * [Документация на испанском языке](https://github.com/sebastianbergmann/phpunit-documentation-spanish) 15 | * [Документация на французском языке](https://github.com/sebastianbergmann/phpunit-documentation-french) 16 | * [Документация на португальском языке](https://github.com/sebastianbergmann/phpunit-documentation-brazilian-portuguese) 17 | * [Документация на японском языке](https://github.com/sebastianbergmann/phpunit-documentation-japanese) 18 | * [Документация на китайском упрощённом языке](https://github.com/sebastianbergmann/phpunit-documentation-chinese) 19 | 20 | ### Добавление нового перевода 21 | 22 | Если вы хотите создать новый перевод, пожалуйста, откройте ишью в 23 | трекере английской документации, в которой укажите, на какой язык вы хотели бы 24 | перевести документацию. Будет создан репозиторий и добавлен к доступным переводам. 25 | 26 | В идеале вы уже подготовили первую версию перевода в форке или 27 | копии английской документации, которая затем будет импортирована в 28 | официальный репозиторий. 29 | 30 | ## Сборка документации 31 | 32 | ### Требования 33 | 34 | - Python 35 | - [Sphinx](http://www.sphinx-doc.org/) 36 | - [Тема для Sphinx «Read the Docs»](https://github.com/rtfd/sphinx_rtd_theme) 37 | 38 | ### Сборка документации в формате HTML 39 | 40 | Чтобы выполнить полную сборку документации: 41 | 42 | ```bash 43 | make html 44 | ``` 45 | 46 | ### Вывод 47 | 48 | После выполнения вышеуказанной команды вы найдете HTML-файлы по пути `build/html`. 49 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | Sphinx==1.6.5 2 | sphinx_rtd_theme==0.3.0 3 | -------------------------------------------------------------------------------- /src/_static/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sebastianbergmann/phpunit-documentation-russian/7584d132eb7a810d16b8fedd7dde443803da6cc8/src/_static/.gitkeep -------------------------------------------------------------------------------- /src/annotations.rst: -------------------------------------------------------------------------------- 1 | .. _appendixes.annotations: 2 | 3 | ========= 4 | Аннотации 5 | ========= 6 | 7 | Аннотация — специальная форма синтаксических метаданных, которые могут добавлены 8 | в исходный код некоторых языков программирования. Хотя у PHP нет собственной 9 | языковой возможности для аннотирования исходного кода, использование тегов, таких как 10 | ``@annotation arguments`` в блоке документации (альтернативное название — докблок), было принято 11 | в сообществе PHP для аннотации кода. В PHP блоки документации 12 | «рефлексивны»: к ним можно получить доступ с помощью 13 | метода API Reflection ``getDocComment()`` на уровне функции, 14 | класса, методе и атрибуте. Такие приложения, как PHPUnit, используют 15 | информацию во время выполнения для настройки их поведения. 16 | 17 | .. admonition:: Примечание 18 | 19 | Комментарий документации в PHP должен начинаться с ``/**`` и заканчиваться 20 | ``*/``. Аннотации в любом другом стиле комментария будут проигнорированы. 21 | 22 | В этом приложении представлены все разновидности аннотаций, поддерживаемые PHPUnit. 23 | 24 | .. _appendixes.annotations.author: 25 | 26 | @author 27 | ####### 28 | 29 | Аннотация ``@author`` — это псевдоним для аннотации 30 | ``@group`` (см. :ref:`appendixes.annotations.group`), позволяющая фильтровать тесты 31 | на основе их авторов. 32 | 33 | .. _appendixes.annotations.after: 34 | 35 | @after 36 | ###### 37 | 38 | Аннотацию ``@after`` можно использовать для указания методов, 39 | которые должны вызываться после каждого тестового метода в тестовом классе. 40 | 41 | .. code-block:: php 42 | 43 | use PHPUnit\Framework\TestCase; 44 | 45 | class MyTest extends TestCase 46 | { 47 | /** 48 | * @after 49 | */ 50 | public function tearDownSomeFixtures() 51 | { 52 | // ... 53 | } 54 | 55 | /** 56 | * @after 57 | */ 58 | public function tearDownSomeOtherFixtures() 59 | { 60 | // ... 61 | } 62 | } 63 | 64 | .. _appendixes.annotations.afterClass: 65 | 66 | @afterClass 67 | ########### 68 | 69 | Аннотацию ``@afterClass`` можно использовать для указания 70 | статических методов, которые должны вызываться после того, как все тестовые методы 71 | в тестовом классе были запущены для очистки общих фикстур. 72 | 73 | .. code-block:: php 74 | 75 | use PHPUnit\Framework\TestCase; 76 | 77 | class MyTest extends TestCase 78 | { 79 | /** 80 | * @afterClass 81 | */ 82 | public static function tearDownSomeSharedFixtures() 83 | { 84 | // ... 85 | } 86 | 87 | /** 88 | * @afterClass 89 | */ 90 | public static function tearDownSomeOtherSharedFixtures() 91 | { 92 | // ... 93 | } 94 | } 95 | 96 | .. _appendixes.annotations.backupGlobals: 97 | 98 | @backupGlobals 99 | ############## 100 | 101 | Операции резервного копирования и восстановления глобальных переменных могут быть полностью 102 | отключены для всех тестов в тестовом классе следующим образом: 103 | 104 | .. code-block:: php 105 | 106 | use PHPUnit\Framework\TestCase; 107 | 108 | /** 109 | * @backupGlobals disabled 110 | */ 111 | class MyTest extends TestCase 112 | { 113 | // ... 114 | } 115 | 116 | Аннотацию ``@backupGlobals`` также можно использовать на уровне 117 | тестового метода. Это позволяет выполнять тонкую настройку операций 118 | резервного копирования и восстановления: 119 | 120 | .. code-block:: php 121 | 122 | use PHPUnit\Framework\TestCase; 123 | 124 | /** 125 | * @backupGlobals disabled 126 | */ 127 | class MyTest extends TestCase 128 | { 129 | /** 130 | * @backupGlobals enabled 131 | */ 132 | public function testThatInteractsWithGlobalVariables() 133 | { 134 | // ... 135 | } 136 | } 137 | 138 | .. _appendixes.annotations.backupStaticAttributes: 139 | 140 | @backupStaticAttributes 141 | ####################### 142 | 143 | Аннотацию ``@backupStaticAttributes`` можно использовать для 144 | резервного копирования всех значений статических свойств во всех объявленных классах перед 145 | каждым тестом с последующим их восстановлением. Она может использоваться на уровне тестового класса 146 | или тестового метода: 147 | 148 | .. code-block:: php 149 | 150 | use PHPUnit\Framework\TestCase; 151 | 152 | /** 153 | * @backupStaticAttributes enabled 154 | */ 155 | class MyTest extends TestCase 156 | { 157 | /** 158 | * @backupStaticAttributes disabled 159 | */ 160 | public function testThatInteractsWithStaticAttributes() 161 | { 162 | // ... 163 | } 164 | } 165 | 166 | .. admonition:: Примечание 167 | 168 | Аннотация ``@backupStaticAttributes`` ограничивается внутренне PHP 169 | и при определённых условиях может привести 170 | к непреднамеренному сохранению статических значений и утечке памяти 171 | в последующих тестах. 172 | 173 | См. :ref:`fixtures.global-state` дополнительной информации. 174 | 175 | .. _appendixes.annotations.before: 176 | 177 | @before 178 | ####### 179 | 180 | Аннотацию ``@before`` можно использовать для указания методов, 181 | которые должны вызываться перед каждым тестовым методом в тестовом классе. 182 | 183 | .. code-block:: php 184 | 185 | use PHPUnit\Framework\TestCase; 186 | 187 | class MyTest extends TestCase 188 | { 189 | /** 190 | * @before 191 | */ 192 | public function setupSomeFixtures() 193 | { 194 | // ... 195 | } 196 | 197 | /** 198 | * @before 199 | */ 200 | public function setupSomeOtherFixtures() 201 | { 202 | // ... 203 | } 204 | } 205 | 206 | .. _appendixes.annotations.beforeClass: 207 | 208 | @beforeClass 209 | ############ 210 | 211 | Аннотацию ``@beforeClass`` можно использовать для указания 212 | статических методов, которые должны вызываться до выполнения любых тестов в тестовом 213 | классе для настройки общих фикстур. 214 | 215 | .. code-block:: php 216 | 217 | use PHPUnit\Framework\TestCase; 218 | 219 | class MyTest extends TestCase 220 | { 221 | /** 222 | * @beforeClass 223 | */ 224 | public static function setUpSomeSharedFixtures() 225 | { 226 | // ... 227 | } 228 | 229 | /** 230 | * @beforeClass 231 | */ 232 | public static function setUpSomeOtherSharedFixtures() 233 | { 234 | // ... 235 | } 236 | } 237 | 238 | .. _appendixes.annotations.codeCoverageIgnore: 239 | 240 | @codeCoverageIgnore* 241 | #################### 242 | 243 | Аннотации ``@codeCoverageIgnore``, 244 | ``@codeCoverageIgnoreStart`` и 245 | ``@codeCoverageIgnoreEnd`` могут использоваться 246 | для исключения строк кода из анализа покрытия. 247 | 248 | Для использования см. :ref:`code-coverage-analysis.ignoring-code-blocks`. 249 | 250 | .. _appendixes.annotations.covers: 251 | 252 | @covers 253 | ####### 254 | 255 | Аннотация ``@covers`` может использовать в тестовом коде для 256 | указания, какие методы собирается тестировать данный тестовый метод: 257 | 258 | .. code-block:: php 259 | 260 | /** 261 | * @covers BankAccount::getBalance 262 | */ 263 | public function testBalanceIsInitiallyZero() 264 | { 265 | $this->assertSame(0, $this->ba->getBalance()); 266 | } 267 | 268 | Если эта аннотация задана, будет учитываться информация о покрытии кода только для указанных методов. 269 | 270 | :numref:`appendixes.annotations.covers.tables.annotations` показывает 271 | синтаксис аннотации ``@covers``. 272 | 273 | .. rst-class:: table 274 | .. list-table:: Аннотации для указания, какие методы покрываются тестом 275 | :name: appendixes.annotations.covers.tables.annotations 276 | :header-rows: 1 277 | 278 | * - Аннотация 279 | - Описание 280 | * - ``@covers ClassName::methodName`` 281 | - Указывает, что аннотированный тестовый метод покрывает указанный метод. 282 | * - ``@covers ClassName`` 283 | - Указывает, что аннотированный тестовый метод покрывает все методы данного класса. 284 | * - ``@covers ClassName`` 285 | - Указывает, что аннотированный тестовый метод покрывает все методы заданного класса и его родительских классов или интерфейсов. 286 | * - ``@covers ClassName::`` 287 | - Указывает, что аннотированный тестовый метод покрывает все общедоступные методы заданного класса. 288 | * - ``@covers ClassName::`` 289 | - Указывает, что аннотированный тестовый метод покрывает все защищённые методы заданного класса. 290 | * - ``@covers ClassName::`` 291 | - Указывает, что аннотированный тестовый метод покрывает все закрытые методы заданного класса. 292 | * - ``@covers ClassName::`` 293 | - Указывает, что аннотированный тестовый метод покрывает все не общедоступные методы заданного класса. 294 | * - ``@covers ClassName::`` 295 | - Указывает, что аннотированный тестовый метод покрывает все не защищённые методы заданного класса. 296 | * - ``@covers ClassName::`` 297 | - Указывает, что аннотированный тестовый метод покрывает все не закрытые методы заданного класса. 298 | * - ``@covers ::functionName`` 299 | - Указывает, что аннотированный тестовый метод покрывает указанную глобальную функцию. 300 | 301 | .. _appendixes.annotations.coversDefaultClass: 302 | 303 | @coversDefaultClass 304 | ################### 305 | 306 | Аннотацию ``@coversDefaultClass`` можно использовать 307 | для указания пространства имени по умолчанию или класса. Таким образом, длинные имена не нужно 308 | повторно указывать для каждой аннотации ``@covers``. См. 309 | :numref:`appendixes.annotations.examples.CoversDefaultClassTest.php`. 310 | 311 | .. code-block:: php 312 | :caption: Использование @coversDefaultClass для сокращений аннотаций 313 | :name: appendixes.annotations.examples.CoversDefaultClassTest.php 314 | 315 | publicMethod(); 330 | } 331 | } 332 | 333 | .. _appendixes.annotations.coversNothing: 334 | 335 | @coversNothing 336 | ############## 337 | 338 | Аннотацию ``@coversNothing`` можно использовать в тестовом 339 | коде для указания, что информация о покрытии кода не должна учитываться 340 | для данного тестового класса. 341 | 342 | Это можно использовать для интеграционного тестирования. См. 343 | :ref:`code-coverage-analysis.specifying-covered-methods.examples.GuestbookIntegrationTest.php` 344 | для примера. 345 | 346 | Данную аннотацию можно использовать на уровне классе или метода и переопределить любые теги ``@covers``. 347 | 348 | .. _appendixes.annotations.dataProvider: 349 | 350 | @dataProvider 351 | ############# 352 | 353 | Тестовый метод может принимать произвольное количество аргументов. Эти аргументы должны 354 | быть предоставлены одним или несколькими методами провайдера данных (``provider()`` в 355 | :ref:`writing-tests-for-phpunit.data-providers.examples.DataTest.php`). 356 | Используемый метод провайдера данных задаётся с помощью аннотации 357 | ``@dataProvider``. 358 | 359 | См. :ref:`writing-tests-for-phpunit.data-providers` для получения подробной информации. 360 | 361 | .. _appendixes.annotations.depends: 362 | 363 | @depends 364 | ######## 365 | 366 | PHPUnit поддерживает объявление явных зависимостей между тестовыми 367 | методами. Такие зависимости не определяют порядок, в котором должны выполняться тестовые методы, 368 | но они позволяют возвращать экземпляр 369 | фикстуры теста продюсером и передавать его зависимым потребителям. 370 | :ref:`writing-tests-for-phpunit.examples.StackTest2.php` показывает, 371 | как использовать аннотацию ``@depends`` для выражения 372 | зависимостей между тестовыми методами. 373 | 374 | См. :ref:`writing-tests-for-phpunit.test-dependencies` для подробной информации. 375 | 376 | .. _appendixes.annotations.doesNotPerformAssertions: 377 | 378 | @doesNotPerformAssertions 379 | ######################### 380 | 381 | Предотвращает выполнение теста, не выполняющего никаких утверждений, для того чтобы не считать его рискованным. 382 | 383 | .. _appendixes.annotations.expectedException: 384 | 385 | @expectedException 386 | ################## 387 | 388 | :ref:`writing-tests-for-phpunit.exceptions.examples.ExceptionTest.php` 389 | показывает, как использовать аннотацию ``@expectedException`` 390 | для проверки того, было ли выброшено исключение внутри тестируемого кода. 391 | 392 | См. :ref:`writing-tests-for-phpunit.exceptions` для получения подробной информации. 393 | 394 | .. _appendixes.annotations.expectedExceptionCode: 395 | 396 | @expectedExceptionCode 397 | ###################### 398 | 399 | Аннотация ``@expectedExceptionCode`` в сочетании 400 | с ``@expectedException`` позволяет делать утверждения по 401 | коду ошибке выбрасываемого исключения, таким образом, сужая конкретное исключение. 402 | 403 | .. code-block:: php 404 | 405 | use PHPUnit\Framework\TestCase; 406 | 407 | class MyTest extends TestCase 408 | { 409 | /** 410 | * @expectedException MyException 411 | * @expectedExceptionCode 20 412 | */ 413 | public function testExceptionHasErrorCode20() 414 | { 415 | throw new MyException('Сообщение исключения', 20); 416 | } 417 | } 418 | 419 | Для облегчения тестирования и уменьшения дублирования можно указать 420 | константу класса в 421 | ``@expectedExceptionCode``, используя синтаксис 422 | "``@expectedExceptionCode ClassName::CONST``". 423 | 424 | .. code-block:: php 425 | 426 | use PHPUnit\Framework\TestCase; 427 | 428 | class MyTest extends TestCase 429 | { 430 | /** 431 | * @expectedException MyException 432 | * @expectedExceptionCode MyClass::ERRORCODE 433 | */ 434 | public function testExceptionHasErrorCode20() 435 | { 436 | throw new MyException('Сообщение исключения', 20); 437 | } 438 | } 439 | class MyClass 440 | { 441 | const ERRORCODE = 20; 442 | } 443 | 444 | .. _appendixes.annotations.expectedExceptionMessage: 445 | 446 | @expectedExceptionMessage 447 | ######################### 448 | 449 | Аннотация ``@expectedExceptionMessage`` работает аналогично 450 | ``@expectedExceptionCode``, поскольку она может сделать 451 | утверждение на сообщении исключения. 452 | 453 | .. code-block:: php 454 | 455 | use PHPUnit\Framework\TestCase; 456 | 457 | class MyTest extends TestCase 458 | { 459 | /** 460 | * @expectedException MyException 461 | * @expectedExceptionMessage Сообщение исключения 462 | */ 463 | public function testExceptionHasRightMessage() 464 | { 465 | throw new MyException('Сообщение исключения', 20); 466 | } 467 | } 468 | 469 | Ожидаемое сообщение может быть подстрокой сообщения исключения. 470 | Это может быть полезно, для того чтобы только утверждать, что переданное определённое имя или параметр 471 | встречается в исключении, не фокусируясь на полном совпадении сообщения исключения в тесте. 472 | 473 | .. code-block:: php 474 | 475 | use PHPUnit\Framework\TestCase; 476 | 477 | class MyTest extends TestCase 478 | { 479 | /** 480 | * @expectedException MyException 481 | * @expectedExceptionMessage broken 482 | */ 483 | public function testExceptionHasRightMessage() 484 | { 485 | $param = 'broken'; 486 | throw new MyException('Некорректный параметр "' . $param . '".', 20); 487 | } 488 | } 489 | 490 | Для облегчения тестирования и уменьшения дублирования можно указать 491 | константу класса в 492 | ``@expectedExceptionMessage``, используя синтаксис 493 | "``@expectedExceptionMessage ClassName::CONST``". 494 | Для примера можно посмотреть на :ref:`appendixes.annotations.expectedExceptionCode`. 495 | 496 | .. _appendixes.annotations.expectedExceptionMessageRegExp: 497 | 498 | @expectedExceptionMessageRegExp 499 | ############################### 500 | 501 | Ожидаемое сообщение также можно указать в виде регулярного выражения, используя 502 | аннотацию ``@expectedExceptionMessageRegExp``. Это 503 | полезно в ситуациях, когда подстрока не подходит для соответствия 504 | заданному сообщению. 505 | 506 | .. code-block:: php 507 | 508 | use PHPUnit\Framework\TestCase; 509 | 510 | class MyTest extends TestCase 511 | { 512 | /** 513 | * @expectedException MyException 514 | * @expectedExceptionMessageRegExp /Аргумент \d+ не может быть целым ? \w+/u 515 | */ 516 | public function testExceptionHasRightMessage() 517 | { 518 | throw new MyException('Аргумент 2 не может быть целым числом'); 519 | } 520 | } 521 | 522 | .. _appendixes.annotations.group: 523 | 524 | @group 525 | ###### 526 | 527 | Тест может быть отмечен как принадлежащий одной или нескольким группам, используя аннотацию 528 | ``@group`` следующим образом: 529 | 530 | .. code-block:: php 531 | 532 | use PHPUnit\Framework\TestCase; 533 | 534 | class MyTest extends TestCase 535 | { 536 | /** 537 | * @group specification 538 | */ 539 | public function testSomething() 540 | { 541 | } 542 | 543 | /** 544 | * @group regresssion 545 | * @group bug2204 546 | */ 547 | public function testSomethingElse() 548 | { 549 | } 550 | } 551 | 552 | Аннотацию ``@group`` можно задать для тестового 553 | класса. Затем она будет «унаследована» всеми методами этого тестового класса. 554 | 555 | Тесты могут быть выбраны для выполнения на основе групп с использованием 556 | опций командной строки исполнителя тестов ``--group`` и ``--exclude-group`` 557 | или используя соответствующие директивы конфигурационного XML-файла. 558 | 559 | .. _appendixes.annotations.large: 560 | 561 | @large 562 | ###### 563 | 564 | Аннотация ``@large`` — псевдоним для ``@group large``. 565 | 566 | Если пакет ``PHP_Invoker`` установлен и включён 567 | строгий режим, большой тест завершится неудачно, если для его выполнения 568 | потребуется более 60 секунд. Этот тайм-аут настраивается через атрибут 569 | ``timeoutForLargeTests`` в конфигурационном XML-файле. 570 | 571 | .. _appendixes.annotations.medium: 572 | 573 | @medium 574 | ####### 575 | 576 | Аннотация ``@medium`` — псевдоним для ``@group medium``. Средний тест не должен 577 | зависеть от теста, отмеченного как ``@large``. 578 | 579 | Если пакет ``PHP_Invoker`` установлен и включён 580 | строгий режим, средний тест завершится неудачно, если для его выполнения 581 | потребуется более 10 секунд. Этот тайм-аут настраивается через атрибут 582 | ``timeoutForMediumTests`` в конфигурационном XML-файле. 583 | 584 | .. _appendixes.annotations.preserveGlobalState: 585 | 586 | @preserveGlobalState 587 | #################### 588 | 589 | Когда тест запускается в отдельном процессе, PHPUnit попытается 590 | сохранить глобальное состояние из родительского процесса, 591 | сериализуя все глобальные переменные в родительском процессе и десериализуя их 592 | в дочернем процессе. Это может вызвать проблемы, если родительский процесс 593 | содержит глобальные переменные, которые невозможно сериализовать. 594 | Для исправления этого, вы можете запретить PHPUnit сохранять глобальное состояние с помощью аннотации 595 | ``@preserveGlobalState``. 596 | 597 | .. code-block:: php 598 | 599 | use PHPUnit\Framework\TestCase; 600 | 601 | class MyTest extends TestCase 602 | { 603 | /** 604 | * @runInSeparateProcess 605 | * @preserveGlobalState disabled 606 | */ 607 | public function testInSeparateProcess() 608 | { 609 | // ... 610 | } 611 | } 612 | 613 | .. _appendixes.annotations.requires: 614 | 615 | @requires 616 | ######### 617 | 618 | Аннотация ``@requires`` можно использовать для пропуска тестов, когда общие 619 | предварительные условия, такие как версия PHP или установленные расширения, не выполняются. 620 | 621 | Полный список возможностей и примеров можно найти в 622 | :ref:`incomplete-and-skipped-tests.requires.tables.api` 623 | 624 | .. _appendixes.annotations.runTestsInSeparateProcesses: 625 | 626 | @runTestsInSeparateProcesses 627 | ############################ 628 | 629 | Указывает, что все тесты в тестовом классе должны выполняться в отдельном процессе PHP. 630 | 631 | .. code-block:: php 632 | 633 | use PHPUnit\Framework\TestCase; 634 | 635 | /** 636 | * @runTestsInSeparateProcesses 637 | */ 638 | class MyTest extends TestCase 639 | { 640 | // ... 641 | } 642 | 643 | *Примечание:* По умолчанию PHPUnit пытается 644 | сохранить глобальное состояние из родительского процесса, сериализуя 645 | все глобальные переменные в родительском процессе и десериализуя их 646 | в дочернем процессе. Это может вызвать проблемы, если родительский процесс 647 | содержит глобальные переменные, которые невозможно сериализовать. 648 | См. :ref:`appendixes.annotations.preserveGlobalState` для получения информации 649 | по изменению этого поведения. 650 | 651 | .. _appendixes.annotations.runInSeparateProcess: 652 | 653 | @runInSeparateProcess 654 | ##################### 655 | 656 | Указывает, что тест должен выполняться в отдельном процессе PHP. 657 | 658 | .. code-block:: php 659 | 660 | use PHPUnit\Framework\TestCase; 661 | 662 | class MyTest extends TestCase 663 | { 664 | /** 665 | * @runInSeparateProcess 666 | */ 667 | public function testInSeparateProcess() 668 | { 669 | // ... 670 | } 671 | } 672 | 673 | *Примечание:* По умолчанию PHPUnit пытается 674 | сохранить глобальное состояние из родительского процесса, сериализуя 675 | все глобальные переменные в родительском процессе и десериализуя их 676 | в дочернем процессе. Это может вызвать проблемы, если родительский процесс 677 | содержит глобальные переменные, которые невозможно сериализовать. 678 | См. :ref:`appendixes.annotations.preserveGlobalState` для получения информации 679 | по изменению этого поведения. 680 | 681 | .. _appendixes.annotations.small: 682 | 683 | @small 684 | ###### 685 | 686 | Аннотация ``@small`` — это псевдоним для 687 | ``@group small``. Небольшой тест не должен зависеть от теста, 688 | отмеченного как ``@medium`` или ``@large``. 689 | 690 | Если пакет ``PHP_Invoker`` установлен и включён 691 | строгий режим, небольшой тест завершится неудачно, если для его выполнения 692 | потребуется более 1 секунды. Этот тайм-аут настраивается через атрибут 693 | ``timeoutForSmallTests`` в конфигурационном XML-файле. 694 | 695 | .. admonition:: Примечание 696 | 697 | Тесты должны быть явно аннотированы либо ``@small``, 698 | ``@medium`` или ``@large`` для включения ограничения времени выполнения. 699 | 700 | .. _appendixes.annotations.test: 701 | 702 | @test 703 | ##### 704 | 705 | В качестве альтернативы добавления префиксов именам тестовым методам 706 | ``test``, вы можете использовать аннотацию ``@test`` 707 | в блоке документации метода, чтобы отметить его как тестовый метод. 708 | 709 | .. code-block:: php 710 | 711 | /** 712 | * @test 713 | */ 714 | public function initialBalanceShouldBe0() 715 | { 716 | $this->assertSame(0, $this->ba->getBalance()); 717 | } 718 | 719 | .. _appendixes.annotations.testdox: 720 | 721 | @testdox 722 | ######## 723 | 724 | Указывает альтернативное описание, используемое при создании предложений для agile-документации. 725 | 726 | Аннотацию ``@testdox`` можно применять как к тестовым классам, так и к тестовым методам. 727 | 728 | .. code-block:: php 729 | 730 | /** 731 | * @testdox A bank account 732 | */ 733 | class BankAccountTest extends TestCase 734 | { 735 | /** 736 | * @testdox has an initial balance of zero 737 | */ 738 | public function balanceIsInitiallyZero() 739 | { 740 | $this->assertSame(0, $this->ba->getBalance()); 741 | } 742 | } 743 | 744 | .. admonition:: Примечание 745 | 746 | До PHPUnit 7.0 (из-за бага в разборе аннотации) использование 747 | аннотации ``@testdox`` также активировало поведение 748 | аннотацию ``@test``. 749 | 750 | .. code-block:: php 751 | 752 | .. _appendixes.annotations.testWith: 753 | 754 | @testWith 755 | ######### 756 | 757 | Вместо реализации метода для использования с ``@dataProvider``, 758 | вы можете определить набор данных, используя аннотацию ``@testWith``. 759 | 760 | Набор данных состоит из одного или нескольких элементов. Для определения набора данных 761 | с несколькими элементами, определите каждый элемент на отдельной строке. 762 | Каждый элемент набора данных должен быть массив, определённым в JSON. 763 | 764 | См. :ref:`writing-tests-for-phpunit.data-providers` для получения 765 | дополнительной информации о передачи набора данных в тест. 766 | 767 | .. code-block:: php 768 | 769 | /** 770 | * @param string $input 771 | * @param int $expectedLength 772 | * 773 | * @testWith ["test", 4] 774 | * ["longer-string", 13] 775 | */ 776 | public function testStringLength(string $input, int $expectedLength) 777 | { 778 | $this->assertSame($expectedLength, strlen($input)); 779 | } 780 | 781 | Представление объекта в JSON будет преобразовано в ассоциативный массив. 782 | 783 | .. code-block:: php 784 | 785 | /** 786 | * @param array $array 787 | * @param array $keys 788 | * 789 | * @testWith [{"day": "monday", "conditions": "sunny"}, ["day", "conditions"]] 790 | */ 791 | public function testArrayKeys($array, $keys) 792 | { 793 | $this->assertSame($keys, array_keys($array)); 794 | } 795 | 796 | .. _appendixes.annotations.ticket: 797 | 798 | @ticket 799 | ####### 800 | 801 | Аннотация ``@ticket`` — это псевдоним для аннотации ``@group`` 802 | (см. :ref:`appendixes.annotations.group`) и позволяет фильтровать тесты на основе 803 | их идентификатора тикета. 804 | 805 | .. _appendixes.annotations.uses: 806 | 807 | @uses 808 | ##### 809 | 810 | Аннотация ``@uses`` указывает код, который будет 811 | выполняться тестом, но не предназначен для покрытия тестом. Хорошим 812 | примером может быть объект значения (value object), который необходим для тестирования единицы (модуля) кода. 813 | 814 | .. code-block:: php 815 | 816 | /** 817 | * @covers BankAccount::deposit 818 | * @uses Money 819 | */ 820 | public function testMoneyCanBeDepositedInAccount() 821 | { 822 | // ... 823 | } 824 | 825 | Эта аннотация особенно полезна в режиме строгого режима, когда 826 | непреднамеренно покрытый код приводит тесте к неудаче. См. 827 | :ref:`risky-tests.unintentionally-covered-code` для получения 828 | дополнительной информации о строгом режиме покрытия. 829 | -------------------------------------------------------------------------------- /src/bibliography.rst: -------------------------------------------------------------------------------- 1 | 2 | 3 | .. _appendixes.bibliography: 4 | 5 | ============ 6 | Библиография 7 | ============ 8 | 9 | [Astels2003] David Astels. *Test Driven Development*. 10 | 11 | [Beck2002] Kent Beck. *Test Driven Development by Example*. [1]_ 12 | 13 | [Meszaros2007] Gerard Meszaros. *xUnit Test Patterns: Refactoring Test Code*. [2]_ 14 | 15 | .. [1] Кент Бек. "Экстремальное программирование. Разработка через тестирование". Питер, 2017. 16 | .. [2] Джерард Месарош. "Шаблоны тестирования xUnit: рефакторинг кода тестов". Вильямс, 2009. 17 | -------------------------------------------------------------------------------- /src/code-coverage-analysis.rst: -------------------------------------------------------------------------------- 1 | 2 | 3 | .. _code-coverage-analysis: 4 | 5 | ==================== 6 | Анализ покрытия кода 7 | ==================== 8 | 9 | *Википедия*: 10 | 11 | В информатике покрытие кода — мера, используемая для описания степени, 12 | в которой исходной код программы протестирован определённым набором тестов. 13 | Программа с высоким покрытием кода была более тщательно протестирована и имеет меньше шансов содержать ошибки программного обеспечения, чем программа с низким покрытием кода тестами. 14 | 15 | В этой главе вы узнаете всё о функциональности покрытия кода PHPUnit, 16 | которая даёт представление о том, какие части кода выполняются при выполнении тестов. 17 | Она использует компонент `php-code-coverage `_, 18 | который, в свою очередь, использует функциональность покрытия кода, предоставляемую 19 | PHP-расширением `Xdebug `_. 20 | 21 | .. admonition:: Примечание 22 | 23 | Xdebug не распространяется как часть PHPUnit. Если во время тестирования вы получаете 24 | уведомление о том, что драйвер покрытия кода отсутствует, это означает, 25 | что Xdebug либо не установлен, либо неправильно настроен. 26 | Прежде чем вы сможете использовать возможности анализа покрытия кода, 27 | вам следует `прочитать руководство по установке Xdebug `_. 28 | 29 | php-code-coverage также поддерживает `phpdbg `_ 30 | в качестве альтернативного источника для данных покрытия кода. 31 | 32 | PHPUnit может генерировать отчёт о покрытии кода на основе HTML, а также лог-файлы в представлении XML 33 | с информацией о покрытии кода в различных форматах (Clover, Crap4J, PHPUnit). 34 | Информация о покрытии кода также может быть представлена в виде текста 35 | (и напечатана в STDOUT) и экспортирована как код PHP для дальнейшей обработки. 36 | 37 | Обратитесь к :ref:`textui` для просмотра списка переключателей командной строки, 38 | которые управляют функциональностью покрытия кода, а также :ref:`appendixes.configuration.logging` 39 | для получения соответствующих параметров конфигурации. 40 | 41 | .. _code-coverage-analysis.metrics: 42 | 43 | Показатели программного обеспечения покрытия кода 44 | ################################################# 45 | 46 | Существуют различные показатели (метрики) программного обеспечения (далее просто «показатель») для оценки покрытия кода: 47 | 48 | *Покрытие строки (Line Coverage)* 49 | 50 | Показатель *Line Coverage* определяет, 51 | была ли выполнена каждая исполняемая строка. 52 | 53 | *Покрытие функции и метода (Function and Method Coverage)* 54 | 55 | Показатель *Function and Method Coverage* 56 | определяет, была ли вызвана каждая функция или метод. 57 | php-code-coverage рассматривает функцию или метод как покрытую тестом, 58 | только когда все исполняемые строки покрыты. 59 | 60 | *Покрытие класса и трейта (Class and Trait Coverage)* 61 | 62 | Показатель *Class and Trait Coverage* 63 | определяет, был ли покрыт каждый метод класса или трейта. 64 | php-code-coverage рассматривает класс или трейт как покрытый, 65 | только когда все их методы покрыты. 66 | 67 | *Покрытие кода операции (Opcode Coverage)* 68 | 69 | Показатель *Opcode Coverage* определяет, выполнялся ли каждый опкод (код операции, opcode) 70 | функции или метода во время выполнения набора тестов. Строка кода обычно компилируется 71 | в несколько кодов опкодов. *Line Coverage* рассматривает строку как покрытую, как только 72 | один из опкодов будет выполнен. 73 | 74 | *Покрытие ветки (Branch Coverage)* 75 | 76 | Показатель *Branch Coverage* определяет, было ли логическое выражение каждой 77 | управляющей структуры оценено как ``true``, так и ``false`` при выполнении набора тестов. 78 | 79 | *Покрытие пути (Path Coverage)* 80 | 81 | Показатель *Path Coverage* определяет, использовался ли каждый из возможных 82 | путей выполнения функции или метода во время выполнения набора тестов. 83 | Путь выполнения — это уникальная последовательность ветвей от входа функции или метода до его выхода. 84 | 85 | *Индекс Change Risk Anti-Patterns (CRAP)* 86 | 87 | Индекс *Change Risk Anti-Patterns (CRAP)* 88 | рассчитывается на основе цикломатической сложности и покрытия кода 89 | единицы кода. Код, который не слишком сложный и имеет адекватный процент 90 | покрытия кода, будет иметь низкий индекс CRAP. Индекс CRAP может быть снижен 91 | путём написания тестов и рефакторинга тестов для уменьшения его сложности. 92 | 93 | .. admonition:: Примечание 94 | 95 | Показатели *Opcode Coverage*, 96 | *Branch Coverage* и 97 | *Path Coverage* ещё не поддерживаются 98 | php-code-coverage. 99 | 100 | .. _code-coverage-analysis.whitelisting-files: 101 | 102 | Белый список файлов 103 | ################### 104 | 105 | Необходимо настроить *белый список* (*whitelist*) для указания 106 | PHPUnit, какие файлы исходного кода следует включить в отчёт о покрытии кода. 107 | Это можно сделать либо используя опцию командной строки ``--whitelist``, 108 | либо через файл конфигурации (см. :ref:`appendixes.configuration.whitelisting-files`). 109 | 110 | Доступны параметры конфигурации ``addUncoveredFilesFromWhitelist`` и ``processUncoveredFilesFromWhitelist`` 111 | для настройки использования белого списка: 112 | 113 | - ``addUncoveredFilesFromWhitelist="false"`` означает, что в отчёт о покрытии кода будут включены только файлы из белого списка, содержащие хотя бы одну строку выполненного кода 114 | - ``addUncoveredFilesFromWhitelist="true"`` (по умолчанию) означает, что все файлы из белого списка будут включены в отчёт о покрытии кода, даже если ни одна строка кода такого файла не была выполнена 115 | - ``processUncoveredFilesFromWhitelist="false"`` (по умолчанию) означает, что в отчёт о покрытии кода будет включены файлы из белого списка, у которых нет исполненных строк кода (если установлено ``addUncoveredFilesFromWhitelist="true"``), но он не будет загружен PHPUnit и поэтому не будет анализироваться для корректной информации о исполненных строк кода 116 | - ``processUncoveredFilesFromWhitelist="true"`` означает, что файл в белом списке, у которого нет исполненных строк кода, будет загружен PHPUnit, чтобы его можно было анализировать для корректной информации о исполненных строк 117 | 118 | .. admonition:: Примечание 119 | 120 | Обратите внимание, что загрузка файлов исходного кода, выполняемая при установке 121 | ``processUncoveredFilesFromWhitelist="true"``, может вызвать проблемы, например, 122 | когда файл исходного кода содержит код вне области класса или функции. 123 | 124 | .. _code-coverage-analysis.ignoring-code-blocks: 125 | 126 | Игнорирование блоков кода 127 | ######################### 128 | 129 | Иногда у вас есть блоки кода, которые вы не можете протестировать и поэтому вы можете 130 | игнорировать при анализе покрытия кода. PHPUnit позволяет сделать это с использованием 131 | аннотаций ``@codeCoverageIgnore``, 132 | ``@codeCoverageIgnoreStart`` и 133 | ``@codeCoverageIgnoreEnd``, как показано в 134 | :numref:`code-coverage-analysis.ignoring-code-blocks.examples.Sample.php`. 135 | 136 | .. code-block:: php 137 | :caption: Использование аннотаций ``@codeCoverageIgnore``, ``@codeCoverageIgnoreStart`` и ``@codeCoverageIgnoreEnd`` 138 | :name: code-coverage-analysis.ignoring-code-blocks.examples.Sample.php 139 | 140 | ba = new BankAccount; 201 | } 202 | 203 | /** 204 | * @covers BankAccount::getBalance 205 | */ 206 | public function testBalanceIsInitiallyZero() 207 | { 208 | $this->assertSame(0, $this->ba->getBalance()); 209 | } 210 | 211 | /** 212 | * @covers BankAccount::withdrawMoney 213 | */ 214 | public function testBalanceCannotBecomeNegative() 215 | { 216 | try { 217 | $this->ba->withdrawMoney(1); 218 | } 219 | 220 | catch (BankAccountException $e) { 221 | $this->assertSame(0, $this->ba->getBalance()); 222 | 223 | return; 224 | } 225 | 226 | $this->fail(); 227 | } 228 | 229 | /** 230 | * @covers BankAccount::depositMoney 231 | */ 232 | public function testBalanceCannotBecomeNegative2() 233 | { 234 | try { 235 | $this->ba->depositMoney(-1); 236 | } 237 | 238 | catch (BankAccountException $e) { 239 | $this->assertSame(0, $this->ba->getBalance()); 240 | 241 | return; 242 | } 243 | 244 | $this->fail(); 245 | } 246 | 247 | /** 248 | * @covers BankAccount::getBalance 249 | * @covers BankAccount::depositMoney 250 | * @covers BankAccount::withdrawMoney 251 | */ 252 | public function testDepositWithdrawMoney() 253 | { 254 | $this->assertSame(0, $this->ba->getBalance()); 255 | $this->ba->depositMoney(1); 256 | $this->assertSame(1, $this->ba->getBalance()); 257 | $this->ba->withdrawMoney(1); 258 | $this->assertSame(0, $this->ba->getBalance()); 259 | } 260 | } 261 | 262 | Также можно указать, что тест не должен покрывать 263 | *какой-либо* метод, используя аннотацию 264 | ``@coversNothing`` (см. 265 | :ref:`appendixes.annotations.coversNothing`). Это может быть 266 | полезно при написании интеграционных тестов, чтобы убедиться, что вы 267 | только генерируете покрытие кода с помощью модульных тестов. 268 | 269 | .. code-block:: php 270 | :caption: Тест, который указывает, что ни один метод не должен быть покрыт 271 | :name: code-coverage-analysis.specifying-covered-methods.examples.GuestbookIntegrationTest.php 272 | 273 | addEntry("suzy", "Hello world!"); 285 | 286 | $queryTable = $this->getConnection()->createQueryTable( 287 | 'guestbook', 'SELECT * FROM guestbook' 288 | ); 289 | 290 | $expectedTable = $this->createFlatXmlDataSet("expectedBook.xml") 291 | ->getTable("guestbook"); 292 | 293 | $this->assertTablesEqual($expectedTable, $queryTable); 294 | } 295 | } 296 | 297 | .. _code-coverage-analysis.edge-cases: 298 | 299 | Крайние случаи 300 | ############## 301 | 302 | В этом разделе показаны заслуживающие внимания крайние случаи, которые приводят к 303 | путанице информации о покрытии кода. 304 | 305 | .. code-block:: php 306 | :name: code-coverage-analysis.edge-cases.examples.Sample.php 307 | 308 | `` by default 38 | lexers['php'] = PhpLexer(startinline=True) 39 | lexers['php-annotations'] = PhpLexer(startinline=True) 40 | 41 | # If extensions (or modules to document with autodoc) are in another directory, 42 | # add these directories to sys.path here. If the directory is relative to the 43 | # documentation root, use os.path.abspath to make it absolute, like shown here. 44 | #sys.path.insert(0, os.path.abspath('.')) 45 | 46 | # -- General configuration ------------------------------------------------ 47 | 48 | # If your documentation needs a minimal Sphinx version, state it here. 49 | needs_sphinx = '1.3' 50 | 51 | # Add any Sphinx extension module names here, as strings. They can be 52 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 53 | # ones. 54 | extensions = [ 55 | 'sphinx.ext.autodoc', 56 | ] 57 | 58 | # Add any paths that contain templates here, relative to this directory. 59 | templates_path = ['_templates'] 60 | 61 | # The suffix(es) of source filenames. 62 | # You can specify multiple suffix as a list of string: 63 | # source_suffix = ['.rst', '.md'] 64 | source_suffix = '.rst' 65 | 66 | # The encoding of source files. 67 | #source_encoding = 'utf-8-sig' 68 | 69 | # The master toctree document. 70 | master_doc = 'index' 71 | 72 | # General information about the project. 73 | project = u'PHPUnit' 74 | copyright = u'2019, Sebastian Bergmann' 75 | author = u'Sebastian Bergmann' 76 | epub_author = u'Sebastian Bergmann' 77 | 78 | # The version info for the project you're documenting, acts as replacement for 79 | # |version| and |release|, also used in various other places throughout the 80 | # built documents. 81 | # 82 | # The short X.Y version. 83 | version = get_version().strip() 84 | # The full version, including alpha/beta/rc tags. 85 | release = version 86 | 87 | # The language for content autogenerated by Sphinx. Refer to documentation 88 | # for a list of supported languages. 89 | # 90 | # This is also used if you do content translation via gettext catalogs. 91 | # Usually you set "language" from the command line for these cases. 92 | language = None 93 | 94 | # There are two options for replacing |today|: either, you set today to some 95 | # non-false value, then it is used: 96 | #today = '' 97 | # Else, today_fmt is used as the format for a strftime call. 98 | today_fmt = '%d.%m.%y %H:%M:%S' 99 | 100 | # List of patterns, relative to source directory, that match files and 101 | # directories to ignore when looking for source files. 102 | exclude_patterns = [] 103 | 104 | # The reST default role (used for this markup: `text`) to use for all 105 | # documents. 106 | #default_role = None 107 | 108 | # If true, '()' will be appended to :func: etc. cross-reference text. 109 | #add_function_parentheses = True 110 | 111 | # If true, the current module name will be prepended to all description 112 | # unit titles (such as .. function::). 113 | #add_module_names = True 114 | 115 | # If true, sectionauthor and moduleauthor directives will be shown in the 116 | # output. They are ignored by default. 117 | #show_authors = False 118 | 119 | # The name of the Pygments (syntax highlighting) style to use. 120 | pygments_style = 'sphinx' 121 | 122 | # A list of ignored prefixes for module index sorting. 123 | #modindex_common_prefix = [] 124 | 125 | # If true, keep warnings as "system message" paragraphs in the built documents. 126 | #keep_warnings = False 127 | 128 | # If true, `todo` and `todoList` produce output, else they produce nothing. 129 | todo_include_todos = False 130 | 131 | 132 | # -- Options for HTML output ---------------------------------------------- 133 | 134 | html_theme = 'sphinx_rtd_theme' 135 | html_theme_options = { 136 | 'collapse_navigation': False, 137 | 'display_version': False, 138 | 'analytics_id': 'UA-130682870-1' 139 | } 140 | 141 | # Add any paths that contain custom themes here, relative to this directory. 142 | #html_theme_path = ['_templates'] 143 | 144 | html_add_permalinks = "" 145 | 146 | # The name for this set of Sphinx documents. If None, it defaults to 147 | # " v documentation". 148 | html_title = u"Руководство PHPUnit на русском языке %s" % get_version() 149 | 150 | # A shorter title for the navigation bar. Default is the same as html_title. 151 | #html_short_title = None 152 | 153 | # The name of an image file (relative to this directory) to place at the top 154 | # of the sidebar. 155 | #html_logo = None 156 | 157 | # The name of an image file (within the static path) to use as favicon of the 158 | # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 159 | # pixels large. 160 | #html_favicon = None 161 | 162 | # Add any paths that contain custom static files (such as style sheets) here, 163 | # relative to this directory. They are copied after the builtin static files, 164 | # so a file named "default.css" will overwrite the builtin "default.css". 165 | html_static_path = ['_static'] 166 | 167 | # Add any extra paths that contain custom files (such as robots.txt or 168 | # .htaccess) here, relative to this directory. These files are copied 169 | # directly to the root of the documentation. 170 | #html_extra_path = [] 171 | 172 | # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, 173 | # using the given strftime format. 174 | #html_last_updated_fmt = '%b %d, %Y' 175 | 176 | # If true, SmartyPants will be used to convert quotes and dashes to 177 | # typographically correct entities. 178 | #html_use_smartypants = True 179 | 180 | # Custom sidebar templates, maps document names to template names. 181 | #html_sidebars = {} 182 | 183 | # Additional templates that should be rendered to pages, maps page names to 184 | # template names. 185 | #html_additional_pages = {} 186 | 187 | # If false, no module index is generated. 188 | #html_domain_indices = True 189 | 190 | # If false, no index is generated. 191 | #html_use_index = True 192 | 193 | # If true, the index is split into individual pages for each letter. 194 | #html_split_index = False 195 | 196 | # If true, links to the reST sources are added to the pages. 197 | #html_show_sourcelink = True 198 | 199 | # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. 200 | html_show_sphinx = False 201 | 202 | # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. 203 | #html_show_copyright = True 204 | 205 | # If true, an OpenSearch description file will be output, and all pages will 206 | # contain a tag referring to it. The value of this option must be the 207 | # base URL from which the finished HTML is served. 208 | #html_use_opensearch = '' 209 | 210 | # This is the file name suffix for HTML files (e.g. ".xhtml"). 211 | #html_file_suffix = None 212 | 213 | # Language to be used for generating the HTML full-text search index. 214 | # Sphinx supports the following languages: 215 | # 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja' 216 | # 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr' 217 | #html_search_language = 'en' 218 | 219 | # A dictionary with options for the search language support, empty by default. 220 | # Now only 'ja' uses this config value 221 | #html_search_options = {'type': 'default'} 222 | 223 | # The name of a javascript file (relative to the configuration directory) that 224 | # implements a search results scorer. If empty, the default will be used. 225 | #html_search_scorer = 'scorer.js' 226 | 227 | # Output file base name for HTML help builder. 228 | htmlhelp_basename = 'PHPUnitdoc' 229 | 230 | # -- Options for LaTeX output --------------------------------------------- 231 | 232 | latex_elements = { 233 | # The paper size ('letterpaper' or 'a4paper'). 234 | #'papersize': 'letterpaper', 235 | 236 | # The font size ('10pt', '11pt' or '12pt'). 237 | #'pointsize': '10pt', 238 | 239 | # Additional stuff for the LaTeX preamble. 240 | #'preamble': '', 241 | 242 | # Latex figure (float) alignment 243 | #'figure_align': 'htbp', 244 | } 245 | 246 | # Grouping the document tree into LaTeX files. List of tuples 247 | # (source start file, target name, title, 248 | # author, documentclass [howto, manual, or own class]). 249 | latex_documents = [ 250 | (master_doc, 'PHPUnit.tex', u'PHPUnit Manual', 251 | u'Sebastian Bergmann', 'manual'), 252 | ] 253 | 254 | # The name of an image file (relative to this directory) to place at the top of 255 | # the title page. 256 | #latex_logo = None 257 | 258 | # For "manual" documents, if this is true, then toplevel headings are parts, 259 | # not chapters. 260 | #latex_use_parts = False 261 | 262 | # If true, show page references after internal links. 263 | #latex_show_pagerefs = False 264 | 265 | # If true, show URL addresses after external links. 266 | #latex_show_urls = False 267 | 268 | # Documents to append as an appendix to all manuals. 269 | #latex_appendices = [] 270 | 271 | # If false, no module index is generated. 272 | #latex_domain_indices = True 273 | 274 | 275 | # -- Options for manual page output --------------------------------------- 276 | 277 | # One entry per manual page. List of tuples 278 | # (source start file, name, description, authors, manual section). 279 | man_pages = [ 280 | (master_doc, 'phpunit', u'PHPUnit Documentation', 281 | [author], 1) 282 | ] 283 | 284 | # If true, show URL addresses after external links. 285 | #man_show_urls = False 286 | 287 | 288 | # -- Options for Texinfo output ------------------------------------------- 289 | 290 | # Grouping the document tree into Texinfo files. List of tuples 291 | # (source start file, target name, title, author, 292 | # dir menu entry, description, category) 293 | texinfo_documents = [ 294 | (master_doc, 'PHPUnit', u'PHPUnit Manual', 295 | author, 'PHPUnit', 'One line description of project.', 296 | 'Miscellaneous'), 297 | ] 298 | 299 | # Documents to append as an appendix to all manuals. 300 | #texinfo_appendices = [] 301 | 302 | # If false, no module index is generated. 303 | #texinfo_domain_indices = True 304 | 305 | # How to display URL addresses: 'footnote', 'no', or 'inline'. 306 | #texinfo_show_urls = 'footnote' 307 | 308 | # If true, do not generate a @detailmenu in the "Top" node's menu. 309 | #texinfo_no_detailmenu = False 310 | 311 | numfig = True 312 | 313 | numfig_format = { 314 | 'code-block': u'Пример %s', 315 | 'figure': u'Изображение %s', 316 | 'table': u'Таблица %s', 317 | 'section': u'Раздел' 318 | } 319 | 320 | 321 | # -- Options for epub output ------------------------------------------- 322 | 323 | -------------------------------------------------------------------------------- /src/configuration.rst: -------------------------------------------------------------------------------- 1 | 2 | 3 | .. _appendixes.configuration: 4 | 5 | ========================== 6 | Конфигурационный XML-файл 7 | ========================== 8 | 9 | .. _appendixes.configuration.phpunit: 10 | 11 | PHPUnit 12 | ####### 13 | 14 | Атрибуты элемента ```` можно 15 | использовать для настройки основной функциональности PHPUnit. 16 | 17 | .. parsed-literal:: 18 | 19 | 25 | cacheResult="false" 26 | cacheTokens="false" 27 | colors="false" 28 | convertErrorsToExceptions="true" 29 | convertNoticesToExceptions="true" 30 | convertWarningsToExceptions="true" 31 | forceCoversAnnotation="false" 32 | printerClass="PHPUnit\TextUI\ResultPrinter" 33 | 34 | processIsolation="false" 35 | stopOnError="false" 36 | stopOnFailure="false" 37 | stopOnIncomplete="false" 38 | stopOnSkipped="false" 39 | stopOnRisky="false" 40 | testSuiteLoaderClass="PHPUnit\Runner\StandardTestSuiteLoader" 41 | 42 | timeoutForSmallTests="1" 43 | timeoutForMediumTests="10" 44 | timeoutForLargeTests="60" 45 | verbose="false"> 46 | 47 | 48 | 49 | Конфигурация XML выше соответствует поведению по умолчанию 50 | исполнителя тестов TextUI, описанному в :ref:`textui.clioptions`. 51 | 52 | Дополнительные опции, недоступные в качестве опций командной строки: 53 | 54 | ``convertErrorsToExceptions`` 55 | 56 | По умолчанию PHPUnit установит обработчик ошибок, которые преобразует 57 | следующие ошибки в исключения: 58 | 59 | - ``E_WARNING`` 60 | 61 | - ``E_NOTICE`` 62 | 63 | - ``E_USER_ERROR`` 64 | 65 | - ``E_USER_WARNING`` 66 | 67 | - ``E_USER_NOTICE`` 68 | 69 | - ``E_STRICT`` 70 | 71 | - ``E_RECOVERABLE_ERROR`` 72 | 73 | - ``E_DEPRECATED`` 74 | 75 | - ``E_USER_DEPRECATED`` 76 | 77 | Установите ``convertErrorsToExceptions`` в 78 | ``false`` для отключения этой возможности. 79 | 80 | ``convertNoticesToExceptions`` 81 | 82 | Когда установлено значение ``false``, обработчик ошибок, установленный 83 | ``convertErrorsToExceptions``, не будет преобразовывать ошибки 84 | ``E_NOTICE``, ``E_USER_NOTICE`` или 85 | ``E_STRICT`` в исключения. 86 | 87 | ``convertWarningsToExceptions`` 88 | 89 | Когда установлено значение ``false``, обработчик ошибок, установленный 90 | ``convertErrorsToExceptions``, не будет преобразовывать ошибки 91 | ``E_WARNING`` или ``E_USER_WARNING`` 92 | в исключения. 93 | 94 | ``forceCoversAnnotation`` 95 | 96 | Покрытие кода будет записываться только для тестов, в которых используется 97 | аннотация ``@covers``, задокументированная в 98 | :ref:`appendixes.annotations.covers`. 99 | 100 | ``timeoutForLargeTests`` 101 | 102 | Если применяется ограничение по времени, основанное на размере теста, тогда этот атрибут 103 | устанавливает тайм-аут для всех тестов, отмеченных как ``@large``. 104 | Если тест не завершится в течение установленного тайм-аута, он завершится неудачей. 105 | 106 | ``timeoutForMediumTests`` 107 | 108 | Если применяется ограничение по времени, основанное на размере теста, тогда этот атрибут 109 | устанавливает тайм-аут для всех тестов, отмеченных как ``@medium``. 110 | Если тест не завершится в течение установленного тайм-аута, он завершится неудачей. 111 | 112 | ``timeoutForSmallTests`` 113 | 114 | Если применяется ограничение по времени, основанное на размере теста, тогда этот атрибут 115 | установит тайм-аут для всех тестов, не отмеченных как 116 | ``@medium`` или ``@large``. 117 | Если тест не завершится в течение установленного тайм-аута, он завершится неудачей. 118 | 119 | .. _appendixes.configuration.testsuites: 120 | 121 | Набор тестов 122 | ############ 123 | 124 | Элемент ```` и его 125 | один или несколько дочерних элементов ```` можно 126 | использовать для составления набора тестов из наборов тестов и тестовых классов. 127 | 128 | .. code-block:: xml 129 | 130 | 131 | 132 | /path/to/*Test.php files 133 | /path/to/MyTest.php 134 | /path/to/exclude 135 | 136 | 137 | 138 | Используя атрибуты ``phpVersion`` и 139 | ``phpVersionOperator`` можно указать требуемую версию PHP. 140 | В приведённом ниже примере будут добавлены только файлы 141 | :file:`/path/to/\*Test.php` 142 | и файл :file:`/path/to/MyTest.php`, если версия PHP не менее 5.3.0. 143 | 144 | .. code-block:: xml 145 | 146 | 147 | 148 | /path/to/files 149 | /path/to/MyTest.php 150 | 151 | 152 | 153 | Атрибут ``phpVersionOperator`` не является обязательным 154 | и по умолчанию ``>=``. 155 | 156 | .. _appendixes.configuration.groups: 157 | 158 | Группы 159 | ###### 160 | 161 | Элемент ```` и его дочерние элементы 162 | ````, 163 | ```` и 164 | ```` можно использовать для выбора 165 | групп тестов, отмеченных аннотацией ``@group`` 166 | (описанных в :ref:`appendixes.annotations.group`), 167 | которые должны (или не должны) выполняться. 168 | 169 | .. code-block:: xml 170 | 171 | 172 | 173 | name 174 | 175 | 176 | name 177 | 178 | 179 | 180 | Вышеприведённая конфигурация XML соответствует вызову исполнителя тестов TextUI 181 | со следующими опциями: 182 | 183 | - 184 | 185 | ``--group name`` 186 | 187 | - 188 | 189 | ``--exclude-group name`` 190 | 191 | .. _appendixes.configuration.whitelisting-files: 192 | 193 | Файлы в белом списке для покрытия кода 194 | ###################################### 195 | 196 | Элемент ```` и его дочерние элементы можно 197 | использовать для настройки белого списка при создании отчёта о покрытии кода. 198 | 199 | .. code-block:: xml 200 | 201 | 202 | 203 | /path/to/files 204 | /path/to/file 205 | 206 | /path/to/files 207 | /path/to/file 208 | 209 | 210 | 211 | 212 | .. _appendixes.configuration.logging: 213 | 214 | Логирование 215 | ########### 216 | 217 | Элемент ```` и его дочерние элементы ```` 218 | можно использовать для настройки логирования 219 | выполнения тестов. 220 | 221 | .. code-block:: xml 222 | 223 | 224 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | Вышеприведённая конфигурация XML соответствует вызову исполнителя тестов TextUI 235 | со следующими опциями: 236 | 237 | - 238 | 239 | ``--coverage-html /tmp/report`` 240 | 241 | - 242 | 243 | ``--coverage-clover /tmp/coverage.xml`` 244 | 245 | - 246 | 247 | ``--coverage-php /tmp/coverage.serialized`` 248 | 249 | - 250 | 251 | ``--coverage-text`` 252 | 253 | - 254 | 255 | ``> /tmp/logfile.txt`` 256 | 257 | - 258 | 259 | ``--log-junit /tmp/logfile.xml`` 260 | 261 | - 262 | 263 | ``--testdox-html /tmp/testdox.html`` 264 | 265 | - 266 | 267 | ``--testdox-text /tmp/testdox.txt`` 268 | 269 | Атрибуты ``lowUpperBound``, ``highLowerBound``, 270 | ``showUncoveredFiles`` не имеет эквивалента опции 271 | исполнителя тестов TextUI. 272 | 273 | - 274 | 275 | ``lowUpperBound``: Максимальный процент покрытия, который считается "низко" покрытым. 276 | 277 | - 278 | 279 | ``highLowerBound``: Минимальный процент покрытия, который считается "высоко" покрытым. 280 | 281 | - 282 | 283 | ``showUncoveredFiles``: Показать все файлы в белом списке при выводе с опцией ``--coverage-text``, 284 | а не только те, для которых есть информация о покрытии. 285 | 286 | - 287 | 288 | ``showOnlySummary``: Показать только краткую сводку в выводе при использовании ``--coverage-text``. 289 | 290 | .. _appendixes.configuration.test-listeners: 291 | 292 | Обработчики тестов 293 | ################## 294 | 295 | Элемент ```` и его дочерние элементы 296 | ```` можно использовать для присоединения 297 | дополнительных обработчиков теста к выполнению теста. 298 | 299 | .. code-block:: xml 300 | 301 | 302 | 303 | 304 | 305 | 306 | Sebastian 307 | 308 | 309 | 22 310 | April 311 | 19.78 312 | 313 | 314 | 315 | 316 | 317 | 318 | Вышеприведённая конфигурация XML соответствует прикреплению объекта 319 | ``$listener`` (см. ниже) к выполнению теста: 320 | 321 | .. code-block:: php 322 | 323 | $listener = new MyListener( 324 | ['Sebastian'], 325 | 22, 326 | 'April', 327 | 19.78, 328 | null, 329 | new stdClass 330 | ); 331 | 332 | .. _appendixes.configuration.extensions: 333 | 334 | Регистрация расширений TestRunner 335 | ################################# 336 | 337 | Элемент ```` и его дочерние элементы ```` 338 | можно использовать для регистрации пользовательских расширений TestRunner. 339 | 340 | :numref:`configuration.examples.RegisterExtension` показывает, как 341 | зарегистрировать такое расширение. 342 | 343 | .. code-block:: xml 344 | :caption: Регистрация расширения TestRunner 345 | :name: configuration.examples.RegisterExtension 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | .. _appendixes.configuration.php-ini-constants-variables: 355 | 356 | Установка INI-настроек, констант и глобальных переменных PHP 357 | ############################################################ 358 | 359 | Элемент ```` и его дочерние элементы 360 | можно использовать для настройки параметров, констант и глобальных переменных PHP. 361 | Он может также использоваться для добавления новых путей в опцию ``include_path``. 362 | 363 | .. code-block:: xml 364 | 365 | 366 | . 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | Вышеприведённая конфигурация XML соответствует следующем коду PHP: 380 | 381 | .. code-block:: php 382 | 383 | ini_set('foo', 'bar'); 384 | define('foo', 'bar'); 385 | $GLOBALS['foo'] = 'bar'; 386 | $_ENV['foo'] = 'bar'; 387 | $_POST['foo'] = 'bar'; 388 | $_GET['foo'] = 'bar'; 389 | $_COOKIE['foo'] = 'bar'; 390 | $_SERVER['foo'] = 'bar'; 391 | $_FILES['foo'] = 'bar'; 392 | $_REQUEST['foo'] = 'bar'; 393 | 394 | 395 | -------------------------------------------------------------------------------- /src/copyright.rst: -------------------------------------------------------------------------------- 1 | 2 | 3 | .. _appendixes.copyright: 4 | 5 | =============== 6 | Авторские права 7 | =============== 8 | 9 | .. code-block:: text 10 | 11 | Copyright (c) 2005-2019 Sebastian Bergmann. 12 | 13 | Эта работа распространяется под лицензией Creative Commons Attribution 3.0 Unported. 14 | 15 | Ниже приводится краткое описание лицензии, после которого следует полный 16 | юридический текст (на английском). 17 | 18 | -------------------------------------------------------------------- 19 | 20 | Вы можете свободно: 21 | 22 | * Делиться (обмениваться) — копировать, распространять и передавать работу 23 | * Адаптировать — делать ремиксы, видоизменять, и создавать новое, 24 | опираясь на эту работу 25 | 26 | При обязательном соблюдении следующих условий: 27 | 28 | Вы должны обеспечить соответствующее указание авторства, предоставить ссылку 29 | на лицензию, и обозначить изменения, если таковые были сделаны. Вы можете 30 | это делать любым разумным способом, но не таким, который подразумевал бы, 31 | что лицензиар одобряет вас или ваш способ использования произведения. 32 | 33 | * Для любого повторного использования или распространения вы должны 34 | разъяснять другим условия лицензии на данную работу. 35 | Лучший способ сделать это — ссылку на эту веб-страницу. 36 | 37 | * Любое из вышеуказанных условий может быть отменено, если вы получите 38 | разрешение владельца авторских прав. 39 | 40 | * Ничто в этой лицензии не нарушает или ограничивает моральные права 41 | автора. 42 | 43 | Ваше добросовестное использование и другие права никоим образом не затрагиваются 44 | выше. 45 | 46 | Это доступное для человеческого понимания резюме Юридического Кодекса 47 | (полная лицензия) ниже. 48 | 49 | ==================================================================== 50 | 51 | Creative Commons Legal Code 52 | Attribution 3.0 Unported 53 | 54 | CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE 55 | LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN 56 | ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS 57 | INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO 58 | WARRANTIES REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS 59 | LIABILITY FOR DAMAGES RESULTING FROM ITS USE. 60 | 61 | License 62 | 63 | THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS 64 | CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS 65 | PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE 66 | WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW 67 | IS PROHIBITED. 68 | 69 | BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND 70 | AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS 71 | LICENSE MAY BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU 72 | THE RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF 73 | SUCH TERMS AND CONDITIONS. 74 | 75 | 1. Definitions 76 | 77 | a. "Adaptation" means a work based upon the Work, or upon the 78 | Work and other pre-existing works, such as a translation, 79 | adaptation, derivative work, arrangement of music or other 80 | alterations of a literary or artistic work, or phonogram or 81 | performance and includes cinematographic adaptations or any 82 | other form in which the Work may be recast, transformed, or 83 | adapted including in any form recognizably derived from the 84 | original, except that a work that constitutes a Collection 85 | will not be considered an Adaptation for the purpose of this 86 | License. For the avoidance of doubt, where the Work is a 87 | musical work, performance or phonogram, the synchronization of 88 | the Work in timed-relation with a moving image ("synching") 89 | will be considered an Adaptation for the purpose of this 90 | License. 91 | 92 | b. "Collection" means a collection of literary or artistic works, 93 | such as encyclopedias and anthologies, or performances, 94 | phonograms or broadcasts, or other works or subject matter 95 | other than works listed in Section 1(f) below, which, by 96 | reason of the selection and arrangement of their contents, 97 | constitute intellectual creations, in which the Work is 98 | included in its entirety in unmodified form along with one or 99 | more other contributions, each constituting separate and 100 | independent works in themselves, which together are assembled 101 | into a collective whole. A work that constitutes a Collection 102 | will not be considered an Adaptation (as defined above) for 103 | the purposes of this License. 104 | 105 | c. "Distribute" means to make available to the public the 106 | original and copies of the Work or Adaptation, as appropriate, 107 | through sale or other transfer of ownership. 108 | 109 | d. "Licensor" means the individual, individuals, entity or 110 | entities that offer(s) the Work under the terms of this License. 111 | 112 | e. "Original Author" means, in the case of a literary or artistic 113 | work, the individual, individuals, entity or entities who 114 | created the Work or if no individual or entity can be 115 | identified, the publisher; and in addition (i) in the case of 116 | a performance the actors, singers, musicians, dancers, and 117 | other persons who act, sing, deliver, declaim, play in, 118 | interpret or otherwise perform literary or artistic works or 119 | expressions of folklore; (ii) in the case of a phonogram the 120 | producer being the person or legal entity who first fixes the 121 | sounds of a performance or other sounds; and, (iii) in the 122 | case of broadcasts, the organization that transmits the 123 | broadcast. 124 | 125 | f. "Work" means the literary and/or artistic work offered under 126 | the terms of this License including without limitation any 127 | production in the literary, scientific and artistic domain, 128 | whatever may be the mode or form of its expression including 129 | digital form, such as a book, pamphlet and other writing; a 130 | lecture, address, sermon or other work of the same nature; a 131 | dramatic or dramatico-musical work; a choreographic work or 132 | entertainment in dumb show; a musical composition with or 133 | without words; a cinematographic work to which are assimilated 134 | works expressed by a process analogous to cinematography; a 135 | work of drawing, painting, architecture, sculpture, engraving 136 | or lithography; a photographic work to which are assimilated 137 | works expressed by a process analogous to photography; a work 138 | of applied art; an illustration, map, plan, sketch or three- 139 | dimensional work relative to geography, topography, 140 | architecture or science; a performance; a broadcast; a 141 | phonogram; a compilation of data to the extent it is protected 142 | as a copyrightable work; or a work performed by a variety or 143 | circus performer to the extent it is not otherwise considered 144 | a literary or artistic work. 145 | 146 | g. "You" means an individual or entity exercising rights under 147 | this License who has not previously violated the terms of 148 | this License with respect to the Work, or who has received 149 | express permission from the Licensor to exercise rights under 150 | this License despite a previous violation. 151 | 152 | h. "Publicly Perform" means to perform public recitations of the 153 | Work and to communicate to the public those public 154 | recitations, by any means or process, including by wire or 155 | wireless means or public digital performances; to make 156 | available to the public Works in such a way that members of 157 | the public may access these Works from a place and at a place 158 | individually chosen by them; to perform the Work to the public 159 | by any means or process and the communication to the public of 160 | the performances of the Work, including by public digital 161 | performance; to broadcast and rebroadcast the Work by any 162 | means including signs, sounds or images. 163 | 164 | i. "Reproduce" means to make copies of the Work by any means 165 | including without limitation by sound or visual recordings and 166 | the right of fixation and reproducing fixations of the Work, 167 | including storage of a protected performance or phonogram in 168 | digital form or other electronic medium. 169 | 170 | 2. Fair Dealing Rights. Nothing in this License is intended to 171 | reduce, limit, or restrict any uses free from copyright or rights 172 | arising from limitations or exceptions that are provided for in 173 | connection with the copyright protection under copyright law or 174 | other applicable laws. 175 | 176 | 3. License Grant. Subject to the terms and conditions of this 177 | License, Licensor hereby grants You a worldwide, royalty-free, 178 | non-exclusive, perpetual (for the duration of the applicable 179 | copyright) license to exercise the rights in the Work as stated 180 | below: 181 | 182 | a. to Reproduce the Work, to incorporate the Work into one or 183 | more Collections, and to Reproduce the Work as incorporated 184 | in the Collections; 185 | 186 | b. to create and Reproduce Adaptations provided that any such 187 | Adaptation, including any translation in any medium, takes 188 | reasonable steps to clearly label, demarcate or otherwise 189 | identify that changes were made to the original Work. For 190 | example, a translation could be marked "The original work was 191 | translated from English to Spanish," or a modification could 192 | indicate "The original work has been modified."; 193 | 194 | c. to Distribute and Publicly Perform the Work including as 195 | incorporated in Collections; and, 196 | 197 | d. to Distribute and Publicly Perform Adaptations. 198 | 199 | e. For the avoidance of doubt: 200 | 201 | i. Non-waivable Compulsory License Schemes. In those 202 | jurisdictions in which the right to collect royalties 203 | through any statutory or compulsory licensing scheme cannot 204 | be waived, the Licensor reserves the exclusive right to 205 | collect such royalties for any exercise by You of the 206 | rights granted under this License; 207 | 208 | ii. Waivable Compulsory License Schemes. In those 209 | jurisdictions in which the right to collect royalties 210 | through any statutory or compulsory licensing scheme can 211 | be waived, the Licensor waives the exclusive right to 212 | collect such royalties for any exercise by You of the 213 | rights granted under this License; and, 214 | 215 | iii. Voluntary License Schemes. The Licensor waives the right 216 | to collect royalties, whether individually or, in the 217 | event that the Licensor is a member of a collecting 218 | society that administers voluntary licensing schemes, via 219 | that society, from any exercise by You of the rights 220 | granted under this License. 221 | 222 | The above rights may be exercised in all media and formats whether 223 | now known or hereafter devised. The above rights include the right 224 | to make such modifications as are technically necessary to exercise 225 | the rights in other media and formats. Subject to Section 8(f), all 226 | rights not expressly granted by Licensor are hereby reserved. 227 | 228 | 4. Restrictions. The license granted in Section 3 above is expressly 229 | made subject to and limited by the following restrictions: 230 | 231 | a. You may Distribute or Publicly Perform the Work only under the 232 | terms of this License. You must include a copy of, or the 233 | Uniform Resource Identifier (URI) for, this License with every 234 | copy of the Work You Distribute or Publicly Perform. You may 235 | not offer or impose any terms on the Work that restrict the 236 | terms of this License or the ability of the recipient of the 237 | Work to exercise the rights granted to that recipient under 238 | the terms of the License. You may not sublicense the Work. You 239 | must keep intact all notices that refer to this License and to 240 | the disclaimer of warranties with every copy of the Work You 241 | Distribute or Publicly Perform. When You Distribute or 242 | Publicly Perform the Work, You may not impose any effective 243 | technological measures on the Work that restrict the ability 244 | of a recipient of the Work from You to exercise the rights 245 | granted to that recipient under the terms of the License. This 246 | Section 4(a) applies to the Work as incorporated in a 247 | Collection, but this does not require the Collection apart 248 | from the Work itself to be made subject to the terms of this 249 | License. If You create a Collection, upon notice from any 250 | Licensor You must, to the extent practicable, remove from the 251 | Collection any credit as required by Section 4(b), as 252 | requested. If You create an Adaptation, upon notice from any 253 | Licensor You must, to the extent practicable, remove from the 254 | Adaptation any credit as required by Section 4(b), as requested. 255 | 256 | b. If You Distribute, or Publicly Perform the Work or any 257 | Adaptations or Collections, You must, unless a request has 258 | been made pursuant to Section 4(a), keep intact all copyright 259 | notices for the Work and provide, reasonable to the medium or 260 | means You are utilizing: (i) the name of the Original Author 261 | (or pseudonym, if applicable) if supplied, and/or if the 262 | Original Author and/or Licensor designate another party or 263 | parties (e.g., a sponsor institute, publishing entity, 264 | journal) for attribution ("Attribution Parties") in Licensor's 265 | copyright notice, terms of service or by other reasonable 266 | means, the name of such party or parties; (ii) the title of 267 | the Work if supplied; (iii) to the extent reasonably 268 | practicable, the URI, if any, that Licensor specifies to be 269 | associated with the Work, unless such URI does not refer to 270 | the copyright notice or licensing information for the Work; 271 | and (iv), consistent with Section 3(b), in the case of an 272 | Adaptation, a credit identifying the use of the Work in the 273 | Adaptation (e.g., "French translation of the Work by Original 274 | Author," or "Screenplay based on original Work by Original 275 | Author"). The credit required by this Section 4 (b) may be 276 | implemented in any reasonable manner; provided, however, that 277 | in the case of a Adaptation or Collection, at a minimum such 278 | credit will appear, if a credit for all contributing authors 279 | of the Adaptation or Collection appears, then as part of these 280 | credits and in a manner at least as prominent as the credits 281 | for the other contributing authors. For the avoidance of 282 | doubt, You may only use the credit required by this Section 283 | for the purpose of attribution in the manner set out above 284 | and, by exercising Your rights under this License, You may not 285 | implicitly or explicitly assert or imply any connection with, 286 | sponsorship or endorsement by the Original Author, Licensor 287 | and/or Attribution Parties, as appropriate, of You or Your use 288 | of the Work, without the separate, express prior written 289 | permission of the Original Author, Licensor and/or 290 | Attribution Parties. 291 | 292 | c. Except as otherwise agreed in writing by the Licensor or as 293 | may be otherwise permitted by applicable law, if You 294 | Reproduce, Distribute or Publicly Perform the Work either by 295 | itself or as part of any Adaptations or Collections, You must 296 | not distort, mutilate, modify or take other derogatory action 297 | in relation to the Work which would be prejudicial to the 298 | Original Author's honor or reputation. Licensor agrees that in 299 | those jurisdictions (e.g. Japan), in which any exercise of the 300 | right granted in Section 3(b) of this License (the right to 301 | make Adaptations) would be deemed to be a distortion, 302 | mutilation, modification or other derogatory action 303 | prejudicial to the Original Author's honor and reputation, the 304 | Licensor will waive or not assert, as appropriate, this 305 | Section, to the fullest extent permitted by the applicable 306 | national law, to enable You to reasonably exercise Your right 307 | under Section 3(b) of this License (right to make Adaptations) 308 | but not otherwise. 309 | 310 | 5. Representations, Warranties and Disclaimer 311 | 312 | UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, 313 | LICENSOR OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR 314 | WARRANTIES OF ANY KIND CONCERNING THE WORK, EXPRESS, IMPLIED, 315 | STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, WARRANTIES OF 316 | TITLE, MERCHANTIBILITY, FITNESS FOR A PARTICULAR PURPOSE, 317 | NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, 318 | ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT 319 | DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF 320 | IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU. 321 | 322 | 6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY 323 | APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY 324 | LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE 325 | OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF 326 | THE WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY 327 | OF SUCH DAMAGES. 328 | 329 | 7. Termination 330 | 331 | a. This License and the rights granted hereunder will terminate 332 | automatically upon any breach by You of the terms of this 333 | License. Individuals or entities who have received Adaptations 334 | or Collections from You under this License, however, will not 335 | have their licenses terminated provided such individuals or 336 | entities remain in full compliance with those licenses. 337 | Sections 1, 2, 5, 6, 7, and 8 will survive any termination of 338 | this License. 339 | 340 | b. Subject to the above terms and conditions, the license granted 341 | here is perpetual (for the duration of the applicable 342 | copyright in the Work). Notwithstanding the above, Licensor 343 | reserves the right to release the Work under different license 344 | terms or to stop distributing the Work at any time; provided, 345 | however that any such election will not serve to withdraw this 346 | License (or any other license that has been, or is required to 347 | be, granted under the terms of this License), and this License 348 | will continue in full force and effect unless terminated as 349 | stated above. 350 | 351 | 8. Miscellaneous 352 | 353 | a. Each time You Distribute or Publicly Perform the Work or a 354 | Collection, the Licensor offers to the recipient a license to 355 | the Work on the same terms and conditions as the license 356 | granted to You under this License. 357 | 358 | b. Each time You Distribute or Publicly Perform an Adaptation, 359 | Licensor offers to the recipient a license to the original 360 | Work on the same terms and conditions as the license granted 361 | to You under this License. 362 | 363 | c. If any provision of this License is invalid or unenforceable 364 | under applicable law, it shall not affect the validity or 365 | enforceability of the remainder of the terms of this License, 366 | and without further action by the parties to this agreement, 367 | such provision shall be reformed to the minimum extent 368 | necessary to make such provision valid and enforceable. 369 | 370 | d. No term or provision of this License shall be deemed waived 371 | and no breach consented to unless such waiver or consent shall 372 | be in writing and signed by the party to be charged with such 373 | waiver or consent. 374 | 375 | e. This License constitutes the entire agreement between the 376 | parties with respect to the Work licensed here. There are no 377 | understandings, agreements or representations with respect to 378 | the Work not specified here. Licensor shall not be bound by 379 | any additional provisions that may appear in any communication 380 | from You. This License may not be modified without the mutual 381 | written agreement of the Licensor and You. 382 | 383 | f. The rights granted under, and the subject matter referenced, 384 | in this License were drafted utilizing the terminology of the 385 | Berne Convention for the Protection of Literary and Artistic 386 | Works (as amended on September 28, 1979), the Rome Convention 387 | of 1961, the WIPO Copyright Treaty of 1996, the WIPO 388 | Performances and Phonograms Treaty of 1996 and the Universal 389 | Copyright Convention (as revised on July 24, 1971). These 390 | rights and subject matter take effect in the relevant 391 | jurisdiction in which the License terms are sought to be 392 | enforced according to the corresponding provisions of the 393 | implementation of those treaty provisions in the applicable 394 | national law. If the standard suite of rights granted under 395 | applicable copyright law includes additional rights not 396 | granted under this License, such additional rights are deemed 397 | to be included in the License; this License is not intended to 398 | restrict the license of any rights under applicable law. 399 | 400 | Creative Commons is not a party to this License, and makes no 401 | warranty whatsoever in connection with the Work. Creative Commons 402 | will not be liable to You or any party on any legal theory for any 403 | damages whatsoever, including without limitation any general, 404 | special, incidental or consequential damages arising in connection 405 | to this license. Notwithstanding the foregoing two (2) sentences, 406 | if Creative Commons has expressly identified itself as the Licensor 407 | hereunder, it shall have all rights and obligations of Licensor. 408 | 409 | Except for the limited purpose of indicating to the public that the 410 | Work is licensed under the CCPL, Creative Commons does not authorize 411 | the use by either party of the trademark "Creative Commons" or any 412 | related trademark or logo of Creative Commons without the prior 413 | written consent of Creative Commons. Any permitted use will be in 414 | compliance with Creative Commons' then-current trademark usage 415 | guidelines, as may be published on its website or otherwise made 416 | available upon request from time to time. For the avoidance of 417 | doubt, this trademark restriction does not form part of this 418 | License. 419 | 420 | Creative Commons may be contacted at http://creativecommons.org/. 421 | 422 | ==================================================================== 423 | 424 | -------------------------------------------------------------------------------- /src/extending-phpunit.rst: -------------------------------------------------------------------------------- 1 | 2 | 3 | .. _extending-phpunit: 4 | 5 | ================== 6 | Расширение PHPUnit 7 | ================== 8 | 9 | PHPUnit можно расширить различными способами для облегчения процесса написания тестов 10 | и настройки обратной связи, получаемой от выполнения тестов. Вот общие отправные точки 11 | для расширения PHPUnit. 12 | 13 | .. _extending-phpunit.PHPUnit_Framework_TestCase: 14 | 15 | Подкласс PHPUnit\\Framework\\TestCase 16 | ##################################### 17 | 18 | Написать пользовательские утверждения и вспомогательные методы 19 | в абстрактных подклассах ``PHPUnit\Framework\TestCase`` и наследуйте 20 | ваши классы тестов от этого класса. 21 | Это один из самых простых способов расширения PHPUnit. 22 | 23 | .. _extending-phpunit.custom-assertions: 24 | 25 | Написание пользовательских утверждений 26 | ###################################### 27 | 28 | При написании пользовательских утверждений лучше всего следовать 29 | принципам реализации собственных утверждений PHPUnit. 30 | Как вы можете видеть в 31 | :numref:`extending-phpunit.examples.Assert.php`, метод 32 | ``assertTrue()`` — это просто обёртка над методами 33 | ``isTrue()`` и ``assertThat()``: 34 | ``isTrue()`` создаёт объект сопоставления, который передаётся 35 | ``assertThat()`` для проведения вычисления. 36 | 37 | .. code-block:: php 38 | :caption: Методы assertTrue() и isTrue() класса PHPUnit\\Framework\\Assert 39 | :name: extending-phpunit.examples.Assert.php 40 | 41 | getName()); 146 | } 147 | 148 | public function addWarning(PHPUnit\Framework\Test $test, PHPUnit\Framework\Warning $e, float $time): void 149 | { 150 | printf("Предупреждение во время выполнения теста '%s'.\n", $test->getName()); 151 | } 152 | 153 | public function addFailure(PHPUnit\Framework\Test $test, PHPUnit\Framework\AssertionFailedError $e, float $time): void 154 | { 155 | printf("Тест '%s' провалился.\n", $test->getName()); 156 | } 157 | 158 | public function addIncompleteTest(PHPUnit\Framework\Test $test, Exception $e, float $time): void 159 | { 160 | printf("Тест '%s' является неполным.\n", $test->getName()); 161 | } 162 | 163 | public function addRiskyTest(PHPUnit\Framework\Test $test, Exception $e, float $time): void 164 | { 165 | printf("Тест '%s' считается рискованным.\n", $test->getName()); 166 | } 167 | 168 | public function addSkippedTest(PHPUnit\Framework\Test $test, Exception $e, float $time): void 169 | { 170 | printf("Тест '%s' был пропущен.\n", $test->getName()); 171 | } 172 | 173 | public function startTest(PHPUnit\Framework\Test $test): void 174 | { 175 | printf("Тест '%s' запустился.\n", $test->getName()); 176 | } 177 | 178 | public function endTest(PHPUnit\Framework\Test $test, float $time): void 179 | { 180 | printf("Тест '%s' завершился.\n", $test->getName()); 181 | } 182 | 183 | public function startTestSuite(PHPUnit\Framework\TestSuite $suite): void 184 | { 185 | printf("Набор тестов '%s' запустился.\n", $suite->getName()); 186 | } 187 | 188 | public function endTestSuite(PHPUnit\Framework\TestSuite $suite): void 189 | { 190 | printf("Набор тестов '%s' завершился.\n", $suite->getName()); 191 | } 192 | } 193 | 194 | :numref:`extending-phpunit.examples.ExtendedTestListener.php` 195 | показывает, как использовать трейт ``PHPUnit\Framework\TestListenerDefaultImplementation``, 196 | который позволяет указать только интересующие методы интерфейса для вашего случая, 197 | но при этом предоставляет пустые реализации для всех остальных методов. 198 | 199 | .. code-block:: php 200 | :caption: Использование трейта с реализацией по умолчанию для обработчика тестов 201 | :name: extending-phpunit.examples.ExtendedTestListener.php 202 | 203 | getName()); 214 | } 215 | } 216 | 217 | В :ref:`appendixes.configuration.test-listeners` вы увидите, 218 | как настроить PHPUnit для добавления обработчика тестов 219 | к выполнению теста. 220 | 221 | .. _extending-phpunit.PHPUnit_Framework_Test: 222 | 223 | Реализация PHPUnit\\Framework\\Test 224 | ################################### 225 | 226 | Интерфейс ``PHPUnit\Framework\Test`` — небольшой и простой 227 | для реализации. Вы можете написать реализацию 228 | ``PHPUnit\Framework\Test``, которая проще, чем 229 | ``PHPUnit\Framework\TestCase``, и которая, например, запускает 230 | *тесты, управляемые данными*. 231 | 232 | :numref:`extending-phpunit.examples.DataDrivenTest.php` 233 | показывает класс теста, управляемого данными, который сравнивает значения из CSV-файла, 234 | где значения разделены запятой. Каждая строка такого файла выглядит примерно как 235 | ``foo;bar``, где первое значение — это то, что мы ожидаем, а 236 | второе значение — фактическое. 237 | 238 | .. code-block:: php 239 | :caption: Тест, управляемый данными 240 | :name: extending-phpunit.examples.DataDrivenTest.php 241 | 242 | lines = file($dataFile); 252 | } 253 | 254 | public function count() 255 | { 256 | return 1; 257 | } 258 | 259 | public function run(PHPUnit\Framework\TestResult $result = null) 260 | { 261 | if ($result === null) { 262 | $result = new PHPUnit\Framework\TestResult; 263 | } 264 | 265 | foreach ($this->lines as $line) { 266 | $result->startTest($this); 267 | PHP_Timer::start(); 268 | $stopTime = null; 269 | 270 | list($expected, $actual) = explode(';', $line); 271 | 272 | try { 273 | PHPUnit\Framework\Assert::assertEquals( 274 | trim($expected), trim($actual) 275 | ); 276 | } 277 | 278 | catch (PHPUnit\Framework\AssertionFailedError $e) { 279 | $result->addFailure($this, $e, $stopTime); 280 | } 281 | 282 | catch (Exception $e) { 283 | $result->addError($this, $e, $stopTime); 284 | } 285 | 286 | finally { 287 | $stopTime = PHP_Timer::stop(); 288 | } 289 | 290 | $result->endTest($this, $stopTime); 291 | } 292 | 293 | return $result; 294 | } 295 | } 296 | 297 | $test = new DataDrivenTest('data_file.csv'); 298 | $result = PHPUnit\TextUI\TestRunner::run($test); 299 | 300 | .. parsed-literal:: 301 | 302 | PHPUnit |version|.0 by Sebastian Bergmann and contributors. 303 | 304 | .F 305 | 306 | Time: 0 seconds 307 | 308 | There was 1 failure: 309 | 310 | 1) DataDrivenTest 311 | Failed asserting that two strings are equal. 312 | expected string 313 | difference < x> 314 | got string 315 | /home/sb/DataDrivenTest.php:32 316 | /home/sb/DataDrivenTest.php:53 317 | 318 | FAILURES! 319 | Tests: 2, Failures: 1. 320 | 321 | .. _extending-phpunit.TestRunner: 322 | 323 | Расширение TestRunner 324 | ##################### 325 | 326 | PHPUnit |version| поддерживает расширения TestRunner, которые привязываются 327 | к различным событиям во время выполнения теста. 328 | См. :ref:`appendixes.configuration.extensions` для получения дополнительной информации 329 | о регистрации расширений в конфигурационном XML-файле PHPUnit. 330 | 331 | Каждое доступное событие, к которому может подключаться расширение, представлено интерфейсом, 332 | которое расширению необходимо реализовать. 333 | :ref:`extending-phpunit.hooks` перечисляет доступные события в 334 | PHPUnit |version|. 335 | 336 | .. _extending-phpunit.hooks: 337 | 338 | Интерфейсы доступных событий 339 | ---------------------------- 340 | 341 | - ``AfterIncompleteTestHook`` 342 | - ``AfterLastTestHook`` 343 | - ``AfterRiskyTestHook`` 344 | - ``AfterSkippedTestHook`` 345 | - ``AfterSuccessfulTestHook`` 346 | - ``AfterTestErrorHook`` 347 | - ``AfterTestFailureHook`` 348 | - ``AfterTestWarningHook`` 349 | - ``BeforeFirstTestHook`` 350 | - ``BeforeTestHook`` 351 | 352 | :numref:`extending-phpunit.examples.TestRunnerExtension` показывает пример 353 | расширения, реализующего ``BeforeFirstTestHook`` и ``AfterLastTestHook``: 354 | 355 | .. code-block:: php 356 | :caption: Пример расширения TestRunner 357 | :name: extending-phpunit.examples.TestRunnerExtension 358 | 359 | stack`` вместо локальной переменной метода 40 | ``$stack`` в утверждениях ``assertSame()``. 41 | 42 | .. code-block:: php 43 | :caption: Использование setUp(): void для создания фикстуры 44 | :name: fixtures.examples.StackTest.php 45 | 46 | stack = []; 56 | } 57 | 58 | public function testEmpty(): void 59 | { 60 | $this->assertTrue(empty($this->stack)); 61 | } 62 | 63 | public function testPush(): void 64 | { 65 | array_push($this->stack, 'foo'); 66 | $this->assertSame('foo', $this->stack[count($this->stack) - 1]); 67 | $this->assertFalse(empty($this->stack)); 68 | } 69 | 70 | public function testPop(): void 71 | { 72 | array_push($this->stack, 'foo'); 73 | $this->assertSame('foo', array_pop($this->stack)); 74 | $this->assertTrue(empty($this->stack)); 75 | } 76 | } 77 | 78 | Шаблонные методы ``setUp(): void`` и ``tearDown(): void`` вызываются по одному разу при каждом 79 | выполнении тестового метода (и для нового экземпляра) тестового класса. 80 | 81 | Кроме того, вызываются шаблонные методы ``setUpBeforeClass(): void`` и 82 | ``tearDownAfterClass(): void`` перед тем, как первый тест в тестовом классе будет выполнен, 83 | и после запуска последнего теста тестового класса, соответственно. 84 | 85 | Приведённый ниже пример показывает все шаблонные методы, доступные в тестовом классе. 86 | 87 | .. code-block:: php 88 | :caption: Пример, показывающий все доступные шаблонные методы 89 | :name: fixtures.examples.TemplateMethodsTest.php 90 | 91 | assertTrue(true); 115 | } 116 | 117 | public function testTwo(): void 118 | { 119 | fwrite(STDOUT, __METHOD__ . "\n"); 120 | $this->assertTrue(false); 121 | } 122 | 123 | protected function assertPostConditions(): void 124 | { 125 | fwrite(STDOUT, __METHOD__ . "\n"); 126 | } 127 | 128 | protected function tearDown(): void 129 | { 130 | fwrite(STDOUT, __METHOD__ . "\n"); 131 | } 132 | 133 | public static function tearDownAfterClass(): void 134 | { 135 | fwrite(STDOUT, __METHOD__ . "\n"); 136 | } 137 | 138 | protected function onNotSuccessfulTest(Exception $e): void 139 | { 140 | fwrite(STDOUT, __METHOD__ . "\n"); 141 | throw $e; 142 | } 143 | } 144 | 145 | .. parsed-literal:: 146 | 147 | $ phpunit TemplateMethodsTest 148 | PHPUnit |version|.0 by Sebastian Bergmann and contributors. 149 | 150 | TemplateMethodsTest::setUpBeforeClass 151 | TemplateMethodsTest::setUp 152 | TemplateMethodsTest::assertPreConditions 153 | TemplateMethodsTest::testOne 154 | TemplateMethodsTest::assertPostConditions 155 | TemplateMethodsTest::tearDown 156 | .TemplateMethodsTest::setUp 157 | TemplateMethodsTest::assertPreConditions 158 | TemplateMethodsTest::testTwo 159 | TemplateMethodsTest::tearDown 160 | TemplateMethodsTest::onNotSuccessfulTest 161 | FTemplateMethodsTest::tearDownAfterClass 162 | 163 | Time: 0 seconds, Memory: 5.25Mb 164 | 165 | There was 1 failure: 166 | 167 | 1) TemplateMethodsTest::testTwo 168 | Failed asserting that is true. 169 | /home/sb/TemplateMethodsTest.php:30 170 | 171 | FAILURES! 172 | Tests: 2, Assertions: 2, Failures: 1. 173 | 174 | .. _fixtures.more-setup-than-teardown: 175 | 176 | Больше setUp(): void чем tearDown(): void 177 | ######################################### 178 | 179 | Методы ``setUp(): void`` и ``tearDown(): void`` довольно симметричны 180 | в теории, но не на практике. На практике вам нужно 181 | реализовывать ``tearDown(): void``, если вы в ``setUp(): void`` создали внешние ресурсы, 182 | такие как файлы или сокеты. 183 | Если ваш метод ``setUp(): void`` просто создаёт обычные PHP-объекты, вы 184 | можете вообще игнорировать ``tearDown(): void``. Однако, если вы 185 | создаёте много объектов в своём ``setUp(): void``, вам, возможно, потребуется 186 | использовать ``unset()`` для удаления переменных, указывающих на эти объекты 187 | в своём методе ``tearDown(): void``, чтобы они могли быть очищены сборщиком мусора. 188 | Сборщик мусора объектов тестового класса непредсказуем. 189 | 190 | .. _fixtures.variations: 191 | 192 | Разновидности 193 | ############# 194 | 195 | Что произойдёт, если у вас есть два теста с немного различающимися настройками? 196 | Есть два варианта: 197 | 198 | - 199 | 200 | Если код ``setUp(): void`` отличается совсем немного, то 201 | необходимо перенести код, отличающийся от ``setUp(): void``, в тестовый метод. 202 | 203 | - 204 | 205 | Если у вас действительно разный ``setUp(): void``, вам нужен 206 | другой тестовый класс. Задайте соответствующее название классу после внесения изменений. 207 | 208 | .. _fixtures.sharing-fixture: 209 | 210 | Совместное использование фикстур 211 | ################################ 212 | 213 | Есть несколько веских причин для совместного использования фикстур между тестами, но в большинстве 214 | случаев эта необходимость связана с неразрешённой проблемой проектирования. 215 | 216 | Хорошим примером фикстуры для совместного использования между тестами может быть 217 | соединение с базой данных: вы подключаетесь к базе данных только один раз и затем повторно используете 218 | это соединение к базе данных вместо создания нового подключения для каждого теста. 219 | Это позволяет сделать ваши тесты быстрее. 220 | 221 | :numref:`fixtures.sharing-fixture.examples.DatabaseTest.php` 222 | использует шаблонные методы ``setUpBeforeClass(): void`` и 223 | ``tearDownAfterClass(): void`` для подключения к базе данных 224 | до выполнения первого теста в тестовом классе и закрытие соединения 225 | с базой данных после запуска последнего теста, соответственно. 226 | 227 | .. code-block:: php 228 | :caption: Совместное использование фикстур тестами в тестовом наборе 229 | :name: fixtures.sharing-fixture.examples.DatabaseTest.php 230 | 231 | `_ 263 | То же самое относится и к коду, использующему глобальные переменные. Обычно код, 264 | который вы хотите протестировать, сильно связан с глобальной переменной, и вы не можете 265 | управлять её созданием. Ещё одна проблема заключается в том, что одно изменение в тесте, 266 | использующем глобальную переменную, может сломать другой тест. 267 | 268 | В PHP глобальные переменные работают следующим образом: 269 | 270 | - 271 | 272 | Глобальная переменная ``$foo = 'bar';`` сохраняется как ``$GLOBALS['foo'] = 'bar';``. 273 | 274 | - 275 | 276 | Переменная ``$GLOBALS`` — это так называемая *суперглобальная* переменная. 277 | 278 | - 279 | 280 | Суперглобальные переменные — это встроенные переменные, доступные во всех областях видимости. 281 | 282 | - 283 | 284 | В области видимости функции или метода вы можете получить доступ к ``$foo`` либо напрямую через ``$GLOBALS['foo']`` или используя ``global $foo;`` 285 | для создания локальной переменной в текущей области видимости, ссылающейся на глобальную переменную. 286 | 287 | Помимо глобальных переменных, статические атрибуты классов также являются частью 288 | глобального состояния. 289 | 290 | До версии 6, PHPUnit по умолчанию запускал тесты таким образом, 291 | что изменения в глобальных и суперглобальных переменных (``$GLOBALS``, 292 | ``$_ENV``, ``$_POST``, 293 | ``$_GET``, ``$_COOKIE``, 294 | ``$_SERVER``, ``$_FILES``, 295 | ``$_REQUEST``) не влияли на другие тесты. 296 | 297 | Начиная с версии 6, PHPUnit больше не делает операции резервного копирования и восстановления 298 | глобальных и суперглобальных переменных по умолчанию. 299 | Это можно включить, используя опцию ``--globals-backup`` 300 | или настройку ``backupGlobals="true"`` в конфигурационном XML-файле. 301 | 302 | Используя опцию ``--static-backup`` или настройку 303 | ``backupStaticAttributes="true"`` в конфигурационном 304 | XML-файле, данная изоляция выше может быть расширена до статических атрибутов классов. 305 | 306 | .. admonition:: Примечание 307 | 308 | Операции резервного копирования и восстановления глобальных переменных и статических 309 | атрибутов классов используют ``serialize()`` и 310 | ``unserialize()``. 311 | 312 | Объекты некоторых классов (например, ``PDO``) не могут быть 313 | сериализованы, и операция резервного копирования будет прервана, 314 | когда подобный объект будет сохраняться, например, в массив ``$GLOBALS``. 315 | 316 | Аннотация ``@backupGlobals``, которая обсуждается в 317 | :ref:`appendixes.annotations.backupGlobals`, может использоваться для 318 | управления операциями резервного копирования и восстановления глобальных переменных. 319 | Кроме этого, вы можете предоставить чёрный список глобальных переменных, которые должны быть 320 | исключены при выполнении операций резервного копирования и восстановления, как показано ниже: 321 | 322 | .. code-block:: php 323 | 324 | class MyTest extends TestCase 325 | { 326 | protected $backupGlobalsBlacklist = ['globalVariable']; 327 | 328 | // ... 329 | } 330 | 331 | .. admonition:: Примечание 332 | 333 | Установка свойства ``$backupGlobalsBlacklist`` внутри, например, 334 | метода ``setUp(): void``, не даст никакого эффекта. 335 | 336 | Аннотацию ``@backupStaticAttributes``, обсуждаемую в 337 | :ref:`appendixes.annotations.backupStaticAttributes`, можно использовать 338 | для резервного копирования всех статических значений свойств во всех объявленных классах 339 | перед каждым тестом с последующим их восстановлением. 340 | 341 | Она обрабатывает все классы, объявленные в момент запуска теста, а не 342 | только сам тестовый класс. Она применяется только к статическим свойствам класса, 343 | а не к статическим переменным внутри функций. 344 | 345 | .. admonition:: Примечание 346 | 347 | Операция ``@backupStaticAttributes`` выполняется перед каждым тестовым методом, 348 | но только если она включена. Если статическое значение было 349 | изменено ранее выполненным тестом с отключенным 350 | ``@backupStaticAttributes``, тогда это значение будет скопировано 351 | и восстановлено, но не к первоначальному значению по умолчанию. 352 | PHP не записывает первоначально объявленное значение по умолчанию любой 353 | статической переменной. 354 | 355 | То же самое относительно и к статическим свойствам классов, которые недавно были 356 | загружены или объявлены внутри теста. Они не могут быть сброшены к первоначально 357 | объявленному значению по умолчанию после теста, так как это значение неизвестно. 358 | Независимо установленного значения, произойдёт утечка памяти в последующие тесты. 359 | 360 | Для модульных тестов рекомендуется явно сбросить значения статических свойств 361 | в методе теста ``setUp(): void`` (и в идеале также в методе ``tearDown(): void``, 362 | чтобы не повлиять на последующие выполняемые тесты). 363 | 364 | Вы можете предоставить чёрный список статических атрибутов, которые должны быть исключены из операций резервного копирования и восстановления: 365 | 366 | .. code-block:: php 367 | 368 | class MyTest extends TestCase 369 | { 370 | protected $backupStaticAttributesBlacklist = [ 371 | 'className' => ['attributeName'] 372 | ]; 373 | 374 | // ... 375 | } 376 | 377 | .. admonition:: Примечание 378 | 379 | Установка свойства ``$backupStaticAttributesBlacklist`` внутри, 380 | например, метода ``setUp(): void``, не даст никакого эффекта. 381 | 382 | 383 | -------------------------------------------------------------------------------- /src/incomplete-and-skipped-tests.rst: -------------------------------------------------------------------------------- 1 | .. _incomplete-and-skipped-tests: 2 | 3 | ============================ 4 | Неполные и пропущенные тесты 5 | ============================ 6 | 7 | .. _incomplete-and-skipped-tests.incomplete-tests: 8 | 9 | Неполные тесты 10 | ############## 11 | 12 | Когда вы работаете над новым тестовым классом, вы можете начать с написания 13 | пустых тестовых методов для отслеживания тех тестов, которые нужно написать, 14 | например: 15 | 16 | .. code-block:: php 17 | 18 | public function testSomething() 19 | { 20 | } 21 | 22 | Проблема с пустыми тестовыми методами состоит в том, что фреймворком PHPUnit 23 | они интерпретируется как успешно пройденные. Это ошибочное толкование приводит к тому, 24 | что отчёты о покрытии становятся бесполезными — вы не сможете увидеть, 25 | действительно ли тест прошёл, либо он просто ещё не реализован. 26 | Вызов ``$this->fail()`` в нереализованном тестовом методе 27 | также не поможет, поскольку тогда тест будет интерпретироваться как 28 | не пройденный. Это было бы так же неверно, как и считать нереализованный тест как пройденный. 29 | 30 | Если мы думаем об успешном тестировании как о зелёном свете, а о непройденном тесте как 31 | о красном свете, то нам нужен дополнительный жёлтый свет для обозначения теста как 32 | неполного или ещё не реализованного. 33 | ``PHPUnit\Framework\IncompleteTest`` — это интерфейс для обозначения 34 | исключения, выбрасываемого тестовым методом как результат того, что данный 35 | тестовый метод неполный или в данный момент ещё не реализован. 36 | ``PHPUnit\Framework\IncompleteTestError`` — стандартная реализация этого интерфейса. 37 | 38 | :numref:`incomplete-and-skipped-tests.incomplete-tests.examples.SampleTest.php` 39 | показывает тестовый класс ``SampleTest``, содержащий один тестовый метод ``testSomething()``. 40 | Вызывая удобный метод ``markTestIncomplete()`` (который автоматически 41 | вызывает исключение ``PHPUnit\Framework\IncompleteTestError``) в тестовом методе, мы отмечаем, 42 | что данный тест является неполным. 43 | 44 | .. code-block:: php 45 | :caption: Маркировка теста как неполного 46 | :name: incomplete-and-skipped-tests.incomplete-tests.examples.SampleTest.php 47 | 48 | assertTrue(true, 'This should already work.'); 57 | 58 | // Остановиться тут и отметить, что тест неполный. 59 | $this->markTestIncomplete( 60 | 'Этот тест ещё не реализован.' 61 | ); 62 | } 63 | } 64 | 65 | Неполный тест обозначается ``I`` в выводе исполнителя тестов командной строки 66 | PHPUnit, как показано в следующем примере: 67 | 68 | .. parsed-literal:: 69 | 70 | $ phpunit --verbose SampleTest 71 | PHPUnit |version|.0 by Sebastian Bergmann and contributors. 72 | 73 | I 74 | 75 | Time: 0 seconds, Memory: 3.95Mb 76 | 77 | There was 1 incomplete test: 78 | 79 | 1) SampleTest::testSomething 80 | Этот тест ещё не реализован. 81 | 82 | /home/sb/SampleTest.php:12 83 | OK, but incomplete or skipped tests! 84 | Tests: 1, Assertions: 1, Incomplete: 1. 85 | 86 | :numref:`incomplete-and-skipped-tests.incomplete-tests.tables.api` 87 | показывает API для маркировки тестов как неполных. 88 | 89 | .. rst-class:: table 90 | .. list-table:: API для неполных тестов 91 | :name: incomplete-and-skipped-tests.incomplete-tests.tables.api 92 | :header-rows: 1 93 | 94 | * - Метод 95 | - Описание 96 | * - ``void markTestIncomplete()`` 97 | - Помечает текущий тест как неполный. 98 | * - ``void markTestIncomplete(string $message)`` 99 | - Помечает текущий тест как неполный, используя ``$message`` в качестве пояснительного сообщения. 100 | 101 | .. _incomplete-and-skipped-tests.skipping-tests: 102 | 103 | Пропущенные тесты 104 | ################# 105 | 106 | Не все тесты могут выполняться в любом окружении. Рассмотрим, например, 107 | уровень абстракции базы данных, содержащий несколько драйверов для различных систем 108 | баз данных, которые он поддерживает. Разумеется, тесты для драйвера MySQL могут 109 | выполняться только в том случае, если доступен сервер MySQL. 110 | 111 | :numref:`incomplete-and-skipped-tests.skipping-tests.examples.DatabaseTest.php` 112 | демонстрирует тестовый класс ``DatabaseTest``, содержащий один тестовый 113 | метод ``testConnection()``. В шаблонном методе ``setUp(): void`` тестового класса мы проверяем, 114 | доступно ли расширение MySQLi, и используем метод ``markTestSkipped()`` 115 | для пропуска этого теста в противном случае. 116 | 117 | .. code-block:: php 118 | :caption: Пропуск теста 119 | :name: incomplete-and-skipped-tests.skipping-tests.examples.DatabaseTest.php 120 | 121 | markTestSkipped( 130 | 'Расширение MySQLi недоступно.' 131 | ); 132 | } 133 | } 134 | 135 | public function testConnection() 136 | { 137 | // ... 138 | } 139 | } 140 | 141 | Пропущенный тест обозначается ``S`` в выводе исполнителя тестов командной строки 142 | PHPUnit, как показано в следующем примере: 143 | 144 | .. parsed-literal:: 145 | 146 | $ phpunit --verbose DatabaseTest 147 | PHPUnit |version|.0 by Sebastian Bergmann and contributors. 148 | 149 | S 150 | 151 | Time: 0 seconds, Memory: 3.95Mb 152 | 153 | There was 1 skipped test: 154 | 155 | 1) DatabaseTest::testConnection 156 | Расширение MySQLi недоступно. 157 | 158 | /home/sb/DatabaseTest.php:9 159 | OK, but incomplete or skipped tests! 160 | Tests: 1, Assertions: 0, Skipped: 1. 161 | 162 | :numref:`incomplete-and-skipped-tests.skipped-tests.tables.api` 163 | показывает API пропущенных тестов. 164 | 165 | .. rst-class:: table 166 | .. list-table:: API для пропущенных тестов 167 | :name: incomplete-and-skipped-tests.skipped-tests.tables.api 168 | :header-rows: 1 169 | 170 | * - Метод 171 | - Описание 172 | * - ``void markTestSkipped()`` 173 | - Отмечает текущий тест как пропущенный. 174 | * - ``void markTestSkipped(string $message)`` 175 | - Отмечает текущий тест как пропущенный, используя ``$message`` в качестве пояснительного сообщения. 176 | 177 | .. _incomplete-and-skipped-tests.skipping-tests-using-requires: 178 | 179 | Пропуск тестов с помощью @requires 180 | ################################## 181 | 182 | В дополнение к вышеперечисленным методам можно также использовать аннотацию 183 | ``@requires``, чтобы предоставить общие предварительные условия для тестового класса. 184 | 185 | .. rst-class:: table 186 | .. list-table:: Возможные примеры использования @requires 187 | :name: incomplete-and-skipped-tests.requires.tables.api 188 | :header-rows: 1 189 | 190 | * - Тип 191 | - Возможные значения 192 | - Примеры 193 | - Дополнительный пример 194 | * - ``PHP`` 195 | - Любой идентификатор версии PHP с необязательным оператором 196 | - @requires PHP 7.1.20 197 | - @requires PHP >= 7.2 198 | * - ``PHPUnit`` 199 | - Любой идентификатор версии PHPUnit с необязательным оператором 200 | - @requires PHPUnit 7.3.1 201 | - @requires PHPUnit < 8 202 | * - ``OS`` 203 | - Регулярное выражения для `PHP_OS `_ 204 | - @requires OS Linux 205 | - @requires OS WIN32|WINNT 206 | * - ``OSFAMILY`` 207 | - Любое `семейство ОС `_ 208 | - @requires OSFAMILY Solaris 209 | - @requires OSFAMILY Windows 210 | * - ``function`` 211 | - Любой корректный параметр для `function_exists `_ 212 | - @requires function imap_open 213 | - @requires function ReflectionMethod::setAccessible 214 | * - ``extension`` 215 | - Имя расширения вместе с необязательным идентификатором версии и необязательным оператором 216 | - @requires extension mysqli 217 | - @requires extension redis >= 2.2.0 218 | 219 | Типы PHP, PHPUnit и extension поддерживают следующие операторы: ``<``, ``<=``, ``>``, ``>=``, ``=``, ``==``, ``!=``, ``<>``. 220 | 221 | .. code-block:: php 222 | :caption: Пропуск тестового класса с использованием @requires 223 | :name: incomplete-and-skipped-tests.skipping-tests.examples.DatabaseClassSkippingTest.php 224 | 225 | = 5.3 235 | */ 236 | public function testConnection() 237 | { 238 | // Тест требует расширения mysqli и PHP >= 5.3 239 | } 240 | 241 | // ... Все остальные тесты требуют расширения mysqli 242 | } 243 | 244 | Если вы используете синтаксис, который не компилируется с определённой версией PHP, 245 | посмотрите на версии, от которых зависят тестовые классы в XML-конфигурации (см :ref:`appendixes.configuration.testsuites`) 246 | -------------------------------------------------------------------------------- /src/index.rst: -------------------------------------------------------------------------------- 1 | ====================== 2 | Руководство по PHPUnit 3 | ====================== 4 | 5 | Документация на русском языке для PHPUnit версии |version|. Обновлено: |today|. 6 | 7 | Себастьян Бергман (Sebastian Bergmann) 8 | 9 | Распространяется под лицензией Creative Commons Attribution 3.0 Unported. 10 | 11 | Перевод документации на русский язык вышел 10 июля 2018 года, поэтому возможны опечатки и неточности. Пожалуйста, если вы найдете их, создайте `ишью `_ или `пулреквест `_ с исправлением. 12 | 13 | Если вы новичок в тестировании, либо желаете поддержать перевод документации, то вы можете купить `книгу рецептов PHPUnit `_. 14 | 15 | Также перед чтением документации ознакомьтесь с `соглашением по переводу `_. 16 | 17 | (с) `Алексей Пыльцын `_. 18 | 19 | Содержание: 20 | 21 | .. toctree:: 22 | :maxdepth: 3 23 | :numbered: 1 24 | 25 | installation 26 | writing-tests-for-phpunit 27 | textui 28 | fixtures 29 | organizing-tests 30 | risky-tests 31 | incomplete-and-skipped-tests 32 | database 33 | test-doubles 34 | code-coverage-analysis 35 | logging 36 | extending-phpunit 37 | 38 | .. toctree:: 39 | :caption: Приложение 40 | :maxdepth: 2 41 | :numbered: 1 42 | 43 | assertions 44 | annotations 45 | configuration 46 | bibliography 47 | copyright 48 | -------------------------------------------------------------------------------- /src/installation.rst: -------------------------------------------------------------------------------- 1 | .. _installation: 2 | 3 | ================= 4 | Установка PHPUnit 5 | ================= 6 | 7 | .. _installation.requirements: 8 | 9 | Требования 10 | ########## 11 | 12 | PHPUnit |version| требует PHP 7.2; настоятельно рекомендуется использовать последнюю версию PHP. 13 | 14 | PHPUnit требует расширений `dom `_ и `json `_, 15 | которые обычно включены по умолчанию. 16 | 17 | PHPUnit также требует расширений 18 | `pcre `_, 19 | `reflection `_ 20 | и `spl `_. 21 | Эти стандартные расширения включены по умолчанию и не могут быть 22 | отключены без внесения изменений в систему сборки PHP и/или в исходный код C. 23 | 24 | Для функциональности отчёта по покрытию кода тестами требуются расширения 25 | `Xdebug `_ (2.7.0 или новее) и 26 | `tokenizer `_. 27 | Генерация XML-отчётов требует расширения 28 | `xmlwriter `_. 29 | 30 | .. _installation.phar: 31 | 32 | PHP Archive (PHAR) 33 | ################## 34 | 35 | Самый простой способ получить PHPUnit — загрузить `PHP Archive (PHAR) `_, 36 | в котором есть все необходимые (а также некоторые необязательные) зависимости PHPUnit, 37 | собранные в одном-единственном файле. 38 | 39 | Расширение `phar `_ обязательно 40 | для использования PHP Archives (PHAR). 41 | 42 | Если расширение `Suhosin `_ включено, 43 | вам необходимо разрешить выполнение PHAR в вашем 44 | ``php.ini``: 45 | 46 | .. parsed-literal:: 47 | 48 | suhosin.executor.include.whitelist = phar 49 | 50 | PHAR с PHPUnit можно использовать сразу после загрузки: 51 | 52 | .. parsed-literal:: 53 | 54 | $ wget https://phar.phpunit.de/phpunit-|version|.phar 55 | $ php phpunit-|version|.phar --version 56 | PHPUnit x.y.z by Sebastian Bergmann and contributors. 57 | 58 | Как правило, далее необходимо сделать исполняемым PHAR-файл: 59 | 60 | .. parsed-literal:: 61 | 62 | $ wget https://phar.phpunit.de/phpunit-|version|.phar 63 | $ chmod +x phpunit-|version|.phar 64 | $ ./phpunit-|version|.phar --version 65 | PHPUnit x.y.z by Sebastian Bergmann and contributors. 66 | 67 | .. _installation.phar.verification: 68 | 69 | Проверка релизов PHPUnit PHAR 70 | ============================= 71 | 72 | Все официальные релизы кода, распространяемые проектом PHPUnit, подписываются 73 | релиз-менеджером. Подписи PGP и хеши SHA256 74 | доступны для проверки на `phar.phpunit.de `_. 75 | 76 | В следующем примере показано, как работает проверка релиза. Мы начинаем 77 | с загрузки :file:`phpunit.phar`, а также его отделённой подписи PGP 78 | :file:`phpunit.phar.asc`: 79 | 80 | .. parsed-literal:: 81 | 82 | $ wget https://phar.phpunit.de/phpunit-|version|.phar 83 | $ wget https://phar.phpunit.de/phpunit-|version|.phar.asc 84 | 85 | Мы хотим проверить PHP Archive (:file:`phpunit-x.y.phar`) PHPUnit 86 | с его отделённой подписью (:file:`phpunit-x.y.phar.asc`): 87 | 88 | .. parsed-literal:: 89 | 90 | $ gpg phpunit-|version|.phar.asc 91 | gpg: Signature made Sat 19 Jul 2014 01:28:02 PM CEST using RSA key ID 6372C20A 92 | gpg: Can't check signature: public key not found 93 | 94 | У нас нет открытого ключа релиз-менеджера (``6372C20A``) 95 | в нашей локальной системе. Для продолжения проверки нам нужно 96 | получить открытый ключ релиз-менеджера с сервера ключей. Один из таких 97 | серверов — это :file:`pgp.uni-mainz.de`. Серверы открытых ключей 98 | связаны между собой, поэтому вы можете подключиться к любому из них. 99 | 100 | .. parsed-literal:: 101 | 102 | $ gpg --keyserver pgp.uni-mainz.de --recv-keys 0x4AA394086372C20A 103 | gpg: requesting key 6372C20A from hkp server pgp.uni-mainz.de 104 | gpg: key 6372C20A: public key "Sebastian Bergmann " imported 105 | gpg: Total number processed: 1 106 | gpg: imported: 1 (RSA: 1) 107 | 108 | Теперь мы получили открытый ключ для сущности, известной как "Sebastian 109 | Bergmann ". Однако, у нас нет способа 110 | проверить, что этот ключ был создан человеком под именем Себастьян Бергман (Sebastian 111 | Bergmann). Но давайте снова попробуем проверить подпись релиза. 112 | 113 | .. parsed-literal:: 114 | 115 | $ gpg phpunit-|version|.phar.asc 116 | gpg: Signature made Sat 19 Jul 2014 01:28:02 PM CEST using RSA key ID 6372C20A 117 | gpg: Good signature from "Sebastian Bergmann " 118 | gpg: aka "Sebastian Bergmann " 119 | gpg: aka "Sebastian Bergmann " 120 | gpg: aka "Sebastian Bergmann " 121 | gpg: aka "Sebastian Bergmann " 122 | gpg: aka "[jpeg image of size 40635]" 123 | gpg: WARNING: This key is not certified with a trusted signature! 124 | gpg: There is no indication that the signature belongs to the owner. 125 | Primary key fingerprint: D840 6D0D 8294 7747 2937 7831 4AA3 9408 6372 C20A 126 | 127 | В данный момент подпись хорошая, но мы не доверяем этому ключу. Хорошая 128 | подпись означает, что файл не был изменён. Однако ввиду характера 129 | криптографии открытого ключа вам необходимо дополнительно 130 | проверить, что ключ ``6372C20A`` был создан настоящим 131 | Себастьяном Бергманом. 132 | 133 | Любой злоумышленник может создать открытый ключ и загрузить его на серверы открытых серверов. 134 | Затем они могут создать вредоносный релиз, подписанный этим поддельным ключом. 135 | После чего, если вы попытаетесь проверить подпись этого испорченного релиза, 136 | проверка будет успешной, потому что ключ не является «реальным» ключом. Поэтому вам 137 | нужно проверить подлинность этого ключа. Однако проверка подлинности открытого ключа 138 | выходит за рамки данной документации. 139 | 140 | Проверка подлинности и целостности PHAR с PHPUnit вручную через GPG утомительна. Вот зачем нужен PHIVE (PHAR Installation and Verification Environment), среда установки и проверки PHAR. Вы можете узнать про PHIVE на `сайте `_. 141 | 142 | .. _installation.composer: 143 | 144 | Composer 145 | ######## 146 | 147 | Просто добавьте (для разработки) зависимость 148 | ``phpunit/phpunit`` в файл 149 | ``composer.json`` вашего проекта, если вы используете `Composer `_ для управления 150 | зависимостями в вашем проекте: 151 | 152 | .. parsed-literal:: 153 | 154 | composer require --dev phpunit/phpunit ^\ |version| 155 | 156 | .. _installation.global: 157 | 158 | Глобальная установка 159 | #################### 160 | 161 | Обратите внимание, что не рекомендуется устанавливать PHPUnit глобально, например, в ``/usr/bin/phpunit`` или 162 | ``/usr/local/bin/phpunit``. 163 | 164 | Вместо этого PHPUnit должен использоваться в виде локальной зависимости проекта. 165 | 166 | Поэтому либо поместите PHAR определённой версии PHPUnit, которая вам нужна, в директорию ``tools`` вашего проекта (который должен управляться с помощью PHIVE), либо укажите конкретную версию PHPUnit в файле ``composer.json`` вашего проекта, если вы используете Composer. 167 | -------------------------------------------------------------------------------- /src/logging.rst: -------------------------------------------------------------------------------- 1 | 2 | 3 | .. _logging: 4 | 5 | =========== 6 | Логирование 7 | =========== 8 | 9 | PHPUnit может создавать несколько типов лог-файлов. 10 | 11 | .. _logging.xml: 12 | 13 | Результаты теста (XML) 14 | ###################### 15 | 16 | Лог-файл XML результатов тестирования, созданный PHPUnit, основан на использовании 17 | `задачи JUnit для Apache Ant `_. 18 | В следующем примере показан лог-файл XML, сгенерированный для тестов в ``ArrayTest``: 19 | 20 | .. code-block:: xml 21 | 22 | 23 | 24 | 31 | 37 | 43 | 44 | 45 | 46 | Следующий лог-файл XML был сгенерирован для двух тестов 47 | ``testFailure`` и ``testError`` тестового класса 48 | ``FailureErrorTest`` и показывает как обозначаются неудачи 49 | и ошибки. 50 | 51 | .. code-block:: xml 52 | 53 | 54 | 55 | 62 | 68 | 69 | testFailure(FailureErrorTest) 70 | Failed asserting that <integer:2> matches expected value <integer:1>. 71 | 72 | /home/sb/FailureErrorTest.php:8 73 | 74 | 75 | 81 | testError(FailureErrorTest) 82 | Exception: 83 | 84 | /home/sb/FailureErrorTest.php:13 85 | 86 | 87 | 88 | 89 | 90 | .. _logging.codecoverage.xml: 91 | 92 | Покрытие кода (XML) 93 | ################### 94 | 95 | Формат XML для логирования информации о покрытии, созданный PHPUnit, 96 | отчасти основан на том, что используется в `Clover `_. 97 | 98 | В следующем примере показан лог-файл XML, сгенерированный для тестов ``BankAccountTest``: 99 | 100 | .. code-block:: xml 101 | 102 | 103 | 104 | 105 | 106 | 107 | 109 | 110 | 111 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 131 | 132 | 135 | 136 | 137 | 138 | .. _logging.codecoverage.text: 139 | 140 | Покрытие кода (TEXT) 141 | #################### 142 | 143 | Человекочитаемое покрытие кода можно выводить в командую строку или текстовый файл. 144 | 145 | Цель этого формата вывода — обеспечить общий обзор покрытия кода тестами при работе 146 | с небольшим набором классов. Для больших проектов этот вывод может быть 147 | полезен для краткого обзора покрытия проектов или при использовании функциональности 148 | с флагом ``--filter``. 149 | При использовании из командной строки, записывая в ``php://stdout``, 150 | будет учитываться настройка ``--colors``. 151 | 152 | Запись в стандартный вывод — это опция по умолчанию при использовании из 153 | командной строки. По умолчанию будут отображаться только файлы, имеющие 154 | хотя бы одну покрытую строку. Это можно изменить через опцию конфигурации 155 | ``showUncoveredFiles``. См. :ref:`appendixes.configuration.logging`. 156 | По умолчанию все файлы и их статус покрытия отображаются в подробном отчёте. 157 | Это можно изменить с помощью конфигурационной опции ``showOnlySummary``. 158 | -------------------------------------------------------------------------------- /src/organizing-tests.rst: -------------------------------------------------------------------------------- 1 | .. _organizing-tests: 2 | 3 | ================== 4 | Организация тестов 5 | ================== 6 | 7 | Одна из целей PHPUnit заключается в том, что 8 | тесты должны быть составными: мы хотим запускать любое количество или комбинацию 9 | тестов вместе, например, все тесты для всего проекта, либо тесты 10 | всех классов компонента, который является частью проекта, либо просто 11 | тесты для одного класса. 12 | 13 | PHPUnit поддерживает различные способы организации тестов и составления их 14 | в набор тестов. В этой главе показаны наиболее часто используемые подходы. 15 | 16 | .. _organizing-tests.filesystem: 17 | 18 | Составление набора тестов с помощью файловой системы 19 | #################################################### 20 | 21 | Возможно, самый простой способ составить набор тестов — это держать все исходные файлы тестов в тестовом каталоге. 22 | PHPUnit может автоматически обнаруживать и запускать тесты путём рекурсивного обхода тестового каталога. 23 | 24 | Давайте посмотрим на набор тестов библиотеки 25 | `sebastianbergmann/money `_. 26 | Просматривая структуру каталогов этого проекта, мы видим, что 27 | классы тестов в каталоге :file:`tests` отражают 28 | структуру пакета и классов тестируемой системы в каталоге 29 | :file:`src`: 30 | 31 | .. code-block:: none 32 | 33 | src tests 34 | `-- Currency.php `-- CurrencyTest.php 35 | `-- IntlFormatter.php `-- IntlFormatterTest.php 36 | `-- Money.php `-- MoneyTest.php 37 | `-- autoload.php 38 | 39 | Для запуска всех тестов библиотеки нам просто нужно указать исполнителю тестов командной строки PHPUnit 40 | каталог с тестами: 41 | 42 | .. parsed-literal:: 43 | 44 | $ phpunit --bootstrap src/autoload.php tests 45 | PHPUnit |version|.0 by Sebastian Bergmann and contributors. 46 | 47 | ................................. 48 | 49 | Time: 636 ms, Memory: 3.50Mb 50 | 51 | OK (33 tests, 52 assertions) 52 | 53 | .. admonition:: Примечание 54 | 55 | Если вы укажите исполнителю тестов командной строки PHPUnit каталог, он 56 | будет искать файлы с маской :file:`*Test.php` 57 | 58 | Для запуска только тестов, объявленных в классе ``CurrencyTest``, находящегося 59 | в файле :file:`tests/CurrencyTest.php`, мы можем использовать 60 | следующую команду: 61 | 62 | .. parsed-literal:: 63 | 64 | $ phpunit --bootstrap src/autoload.php tests/CurrencyTest 65 | PHPUnit |version|.0 by Sebastian Bergmann and contributors. 66 | 67 | ........ 68 | 69 | Time: 280 ms, Memory: 2.75Mb 70 | 71 | OK (8 tests, 8 assertions) 72 | 73 | Для более точного контроля, какие тесты запускать, мы можем использовать опцию ``--filter``: 74 | 75 | .. parsed-literal:: 76 | 77 | $ phpunit --bootstrap src/autoload.php --filter testObjectCanBeConstructedForValidConstructorArgument tests 78 | PHPUnit |version|.0 by Sebastian Bergmann and contributors. 79 | 80 | .. 81 | 82 | Time: 167 ms, Memory: 3.00Mb 83 | 84 | OK (2 test, 2 assertions) 85 | 86 | .. admonition:: Примечание 87 | 88 | Недостатком этого подхода является то, что мы не можем контролировать порядок выполнения тестов. 89 | Это может привести к проблемам с зависимостями теста 90 | см. :ref:`writing-tests-for-phpunit.test-dependencies`. 91 | В следующем разделе вы увидите, как можно явно задать порядок выполнения тестов, 92 | используя конфигурационный XML-файл. 93 | 94 | .. _organizing-tests.xml-configuration: 95 | 96 | Составление набора тестов с помощью конфигурации XML 97 | #################################################### 98 | 99 | XML-файл конфигурации PHPUnit (:ref:`appendixes.configuration`) 100 | также может использоваться для составления набора тестов. 101 | :numref:`organizing-tests.xml-configuration.examples.phpunit.xml` 102 | показывает файл :file:`phpunit.xml` с минимальной настройкой, который добавит все 103 | классы ``*Test``, находящиеся в файлах 104 | :file:`*Test.php`, после рекурсивного обхода каталога :file:`tests`. 105 | 106 | .. code-block:: xml 107 | :caption: Составление набора тестов, используя конфигурацию XML 108 | :name: organizing-tests.xml-configuration.examples.phpunit.xml 109 | 110 | 111 | 112 | 113 | tests 114 | 115 | 116 | 117 | 118 | Если :file:`phpunit.xml` или 119 | :file:`phpunit.xml.dist` (в этом порядке) существует в 120 | текущем рабочем каталоге, а опция ``--configuration`` 121 | *не* используется, то конфигурация будет автоматически 122 | считана из этого файла. 123 | 124 | Порядок выполнения тестов можно сделать явным: 125 | 126 | .. code-block:: xml 127 | :caption: Составление набора тестов, используя конфигурацию XML 128 | :name: organizing-tests.xml-configuration.examples.phpunit.xml2 129 | 130 | 131 | 132 | 133 | tests/IntlFormatterTest.php 134 | tests/MoneyTest.php 135 | tests/CurrencyTest.php 136 | 137 | 138 | 139 | 140 | 141 | -------------------------------------------------------------------------------- /src/risky-tests.rst: -------------------------------------------------------------------------------- 1 | .. _risky-tests: 2 | 3 | ================= 4 | Рискованные тесты 5 | ================= 6 | 7 | При выполнении тестов PHPUnit может проводить дополнительные проверки, описанные ниже. 8 | 9 | .. _risky-tests.useless-tests: 10 | 11 | Бесполезные тесты 12 | ################# 13 | 14 | PHPUnit по умолчанию строг по отношению к тестам, которые ничего не тестируют. Эта проверка 15 | может быть отключена с помощью опции командной строки ``--dont-report-useless-tests`` 16 | или через установку ``beStrictAboutTestsThatDoNotTestAnything="false"`` 17 | в конфигурационном XML-файле PHPUnit. 18 | 19 | Тест, в котором нет утверждений, будет отмечен как рискованный, 20 | если эта проверка включена. Ожидания на поддельных объектах или аннотаций, 21 | таких как @expectedException, считаются за утверждение. 22 | 23 | .. _risky-tests.unintentionally-covered-code: 24 | 25 | Непреднамеренно покрытый код 26 | ############################ 27 | 28 | PHPUnit может быть строгим по отношению к непреднамеренно покрытому коду. Эта проверка 29 | может быть включена с помощью опции командной строки ``--strict-coverage`` 30 | или через установку ``beStrictAboutCoversAnnotation="true"`` в конфигурационном XML-файле PHPUnit. 31 | 32 | Тест с аннотацией @covers, проверяющий код, который не указан при помощи @covers или @uses, 33 | будет отмечен как рискованный, если эта проверка включена. 34 | 35 | .. _risky-tests.output-during-test-execution: 36 | 37 | Вывод во время выполнения теста 38 | ############################### 39 | 40 | PHPUnit может быть строгим по отношению к выводу во время выполнения тестов. Эту проверку можно включить 41 | с помощью опции командной строки ``--disallow-test-output`` или через установку 42 | ``beStrictAboutOutputDuringTests="true"`` в конфигурационном XML-файле PHPUnit. 43 | 44 | Тест, который производит вывод, например, через вызов функции ``print`` либо 45 | в тестовом коде, либо в тестируемом, будет отмечен как рискованный, если 46 | эта проверка включена. 47 | 48 | .. _risky-tests.test-execution-timeout: 49 | 50 | Тайм-аут выполнения теста 51 | ######################### 52 | 53 | Для теста может быть применено ограничение времени выполнения, если установлен пакет 54 | ``PHP_Invoker`` и доступно расширение ``pcntl``. Обеспечение 55 | ограничения времени выполнения может включено с помощью опции командной строки 56 | ``--enforce-time-limit``или через 57 | установку ``enforceTimeLimit="true"`` в конфигурационном XML-файле PHPUnit. 58 | 59 | Тест с аннотацией ``@large`` завершится неудачно, если время его выполнения 60 | превысит 60 секунд. Этот тайм-аут настраивается через атрибут 61 | ``timeoutForLargeTests`` в конфигурационном XML-файле. 62 | 63 | Тест с аннотацией ``@medium`` завершится неудачно, если время его выполнения 64 | займёт больше 10 секунд. Этот тайм-аут настраивается через атрибут 65 | ``timeoutForMediumTests`` в конфигурационном XML-файле. 66 | 67 | Тест с аннотацией ``@small`` завершится неудачно, 68 | если его выполнение займёт более 1 секунды. Этот тайм-аут настраивается через атрибут 69 | ``timeoutForSmallTests`` в конфигурационном XML-файле. 70 | 71 | .. admonition:: Примечание 72 | 73 | Тесты должны явно иметь аннотацию либо ``@small``, 74 | ``@medium`` или ``@large``, чтобы сработало ограничение выполнения теста по времени. 75 | 76 | 77 | .. _risky-tests.global-state-manipulation: 78 | 79 | Манипуляция глобальным состоянием 80 | ################################# 81 | 82 | PHPUnit может быть строгим по отношению к тестам, которые манипулируют глобальным состоянием. Эта проверка 83 | может быть включена с помощью опции командной строки ``--strict-global-state`` или через настройку 84 | ``beStrictAboutChangesToGlobalState="true"`` в конфигурационном XML-файле PHPUnit. 85 | -------------------------------------------------------------------------------- /src/textui.rst: -------------------------------------------------------------------------------- 1 | 2 | 3 | .. _textui: 4 | 5 | =================================== 6 | Исполнитель тестов командной строки 7 | =================================== 8 | 9 | Исполнитель тестов командной строки PHPUnit можно запустить с помощью 10 | команды :file:`phpunit`. Следующий пример показывает, как запускать 11 | тесты с помощью этого инструмента командной строки PHPUnit: 12 | 13 | .. parsed-literal:: 14 | 15 | $ phpunit ArrayTest 16 | PHPUnit |version|.0 by Sebastian Bergmann and contributors. 17 | 18 | .. 19 | 20 | Time: 0 seconds 21 | 22 | OK (2 tests, 2 assertions) 23 | 24 | При вводе команды, как показано выше, исполнитель тестов командной строки PHPUnit будет 25 | искать исходный файл :file:`ArrayTest.php` в текущей рабочей директории, 26 | загрузит его с целью найти в нём класс теста ``ArrayTest``. Затем он выполнит тесты этого класса. 27 | 28 | Для каждого тестового запуска инструмент командной строки PHPUnit выведет один символ для 29 | обозначения прогресса: 30 | 31 | ``.`` 32 | 33 | Выводится, если тест успешно пройден. 34 | 35 | ``F`` 36 | 37 | Выводится, когда утверждение не проходит во время выполнения тестового метода. 38 | 39 | ``E`` 40 | 41 | Выводится, когда возникает ошибка во время запуска тестового метода. 42 | 43 | ``R`` 44 | 45 | Выводится, когда тест был отмечен как рискованный (см. 46 | :ref:`risky-tests`). 47 | 48 | ``S`` 49 | 50 | Выводится, когда тест был пропущен (см. 51 | :ref:`incomplete-and-skipped-tests`). 52 | 53 | ``I`` 54 | 55 | Выводится, когда тест отмечен как незавершённый или ещё не 56 | реализован (см. :ref:`incomplete-and-skipped-tests`). 57 | 58 | PHPUnit различает *неудачные выполнения (failures)* и 59 | *ошибки (errors)*. Неудачное выполнение - это непройденное утверждение PHPUnit, 60 | например вызов ``assertSame()``. 61 | Ошибка — необработанное исключение или ошибка PHP. Иногда 62 | это различие оказывается полезным, поскольку ошибки гораздо легче исправить, чем 63 | неудачные выполнения. В случае большого списка проблем, лучше всего сначала 64 | устранить ошибки и посмотреть, остались ли ещё какие-либо неудачные выполнения, 65 | когда ошибки исправлены. 66 | 67 | .. _textui.clioptions: 68 | 69 | Опции командной строки 70 | ###################### 71 | 72 | Давайте посмотрим на опции командной строки исполнителя тестов в следующем коде: 73 | 74 | .. parsed-literal:: 75 | 76 | $ phpunit --help 77 | PHPUnit |version|.0 by Sebastian Bergmann and contributors. 78 | 79 | Usage: phpunit [options] UnitTest [UnitTest.php] 80 | phpunit [options] 81 | 82 | Code Coverage Options: 83 | 84 | --coverage-clover Generate code coverage report in Clover XML format 85 | --coverage-crap4j Generate code coverage report in Crap4J XML format 86 | --coverage-html Generate code coverage report in HTML format 87 | --coverage-php Export PHP_CodeCoverage object to file 88 | --coverage-text= Generate code coverage report in text format 89 | Default: Standard output 90 | --coverage-xml Generate code coverage report in PHPUnit XML format 91 | --whitelist Whitelist for code coverage analysis 92 | --disable-coverage-ignore Disable annotations for ignoring code coverage 93 | --no-coverage Ignore code coverage configuration 94 | --dump-xdebug-filter Generate script to set Xdebug code coverage filter 95 | 96 | Logging Options: 97 | 98 | --log-junit Log test execution in JUnit XML format to file 99 | --log-teamcity Log test execution in TeamCity format to file 100 | --testdox-html Write agile documentation in HTML format to file 101 | --testdox-text Write agile documentation in Text format to file 102 | --testdox-xml Write agile documentation in XML format to file 103 | --reverse-list Print defects in reverse order 104 | 105 | Test Selection Options: 106 | 107 | --filter Filter which tests to run 108 | --testsuite Filter which testsuite to run 109 | --group ... Only runs tests from the specified group(s) 110 | --exclude-group ... Exclude tests from the specified group(s) 111 | --list-groups List available test groups 112 | --list-suites List available test suites 113 | --list-tests List available tests 114 | --list-tests-xml List available tests in XML format 115 | --test-suffix ... Only search for test in files with specified 116 | suffix(es). Default: Test.php,.phpt 117 | 118 | Test Execution Options: 119 | 120 | --dont-report-useless-tests Do not report tests that do not test anything 121 | --strict-coverage Be strict about @covers annotation usage 122 | --strict-global-state Be strict about changes to global state 123 | --disallow-test-output Be strict about output during tests 124 | --disallow-resource-usage Be strict about resource usage during small tests 125 | --enforce-time-limit Enforce time limit based on test size 126 | --default-time-limit= Timeout in seconds for tests without @small, @medium or @large 127 | --disallow-todo-tests Disallow @todo-annotated tests 128 | 129 | --process-isolation Run each test in a separate PHP process 130 | --globals-backup Backup and restore $GLOBALS for each test 131 | --static-backup Backup and restore static attributes for each test 132 | 133 | --colors= Use colors in output ("never", "auto" or "always") 134 | --columns Number of columns to use for progress output 135 | --columns max Use maximum number of columns for progress output 136 | --stderr Write to STDERR instead of STDOUT 137 | --stop-on-defect Stop execution upon first not-passed test 138 | --stop-on-error Stop execution upon first error 139 | --stop-on-failure Stop execution upon first error or failure 140 | --stop-on-warning Stop execution upon first warning 141 | --stop-on-risky Stop execution upon first risky test 142 | --stop-on-skipped Stop execution upon first skipped test 143 | --stop-on-incomplete Stop execution upon first incomplete test 144 | --fail-on-warning Treat tests with warnings as failures 145 | --fail-on-risky Treat risky tests as failures 146 | -v|--verbose Output more verbose information 147 | --debug Display debugging information 148 | 149 | --loader TestSuiteLoader implementation to use 150 | --repeat Runs the test(s) repeatedly 151 | --teamcity Report test execution progress in TeamCity format 152 | --testdox Report test execution progress in TestDox format 153 | --testdox-group Only include tests from the specified group(s) 154 | --testdox-exclude-group Exclude tests from the specified group(s) 155 | --printer TestListener implementation to use 156 | 157 | --resolve-dependencies Resolve dependencies between tests 158 | --order-by= Run tests in order: default|defects|duration|no-depends|random|reverse 159 | --random-order-seed= Use a specific random seed for random order 160 | --cache-result Write run result to cache to enable ordering tests defects-first 161 | 162 | Configuration Options: 163 | 164 | --prepend A PHP script that is included as early as possible 165 | --bootstrap A PHP script that is included before the tests run 166 | -c|--configuration Read configuration from XML file 167 | --no-configuration Ignore default configuration file (phpunit.xml) 168 | --no-logging Ignore logging configuration 169 | --no-extensions Do not load PHPUnit extensions 170 | --include-path Prepend PHP's include_path with given path(s) 171 | -d key[=value] Sets a php.ini value 172 | --generate-configuration Generate configuration file with suggested settings 173 | --cache-result-file== Specify result cache path and filename 174 | 175 | Miscellaneous Options: 176 | 177 | -h|--help Prints this usage information 178 | --version Prints the version and exits 179 | --atleast-version Checks that version is greater than min and exits 180 | --check-version Check whether PHPUnit is the latest version 181 | 182 | 183 | ``phpunit UnitTest`` 184 | 185 | Запускает тесты, представленные в классе ``UnitTest``. Ожидается, что этот класс будет объявлен 186 | в исходном файле :file:`UnitTest.php`. 187 | 188 | ``UnitTest`` должен быть либо классом, который наследуется от ``PHPUnit\Framework\TestCase``, либо классом 189 | с методом ``public static suite()``, возвращающим объект типа ``PHPUnit\Framework\Test``, например, 190 | экземпляр класса ``PHPUnit\Framework\TestSuite``. 191 | 192 | ``phpunit UnitTest UnitTest.php`` 193 | 194 | Выполняет тесты в классе ``UnitTest``. Ожидается, что этот класс будет 195 | объявлен в указанном исходном файле. 196 | 197 | ``--coverage-clover`` 198 | 199 | Генерирует файл логов в формате XML с информацией о покрытии кода тестами для выполненных тестов. 200 | См. :ref:`logging` для получения более подробной информации. 201 | 202 | Обратите внимание, что данная функциональность доступна только в случае 203 | установленных расширений tokenizer и Xdebug. 204 | 205 | ``--coverage-crap4j`` 206 | 207 | Генерирует отчёт о покрытии кода тестами в формате Crap4j. 208 | См. :ref:`code-coverage-analysis` для получения более подробной информации. 209 | 210 | Обратите внимание, что данная функциональность доступна только в случае 211 | установленных расширений tokenizer и Xdebug. 212 | 213 | ``--coverage-html`` 214 | 215 | Генерирует отчёт о покрытии кода тестами в формате HTML. 216 | См. :ref:`code-coverage-analysis` для получения более подробной информации. 217 | 218 | Обратите внимание, что данная функциональность доступна только в случае 219 | установленных расширений tokenizer и Xdebug. 220 | 221 | ``--coverage-php`` 222 | 223 | Генерирует сериализованный объект класса ``PHP_CodeCoverage`` с 224 | информацией о покрытии кода тестами. 225 | 226 | Обратите внимание, что данная функциональность доступна только в случае 227 | установленных расширений tokenizer и Xdebug. 228 | 229 | ``--coverage-text`` 230 | 231 | Генерирует файл логов или вывод командной строки в человекочитаемом формате 232 | с информацией о покрытии кода тестами для запуска тестов. 233 | См. :ref:`logging` для получения более подробной информации. 234 | 235 | Обратите внимание, что данная функциональность доступна только в случае 236 | установленных расширений tokenizer и Xdebug. 237 | 238 | ``--log-junit`` 239 | 240 | Генерирует файл журнала (logfile) в формате JUnit XML для запуска тестов. 241 | См. :ref:`logging` для получения более подробной информации. 242 | 243 | ``--testdox-html`` и ``--testdox-text`` 244 | 245 | Генерирует agile-документацию в HTML или текстовом формате для запущенных тестов 246 | (см. :ref:`textui.testdox`). 247 | 248 | ``--filter`` 249 | 250 | Выполняются только те тесты, названия которых совпадают с регулярным выражением. 251 | Если регулярное выражение не заключёно в разделители, PHPUnit будет автоматически заключать его в разделители ``/``. 252 | 253 | Имена тестов для совпадения может быть в одном из следующих форматов: 254 | 255 | ``TestNamespace\TestCaseClass::testMethod`` 256 | 257 | Формат имени теста по умолчанию эквивалентен использованию магической константы 258 | ``__METHOD__`` внутри тестового метода. 259 | 260 | ``TestNamespace\TestCaseClass::testMethod with data set #0`` 261 | 262 | Когда в тесте есть провайдер данных, каждая итерация данных 263 | получает текущий индекс, добавленный в концу имени теста по умолчанию. 264 | 265 | ``TestNamespace\TestCaseClass::testMethod with data set "my named data"`` 266 | 267 | Когда в тесте есть провайдер данных, использующий именованные наборы, каждая 268 | итерация данных получает текущее название, добавленное к 269 | концу имени теста по умолчанию. См. 270 | :numref:`textui.examples.TestCaseClass.php` для просмотра примера 271 | именованных наборов данных. 272 | 273 | .. code-block:: php 274 | :caption: Именованные наборы данных 275 | :name: textui.examples.TestCaseClass.php 276 | 277 | assertTrue($data); 290 | } 291 | 292 | public function provider() 293 | { 294 | return [ 295 | 'my named data' => [true], 296 | 'my data' => [true] 297 | ]; 298 | } 299 | } 300 | 301 | ``/path/to/my/test.phpt`` 302 | 303 | Путь в файловой системе к имени теста типа PHPT. 304 | 305 | См. :numref:`textui.examples.filter-patterns` для примеров 306 | корректных шаблонов фильтров. 307 | 308 | .. code-block:: shell 309 | :caption: Примеры шаблонов фильтров 310 | :name: textui.examples.filter-patterns 311 | 312 | --filter 'TestNamespace\\TestCaseClass::testMethod' 313 | --filter 'TestNamespace\\TestCaseClass' 314 | --filter TestNamespace 315 | --filter TestCaseClase 316 | --filter testMethod 317 | --filter '/::testMethod .*"my named data"/' 318 | --filter '/::testMethod .*#5$/' 319 | --filter '/::testMethod .*#(5|6|7)$/' 320 | 321 | См. :numref:`textui.examples.filter-shortcuts` для некоторых 322 | дополнительных сокращений, доступных для сопоставления с 323 | провайдерами данных. 324 | 325 | .. code-block:: shell 326 | :caption: Сокращения фильтра 327 | :name: textui.examples.filter-shortcuts 328 | 329 | --filter 'testMethod#2' 330 | --filter 'testMethod#2-4' 331 | --filter '#2' 332 | --filter '#2-4' 333 | --filter 'testMethod@my named data' 334 | --filter 'testMethod@my.*data' 335 | --filter '@my named data' 336 | --filter '@my.*data' 337 | 338 | ``--testsuite`` 339 | 340 | Выполняется только тот тестовый набор, который совпадает с заданным шаблоном. 341 | 342 | ``--group`` 343 | 344 | Выполняются только тесты из указанных групп. Тест можно добавить в 345 | группу, используя аннотацию ``@group``. 346 | 347 | Аннотации ``@author`` и ``@ticket`` — это псевдонимы для ``@group``, позволяющие фильтровать 348 | тесты по их авторам или по номерам связанных тикетов соответственно. 349 | 350 | ``--exclude-group`` 351 | 352 | Исключить тесты из указанных групп. Тест можно добавить в 353 | группу, используя аннотацию ``@group``. 354 | 355 | ``--list-groups`` 356 | 357 | Список доступных групп тестов. 358 | 359 | ``--test-suffix`` 360 | 361 | Поиск тестовых файлов только с указанными суффиксами. 362 | 363 | ``--dont-report-useless-tests`` 364 | 365 | Не сообщать о тестах, которые ничего не тестируют. См. :ref:`risky-tests` для получения подробной информации. 366 | 367 | ``--strict-coverage`` 368 | 369 | Строгая проверка непроизвольного охвата тестами кода. См. :ref:`risky-tests` для получения подробной информации. 370 | 371 | ``--strict-global-state`` 372 | 373 | Строгая проверка относительно манипуляций с глобальным состоянием. См. :ref:`risky-tests` для получения подробной информации. 374 | 375 | ``--disallow-test-output`` 376 | 377 | Строгая проверка относительно вывода во время выполнения тестов. См. :ref:`risky-tests` для получения подробной информации. 378 | 379 | ``--disallow-todo-tests`` 380 | 381 | Не выполнять тесты, имеющие аннотацию ``@todo`` в своем докблоке. 382 | 383 | ``--enforce-time-limit`` 384 | 385 | Применить ограничение по времени, основываясь на размере теста. См. :ref:`risky-tests` для получения более подробной информации. 386 | 387 | ``--process-isolation`` 388 | 389 | Запускать каждый тест в отдельном процессе PHP. 390 | 391 | ``--no-globals-backup`` 392 | 393 | Не создавать резервную копию и восстанавливать суперглобальный массив ``$GLOBALS``. См. :ref:`fixtures.global-state` 394 | для получения более подробной информации.. 395 | 396 | ``--static-backup`` 397 | 398 | Резервное копирование и восстановление статических атрибутов пользовательских классов. 399 | См. :ref:`fixtures.global-state` для получения более подробной информации. 400 | 401 | ``--colors`` 402 | 403 | Использовать цвета в выводе. 404 | В Windows используйте `ANSICON `_ или `ConEmu `_. 405 | 406 | Существует три возможных значения этой опции: 407 | 408 | - 409 | 410 | ``never``: никогда не отображать цвета в выводе. Это значение по умолчанию, когда не используется опция ``--colors``. 411 | 412 | - 413 | 414 | ``auto``: отображает цвета в выводе, за исключением, если текущий терминал не поддерживает цвета, либо 415 | если вывод не был передан в другую команду или не перенаправлен в файл. 416 | 417 | - 418 | 419 | ``always``: всегда отображать цвета в выводе, даже если текущий терминал не поддерживает цвета, или 420 | когда вывод передаётся в команду или перенаправляется в файл. 421 | 422 | Когда опция ``--colors`` используется без значения, используется ``auto``. 423 | 424 | ``--columns`` 425 | 426 | Определяет количество столбцов для вывода прогресса выполнения тестов. 427 | Если задано значение ``max``, количество столбцов будет максимальным для текущего терминала. 428 | 429 | ``--stderr`` 430 | 431 | Необязательно печатать в поток ``STDERR`` вместо ``STDOUT``. 432 | 433 | ``--stop-on-error`` 434 | 435 | Прекратить выполнение при первой ошибке. 436 | 437 | ``--stop-on-failure`` 438 | 439 | Прекратить выполнение при первой ошибке или неудачном выполнении. 440 | 441 | ``--stop-on-risky`` 442 | 443 | Прекратить выполнение при первом рискованном тесте. 444 | 445 | ``--stop-on-skipped`` 446 | 447 | Прекратить выполнение при первом пропущенном тесте. 448 | 449 | ``--stop-on-incomplete`` 450 | 451 | Прекратить выполнение при первом незавершённом тесте. 452 | 453 | ``--verbose`` 454 | 455 | Выводить более подробную информацию, например, имена незавершённых или пропущенных тестов. 456 | 457 | ``--debug`` 458 | 459 | Выводить отладочную информацию, такую как название теста при его запуске. 460 | 461 | ``--loader`` 462 | 463 | Указывает используемую реализацию загрузчика ``PHPUnit\Runner\TestSuiteLoader`` 464 | 465 | Стандартный загрузчик тестового набора будет искать исходный файл теста 466 | в текущей рабочей директории и в каждой директории, указанной в конфигурационной 467 | PHP-директиве ``include_path``. 468 | Имя класса, такое как ``Project_Package_Class``, сопоставляется с исходным файлом 469 | :file:`Project/Package/Class.php`. 470 | 471 | ``--repeat`` 472 | 473 | Повторять выполнение тестов указанное количество раз. 474 | 475 | ``--testdox`` 476 | 477 | Сообщает о ходе тестирования в формате TestDox (см. :ref:`textui.testdox`). 478 | 479 | ``--printer`` 480 | 481 | Указывает используемую реализацию форматирования вывода. 482 | Этот класс должен наследоваться от ``PHPUnit\Util\Printer`` 483 | и реализовывать интерфейс ``PHPUnit\Framework\TestListener``. 484 | 485 | ``--bootstrap`` 486 | 487 | «Загрузочный» («bootstrap») файл PHP, который будет запускаться перед выполнением тестов. 488 | 489 | ``--configuration``, ``-c`` 490 | 491 | Прочитать конфигурацию из XML-файла. 492 | См. :ref:`appendixes.configuration` для получения более подробной информации. 493 | 494 | Если файл :file:`phpunit.xml` или 495 | :file:`phpunit.xml.dist` (в таком порядке) существует в 496 | текущей рабочей директории, а опция ``--configuration`` 497 | *не* используется, конфигурация будет автоматически прочитана 498 | из этого файла. 499 | 500 | Если директория указана и файл 501 | :file:`phpunit.xml` или :file:`phpunit.xml.dist` (в таком порядке) 502 | существует в этой директории, конфигурация будет автоматически загружена 503 | из этого файла. 504 | 505 | ``--no-configuration`` 506 | 507 | Игнорировать :file:`phpunit.xml` и 508 | :file:`phpunit.xml.dist` из текущей рабочей 509 | директории. 510 | 511 | ``--include-path`` 512 | 513 | Добавить в PHP-опцию ``include_path`` указанные пути. 514 | 515 | ``-d`` 516 | 517 | Устанавливает значение заданной опции конфигурации PHP. 518 | 519 | .. admonition:: Примечание 520 | 521 | Обратите внимание, что с версии 4.8 параметры могут быть указаны после аргументов. 522 | 523 | .. _textui.testdox: 524 | 525 | TestDox 526 | ####### 527 | 528 | Функциональность TestDox PHPUnit просматривает тестовый класс и все названия его 529 | тестовых методов, и преобразует их из имён PHP в стиле написания CamelCase (или snake_case) в предложения: 530 | ``testBalanceIsInitiallyZero()`` (или ``test_balance_is_initially_zero()``) становится «Balance is 531 | initially zero». Если есть несколько тестовых методов, названия которых отличаются 532 | только одной или более цифрой на конце, например 533 | ``testBalanceCannotBecomeNegative()`` и 534 | ``testBalanceCannotBecomeNegative2()``, предложение 535 | «Balance cannot become negative» появится только один раз, при условии, что 536 | все эти тесты прошли успешно. 537 | 538 | Давайте посмотрим aglie-документацию, сгенерированную для класса ``BankAccount``: 539 | 540 | .. parsed-literal:: 541 | 542 | $ phpunit --testdox BankAccountTest 543 | PHPUnit |version|.0 by Sebastian Bergmann and contributors. 544 | 545 | BankAccount 546 | ✔ Balance is initially zero 547 | ✔ Balance cannot become negative 548 | 549 | В качестве альтернативы, aglie-документация может быть сгенерирована в HTML или 550 | текстовом формате и записана в файл, используя аргументы ``--testdox-html`` 551 | и ``--testdox-text``. 552 | 553 | Документация Agile может использоваться для документирования предположений, которые вы делаете относительно внешних пакетов, используемых в проекте. 554 | Когда вы используете внешний пакет, вы подвержены рискам, что пакет не будет работать так, как ожидалось, то есть он изменит своё поведение, 555 | а будущие версии пакета изменятся завуалированным способом, тем самым ломая ваш код, даже не подозревая об этом. 556 | Вы можете снизить эти риски, путём написания каждый раз теста, когда вы делаете предположение. 557 | Если тест проходит, значит ваше предположение верно. 558 | Если вы будете документировать все свои предположения с помощью тестов, новые версии внешнего пакета не будут вызывать беспокойства: 559 | если тесты проходят, то система должна продолжать работать. 560 | 561 | -------------------------------------------------------------------------------- /src/writing-tests-for-phpunit.rst: -------------------------------------------------------------------------------- 1 | .. _writing-tests-for-phpunit: 2 | 3 | =========================== 4 | Написание тестов на PHPUnit 5 | =========================== 6 | 7 | :numref:`writing-tests-for-phpunit.examples.StackTest.php` показывает, 8 | как мы можем писать тесты, используя PHPUnit, которые выполняют операции с массивом PHP. 9 | В этом примере представлены основные соглашения и шаги для написания тестов с помощью PHPUnit: 10 | 11 | #. 12 | 13 | Тесты для класса ``Class`` содержатся в классе ``ClassTest``. 14 | 15 | #. 16 | 17 | ``ClassTest`` наследуется (чаще всего) от ``PHPUnit\Framework\TestCase``. 18 | 19 | #. 20 | 21 | Тесты — общедоступные методы с именами ``test*``. 22 | 23 | Кроме того, вы можете использовать аннотацию ``@test`` в докблоке метода, чтобы пометить его как метод тестирования. 24 | 25 | #. 26 | 27 | Внутри тестовых методов для проверки того, соответствует ли фактическое значение ожидаемому используются методы-утверждения, такие как ``assertSame()`` (см. :ref:`appendixes.assertions`). 28 | 29 | .. code-block:: php 30 | :caption: Тестирование операций с массивами с использованием PHPUnit 31 | :name: writing-tests-for-phpunit.examples.StackTest.php 32 | 33 | assertSame(0, count($stack)); 42 | 43 | array_push($stack, 'foo'); 44 | $this->assertSame('foo', $stack[count($stack)-1]); 45 | $this->assertSame(1, count($stack)); 46 | 47 | $this->assertSame('foo', array_pop($stack)); 48 | $this->assertSame(0, count($stack)); 49 | } 50 | } 51 | 52 | | 53 | *Мартин Фаулер (Martin Fowler)*: 54 | 55 | Всякий раз, когда возникает соблазн что-то распечатать, используя 56 | ``print``, или написать отладочное выражение, напишите тест 57 | вместо этого. 58 | 59 | .. _writing-tests-for-phpunit.test-dependencies: 60 | 61 | Зависимости тестов 62 | ################## 63 | 64 | *Адриан Кун (Adrian Kuhn) и другие*: 65 | 66 | Модульные тесты главным образом пишутся в качестве хорошей практики, 67 | помогающей разработчикам выявлять и исправлять баги, проводить рефакторинг кода 68 | и служить в качестве документации для тестируемого программного модуля (программы). 69 | Для достижения этих преимуществ модульные тесты 70 | в идеале должны охватывать все возможные пути исполнения программы. Один модульный тест 71 | обычно покрывает один конкретный путь в одной функции или методе. Однако тестовые методы 72 | необязательно должны быть инкапсулированными и независимыми. Часто 73 | существуют неявные зависимости между тестовыми методами, скрытые в сценарии 74 | реализации теста. 75 | 76 | PHPUnit поддерживает объявление явных зависимостей между тестовыми методами. 77 | Эти зависимости не определяют порядок, в котором должны выполняться тестовые методы, 78 | но они позволяют возвращать экземпляр (данные) фикстуры теста, созданные поставщиком (producer) 79 | для передачи его зависимым потребителям (consumers). 80 | 81 | - 82 | 83 | Поставщик — тестовый метод, который предоставляет свой тестируемый модуль в качестве возвращаемого значения. 84 | 85 | - 86 | 87 | Потребитель — тестовый метод, который зависит от одного или более поставщиков и их возвращаемых значений. 88 | 89 | :numref:`writing-tests-for-phpunit.examples.StackTest2.php` показывает, 90 | как использовать аннотацию ``@depends`` для представления зависимостей 91 | между тестовыми методами. 92 | 93 | .. code-block:: php 94 | :caption: Использование аннотации ``@depends`` для описания зависимостей 95 | :name: writing-tests-for-phpunit.examples.StackTest2.php 96 | 97 | assertEmpty($stack); 106 | 107 | return $stack; 108 | } 109 | 110 | /** 111 | * @depends testEmpty 112 | */ 113 | public function testPush(array $stack) 114 | { 115 | array_push($stack, 'foo'); 116 | $this->assertSame('foo', $stack[count($stack)-1]); 117 | $this->assertNotEmpty($stack); 118 | 119 | return $stack; 120 | } 121 | 122 | /** 123 | * @depends testPush 124 | */ 125 | public function testPop(array $stack) 126 | { 127 | $this->assertSame('foo', array_pop($stack)); 128 | $this->assertEmpty($stack); 129 | } 130 | } 131 | 132 | В вышеприведённом примере первый тест, ``testEmpty()``, 133 | создаёт новый массив и утверждает, что он пуст. Затем тест возвращает фикстуру 134 | в качестве результата. Второй тест, ``testPush()``, 135 | зависит от ``testEmpty()`` и ему передаётся результат этого зависимого теста 136 | в качестве аргумента. Наконец, ``testPop()`` 137 | зависит от ``testPush()``. 138 | 139 | .. admonition:: Примечание 140 | 141 | Возвращаемое значение, предоставленное поставщиком, по умолчанию передаётся потребителям «как есть». 142 | Это означает, что когда поставщик возвращает объект, ссылка на этот объект передаётся потребителям. 143 | Вместо ссылки возможна, либо (а) (глубокая) копия через ``@depends clone`` или (б) 144 | (поверхностная) копия (на основе ключевого слова PHP ``clone``) через 145 | ``@depends shallowClone``. 146 | 147 | Чтобы быстро находить дефекты, нам нужно сосредоточить внимание 148 | на соответствующих неудачных тестах. Вот почему PHPUnit пропускает выполнение теста, 149 | когда зависимый тест (тест с зависимостью) провалился (не прошёл). 150 | Это помогает локализовать дефекты за счёт использования зависимостей между тестами, как это показано 151 | в :numref:`writing-tests-for-phpunit.examples.DependencyFailureTest.php`. 152 | 153 | .. code-block:: php 154 | :caption: Использование зависимостей между тестами 155 | :name: writing-tests-for-phpunit.examples.DependencyFailureTest.php 156 | 157 | assertTrue(false); 165 | } 166 | 167 | /** 168 | * @depends testOne 169 | */ 170 | public function testTwo() 171 | { 172 | } 173 | } 174 | 175 | .. parsed-literal:: 176 | 177 | $ phpunit --verbose DependencyFailureTest 178 | PHPUnit |version|.0 by Sebastian Bergmann and contributors. 179 | 180 | FS 181 | 182 | Time: 0 seconds, Memory: 5.00Mb 183 | 184 | There was 1 failure: 185 | 186 | 1) DependencyFailureTest::testOne 187 | Failed asserting that false is true. 188 | 189 | /home/sb/DependencyFailureTest.php:6 190 | 191 | There was 1 skipped test: 192 | 193 | 1) DependencyFailureTest::testTwo 194 | This test depends on "DependencyFailureTest::testOne" to pass. 195 | 196 | FAILURES! 197 | Tests: 1, Assertions: 1, Failures: 1, Skipped: 1. 198 | 199 | У теста может быть несколько аннотаций ``@depends``. 200 | PHPUnit не изменяет порядок выполнения тестов, поэтому вы должны убедиться, 201 | что все зависимости действительно могут быть выполнены до запуска теста. 202 | 203 | Тест, содержащий более одной аннотации ``@depends``, 204 | получит фикстуру от первого поставщика в качестве первого аргумента, фикстуру 205 | от второго поставщика вторым аргументом и т.д. 206 | См. :numref:`writing-tests-for-phpunit.examples.MultipleDependencies.php` 207 | 208 | .. code-block:: php 209 | :caption: Тест с несколькими зависимостями 210 | :name: writing-tests-for-phpunit.examples.MultipleDependencies.php 211 | 212 | assertTrue(true); 220 | return 'first'; 221 | } 222 | 223 | public function testProducerSecond() 224 | { 225 | $this->assertTrue(true); 226 | return 'second'; 227 | } 228 | 229 | /** 230 | * @depends testProducerFirst 231 | * @depends testProducerSecond 232 | */ 233 | public function testConsumer($a, $b) 234 | { 235 | $this->assertSame('first', $a); 236 | $this->assertSame('second', $b); 237 | } 238 | } 239 | 240 | .. parsed-literal:: 241 | 242 | $ phpunit --verbose MultipleDependenciesTest 243 | PHPUnit |version|.0 by Sebastian Bergmann and contributors. 244 | 245 | ... 246 | 247 | Time: 0 seconds, Memory: 3.25Mb 248 | 249 | OK (3 tests, 4 assertions) 250 | 251 | .. _writing-tests-for-phpunit.data-providers: 252 | 253 | Провайдеры данных 254 | ################# 255 | 256 | Тестовый метод может принимать произвольное количество аргументов. Эти аргументы могут быть 257 | предоставлены одним или несколькими методами провайдеров данных (data provider) 258 | (см. ``additionProvider()`` в :numref:`writing-tests-for-phpunit.data-providers.examples.DataTest.php`). 259 | Метод, который будет использован в качестве провайдера данных, обозначается с помощью аннотации ``@dataProvider``. 260 | 261 | Метод провайдера данных должен быть объявлен как ``public`` и возвращать либо 262 | массив массивов, либо объект, реализующий интерфейс ``Iterator`` 263 | и возвращать массив при каждой итерации. Для каждого массива, являющегося 264 | частью коллекции, будет вызываться тестовый метод с элементами массива в качестве его аргументов. 265 | 266 | .. code-block:: php 267 | :caption: Использование провайдера данных, который возвращает массив массивов 268 | :name: writing-tests-for-phpunit.data-providers.examples.DataTest.php 269 | 270 | assertSame($expected, $a + $b); 281 | } 282 | 283 | public function additionProvider() 284 | { 285 | return [ 286 | [0, 0, 0], 287 | [0, 1, 1], 288 | [1, 0, 1], 289 | [1, 1, 3] 290 | ]; 291 | } 292 | } 293 | 294 | .. parsed-literal:: 295 | 296 | $ phpunit DataTest 297 | PHPUnit |version|.0 by Sebastian Bergmann and contributors. 298 | 299 | ...F 300 | 301 | Time: 0 seconds, Memory: 5.75Mb 302 | 303 | There was 1 failure: 304 | 305 | 1) DataTest::testAdd with data set #3 (1, 1, 3) 306 | Failed asserting that 2 is identical to 3. 307 | 308 | /home/sb/DataTest.php:9 309 | 310 | FAILURES! 311 | Tests: 4, Assertions: 4, Failures: 1. 312 | 313 | При использовании большого количества наборов данных полезно указывать для каждого из них строковый ключ, 314 | вместо использования числового ключа по умолчанию. 315 | Вывод станет более подробным, так как он будет содержать имя набора данных, не прошедший тест. 316 | 317 | .. code-block:: php 318 | :caption: Использование провайдера данных с наборами данных 319 | :name: writing-tests-for-phpunit.data-providers.examples.DataTest1.php 320 | 321 | assertSame($expected, $a + $b); 332 | } 333 | 334 | public function additionProvider() 335 | { 336 | return [ 337 | 'adding zeros' => [0, 0, 0], 338 | 'zero plus one' => [0, 1, 1], 339 | 'one plus zero' => [1, 0, 1], 340 | 'one plus one' => [1, 1, 3] 341 | ]; 342 | } 343 | } 344 | 345 | .. parsed-literal:: 346 | 347 | $ phpunit DataTest 348 | PHPUnit |version|.0 by Sebastian Bergmann and contributors. 349 | 350 | ...F 351 | 352 | Time: 0 seconds, Memory: 5.75Mb 353 | 354 | There was 1 failure: 355 | 356 | 1) DataTest::testAdd with data set "one plus one" (1, 1, 3) 357 | Failed asserting that 2 is identical to 3. 358 | 359 | /home/sb/DataTest.php:9 360 | 361 | FAILURES! 362 | Tests: 4, Assertions: 4, Failures: 1. 363 | 364 | .. code-block:: php 365 | :caption: Использование провайдера данных, который возвращает объект Iterator 366 | :name: writing-tests-for-phpunit.data-providers.examples.DataTest2.php 367 | 368 | assertSame($expected, $a + $b); 381 | } 382 | 383 | public function additionProvider() 384 | { 385 | return new CsvFileIterator('data.csv'); 386 | } 387 | } 388 | 389 | .. parsed-literal:: 390 | 391 | $ phpunit DataTest 392 | PHPUnit |version|.0 by Sebastian Bergmann and contributors. 393 | 394 | ...F 395 | 396 | Time: 0 seconds, Memory: 5.75Mb 397 | 398 | There was 1 failure: 399 | 400 | 1) DataTest::testAdd with data set #3 ('1', '1', '3') 401 | Failed asserting that 2 is identical to 3. 402 | 403 | /home/sb/DataTest.php:11 404 | 405 | FAILURES! 406 | Tests: 4, Assertions: 4, Failures: 1. 407 | 408 | .. code-block:: php 409 | :caption: Класс CsvFileIterator 410 | :name: writing-tests-for-phpunit.data-providers.examples.CsvFileIterator.php 411 | 412 | file = fopen($file, 'r'); 424 | } 425 | 426 | public function __destruct() 427 | { 428 | fclose($this->file); 429 | } 430 | 431 | public function rewind() 432 | { 433 | rewind($this->file); 434 | $this->current = fgetcsv($this->file); 435 | $this->key = 0; 436 | } 437 | 438 | public function valid() 439 | { 440 | return !feof($this->file); 441 | } 442 | 443 | public function key() 444 | { 445 | return $this->key; 446 | } 447 | 448 | public function current() 449 | { 450 | return $this->current; 451 | } 452 | 453 | public function next() 454 | { 455 | $this->current = fgetcsv($this->file); 456 | $this->key++; 457 | } 458 | } 459 | 460 | Когда тест получает входные данные как из метода с ``@dataProvider``, 461 | так и от одного или более методов с аннотацией ``@depends``, 462 | первыми будут приходить аргументы от провайдера данных, а после от зависимых тестов. 463 | Аргументы от зависимых тестов будут одинаковыми для каждого набора данных. 464 | См. :numref:`writing-tests-for-phpunit.data-providers.examples.DependencyAndDataProviderCombo.php` 465 | 466 | .. code-block:: php 467 | :caption: Комбинация @depends и @dataProvider в одном тесте 468 | :name: writing-tests-for-phpunit.data-providers.examples.DependencyAndDataProviderCombo.php 469 | 470 | assertTrue(true); 483 | return 'first'; 484 | } 485 | 486 | public function testProducerSecond() 487 | { 488 | $this->assertTrue(true); 489 | return 'second'; 490 | } 491 | 492 | /** 493 | * @depends testProducerFirst 494 | * @depends testProducerSecond 495 | * @dataProvider provider 496 | */ 497 | public function testConsumer() 498 | { 499 | $this->assertSame( 500 | ['provider1', 'first', 'second'], 501 | func_get_args() 502 | ); 503 | } 504 | } 505 | 506 | .. parsed-literal:: 507 | 508 | $ phpunit --verbose DependencyAndDataProviderComboTest 509 | PHPUnit |version|.0 by Sebastian Bergmann and contributors. 510 | 511 | ...F 512 | 513 | Time: 0 seconds, Memory: 3.50Mb 514 | 515 | There was 1 failure: 516 | 517 | 1) DependencyAndDataProviderComboTest::testConsumer with data set #1 ('provider2') 518 | Failed asserting that two arrays are identical. 519 | --- Expected 520 | +++ Actual 521 | @@ @@ 522 | Array &0 ( 523 | - 0 => 'provider1' 524 | + 0 => 'provider2' 525 | 1 => 'first' 526 | 2 => 'second' 527 | ) 528 | /home/sb/DependencyAndDataProviderComboTest.php:32 529 | 530 | FAILURES! 531 | Tests: 4, Assertions: 4, Failures: 1. 532 | 533 | .. code-block:: php 534 | :caption: Использование нескольких провайдеров данных для одного теста 535 | :name: writing-tests-for-phpunit.data-providers.examples.DataTest.php 536 | 537 | assertSame($expected, $a + $b); 549 | } 550 | 551 | public function additionWithNonNegativeNumbersProvider() 552 | { 553 | return [ 554 | [0, 1, 1], 555 | [1, 0, 1], 556 | [1, 1, 3] 557 | ]; 558 | } 559 | 560 | public function additionWithNegativeNumbersProvider() 561 | { 562 | return [ 563 | [-1, 1, 0], 564 | [-1, -1, -2], 565 | [1, -1, 0] 566 | ]; 567 | } 568 | } 569 | 570 | .. parsed-literal:: 571 | $ phpunit DataTest 572 | PHPUnit |version|.0 by Sebastian Bergmann and contributors. 573 | 574 | ..F... 6 / 6 (100%) 575 | 576 | Time: 0 seconds, Memory: 5.75Mb 577 | 578 | There was 1 failure: 579 | 580 | 1) DataTest::testAdd with data set #3 (1, 1, 3) 581 | Failed asserting that 2 is identical to 3. 582 | 583 | /home/sb/DataTest.php:12 584 | 585 | FAILURES! 586 | Tests: 6, Assertions: 6, Failures: 1. 587 | 588 | .. admonition:: Примечание 589 | 590 | Когда тест зависит от теста, который использует провайдеры данных, зависимый 591 | тест начнёт выполняться, когда тест, от которого тот зависит, успешно выполнится 592 | хотя бы для одного набора данных. Результат теста, который использует провайдеры данных, 593 | не может быть внедрён в зависимый тест. 594 | 595 | .. admonition:: Примечание 596 | 597 | Все провайдеры данных выполняются как перед вызовом статического метода ``setUpBeforeClass(): void``, 598 | так и перед первым вызовом метода ``setUp(): void``. 599 | Из-за этого вы не сможете получить доступ к переменным, определённым внутри провайдера данных. 600 | Это требуется для того, чтобы PHPUnit смог вычислить общее количество тестов. 601 | 602 | .. _writing-tests-for-phpunit.exceptions: 603 | 604 | Тестирование исключений 605 | ####################### 606 | 607 | :numref:`writing-tests-for-phpunit.exceptions.examples.ExceptionTest.php` 608 | показывает, как использовать метод ``expectException()`` для проверки того, 609 | было ли выброшено исключение в тестируемом коде. 610 | 611 | .. code-block:: php 612 | :caption: Использование метода expectException() 613 | :name: writing-tests-for-phpunit.exceptions.examples.ExceptionTest.php 614 | 615 | expectException(InvalidArgumentException::class); 623 | } 624 | } 625 | 626 | .. parsed-literal:: 627 | 628 | $ phpunit ExceptionTest 629 | PHPUnit |version|.0 by Sebastian Bergmann and contributors. 630 | 631 | F 632 | 633 | Time: 0 seconds, Memory: 4.75Mb 634 | 635 | There was 1 failure: 636 | 637 | 1) ExceptionTest::testException 638 | Failed asserting that exception of type "InvalidArgumentException" is thrown. 639 | 640 | FAILURES! 641 | Tests: 1, Assertions: 1, Failures: 1. 642 | 643 | В дополнение к методу ``expectException()`` существуют методы 644 | ``expectExceptionCode()``, 645 | ``expectExceptionMessage()`` и 646 | ``expectExceptionMessageRegExp()``, чтобы установить ожидания для 647 | исключений, вызванных тестируемым кодом. 648 | 649 | .. admonition:: Примечание 650 | 651 | Обратите внимание, что метод expectExceptionMessage, утверждает, 652 | что фактическое сообщение в ``$actual`` содержит ожидаемое сообщение ``$expected`` 653 | без выполнения точного сравнения строк. 654 | 655 | Кроме того, вы можете использовать аннотации ``@expectedException``, 656 | ``@expectedExceptionCode``, 657 | ``@expectedExceptionMessage`` и 658 | ``@expectedExceptionMessageRegExp``, чтобы установить 659 | ожидания для исключений, вызванных тестируемым кодом. 660 | :numref:`writing-tests-for-phpunit.exceptions.examples.ExceptionTest2.php` 661 | демонстрирует пример использования. 662 | 663 | .. code-block:: php 664 | :caption: Использование аннотации @expectedException 665 | :name: writing-tests-for-phpunit.exceptions.examples.ExceptionTest2.php 666 | 667 | assertFalse(@$writer->write('/is-not-writeable/file', 'stuff')); 772 | } 773 | } 774 | 775 | class FileWriter 776 | { 777 | public function write($file, $content) { 778 | $file = fopen($file, 'w'); 779 | 780 | if ($file == false) { 781 | return false; 782 | } 783 | 784 | // ... 785 | } 786 | } 787 | 788 | .. parsed-literal:: 789 | 790 | $ phpunit ErrorSuppressionTest 791 | PHPUnit |version|.0 by Sebastian Bergmann and contributors. 792 | 793 | . 794 | 795 | Time: 1 seconds, Memory: 5.25Mb 796 | 797 | OK (1 test, 1 assertion) 798 | 799 | Без подавления ошибки тест завершится неудачей с сообщением 800 | ``fopen(/is-not-writeable/file): failed to open stream: No such file or directory``. 801 | 802 | .. _writing-tests-for-phpunit.output: 803 | 804 | Тестирование вывода 805 | ################### 806 | 807 | Иногда вам нужно проверить, что выполнение метода, например, 808 | генерирует ожидаемый вывод (к примеру, через ``echo`` или 809 | ``print``). Класс 810 | ``PHPUnit\Framework\TestCase`` использует возможности 811 | `буферизации вывода `_ PHP 812 | для предоставления такой функциональности. 813 | 814 | :numref:`writing-tests-for-phpunit.output.examples.OutputTest.php` 815 | показывает, как использовать метод ``expectOutputString()`` для установки 816 | ожидаемого вывода. Если этот ожидаемый вывод не будет сгенерирован, тест 817 | будет считаться проваленным. 818 | 819 | .. code-block:: php 820 | :caption: Тестирование вывода функции или метода 821 | :name: writing-tests-for-phpunit.output.examples.OutputTest.php 822 | 823 | expectOutputString('foo'); 831 | print 'foo'; 832 | } 833 | 834 | public function testExpectBarActualBaz() 835 | { 836 | $this->expectOutputString('bar'); 837 | print 'baz'; 838 | } 839 | } 840 | 841 | .. parsed-literal:: 842 | 843 | $ phpunit OutputTest 844 | PHPUnit |version|.0 by Sebastian Bergmann and contributors. 845 | 846 | .F 847 | 848 | Time: 0 seconds, Memory: 5.75Mb 849 | 850 | There was 1 failure: 851 | 852 | 1) OutputTest::testExpectBarActualBaz 853 | Failed asserting that two strings are equal. 854 | --- Expected 855 | +++ Actual 856 | @@ @@ 857 | -'bar' 858 | +'baz' 859 | 860 | FAILURES! 861 | Tests: 2, Assertions: 2, Failures: 1. 862 | 863 | :numref:`writing-tests-for-phpunit.output.tables.api` 864 | показывает доступные методы для тестирования вывода 865 | 866 | .. rst-class:: table 867 | .. list-table:: Методы для тестирования вывода 868 | :name: writing-tests-for-phpunit.output.tables.api 869 | :header-rows: 1 870 | 871 | * - Метод 872 | - Описание 873 | * - ``void expectOutputRegex(string $regularExpression)`` 874 | - Проверить, что вывод соответствует регулярному выражению ``$regularExpression``. 875 | * - ``void expectOutputString(string $expectedString)`` 876 | - Проверить, что вывод соответствует строке ``$expectedString``. 877 | * - ``bool setOutputCallback(callable $callback)`` 878 | - Устанавливает функцию обратного вызова, используемую, например, для нормализации фактического вывода. 879 | * - ``string getActualOutput()`` 880 | - Получить фактический вывод. 881 | 882 | .. admonition:: Примечание 883 | 884 | Тест, который генерирует вывод, не будет работать в строгом режиме. 885 | 886 | .. _writing-tests-for-phpunit.error-output: 887 | 888 | Вывод ошибки 889 | ############ 890 | 891 | Всякий раз, когда тест терпит неудачу, PHPUnit изо всех сил пытается предоставить вам 892 | максимально возможный контекст, который может помочь выявить проблему. 893 | 894 | .. code-block:: php 895 | :caption: Вывод ошибки, сгенерированный при неудачном сравнении массива 896 | :name: writing-tests-for-phpunit.error-output.examples.ArrayDiffTest.php 897 | 898 | assertSame( 906 | [1, 2, 3, 4, 5, 6], 907 | [1, 2, 33, 4, 5, 6] 908 | ); 909 | } 910 | } 911 | 912 | .. parsed-literal:: 913 | 914 | $ phpunit ArrayDiffTest 915 | PHPUnit |version|.0 by Sebastian Bergmann and contributors. 916 | 917 | F 918 | 919 | Time: 0 seconds, Memory: 5.25Mb 920 | 921 | There was 1 failure: 922 | 923 | 1) ArrayDiffTest::testEquality 924 | Failed asserting that two arrays are identical. 925 | --- Expected 926 | +++ Actual 927 | @@ @@ 928 | Array ( 929 | 0 => 1 930 | 1 => 2 931 | - 2 => 3 932 | + 2 => 33 933 | 3 => 4 934 | 4 => 5 935 | 5 => 6 936 | ) 937 | 938 | /home/sb/ArrayDiffTest.php:7 939 | 940 | FAILURES! 941 | Tests: 1, Assertions: 1, Failures: 1. 942 | 943 | В этом примере только одно из значений массива отличается, а остальные значения показаны, 944 | для обеспечения контекста, где произошла ошибка. 945 | 946 | Когда сгенерированный вывод будет длинным для чтения, PHPUnit разделит его 947 | и отобразит несколько строк контекста вокруг каждого несоответствия (разницы). 948 | 949 | .. code-block:: php 950 | :caption: Вывод ошибки при неудачном сравнении длинного массива 951 | :name: writing-tests-for-phpunit.error-output.examples.LongArrayDiffTest.php 952 | 953 | assertSame( 961 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6], 962 | [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 33, 4, 5, 6] 963 | ); 964 | } 965 | } 966 | 967 | .. parsed-literal:: 968 | 969 | $ phpunit LongArrayDiffTest 970 | PHPUnit |version|.0 by Sebastian Bergmann and contributors. 971 | 972 | F 973 | 974 | Time: 0 seconds, Memory: 5.25Mb 975 | 976 | There was 1 failure: 977 | 978 | 1) LongArrayDiffTest::testEquality 979 | Failed asserting that two arrays are identical. 980 | --- Expected 981 | +++ Actual 982 | @@ @@ 983 | 11 => 0 984 | 12 => 1 985 | 13 => 2 986 | - 14 => 3 987 | + 14 => 33 988 | 15 => 4 989 | 16 => 5 990 | 17 => 6 991 | ) 992 | 993 | /home/sb/LongArrayDiffTest.php:7 994 | 995 | FAILURES! 996 | Tests: 1, Assertions: 1, Failures: 1. 997 | 998 | .. _writing-tests-for-phpunit.error-output.edge-cases: 999 | 1000 | Крайние случаи 1001 | ============== 1002 | 1003 | Когда сравнение терпит неудачу, PHPUnit создаёт текстовые представления 1004 | входных значений и сравнивает их. Благодаря этой реализации результат сравнения изменений (формат diff) 1005 | может показать больше проблем, чем существуют на самом деле. 1006 | 1007 | Это происходит только при использовании ``assertEquals()`` или 'слабых' ('weak') функций 1008 | сравнения массивов или объектов. 1009 | 1010 | .. code-block:: php 1011 | :caption: Крайний случай в генерации сравнения при использовании слабого сравнения 1012 | :name: writing-tests-for-phpunit.error-output.edge-cases.examples.ArrayWeakComparisonTest.php 1013 | 1014 | assertEquals( 1022 | [1, 2, 3, 4, 5, 6], 1023 | ['1', 2, 33, 4, 5, 6] 1024 | ); 1025 | } 1026 | } 1027 | 1028 | .. parsed-literal:: 1029 | 1030 | $ phpunit ArrayWeakComparisonTest 1031 | PHPUnit |version|.0 by Sebastian Bergmann and contributors. 1032 | 1033 | F 1034 | 1035 | Time: 0 seconds, Memory: 5.25Mb 1036 | 1037 | There was 1 failure: 1038 | 1039 | 1) ArrayWeakComparisonTest::testEquality 1040 | Failed asserting that two arrays are equal. 1041 | --- Expected 1042 | +++ Actual 1043 | @@ @@ 1044 | Array ( 1045 | - 0 => 1 1046 | + 0 => '1' 1047 | 1 => 2 1048 | - 2 => 3 1049 | + 2 => 33 1050 | 3 => 4 1051 | 4 => 5 1052 | 5 => 6 1053 | ) 1054 | 1055 | /home/sb/ArrayWeakComparisonTest.php:7 1056 | 1057 | FAILURES! 1058 | Tests: 1, Assertions: 1, Failures: 1. 1059 | 1060 | В этом примере сообщается о различии в первом индексе между 1061 | ``1`` и ``'1'``, 1062 | хотя метод ``assertEquals()`` считает, что эти значения совпадают. 1063 | 1064 | 1065 | --------------------------------------------------------------------------------