You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{"payload":{"allShortcutsEnabled":false,"fileTree":{"":{"items":[{"name":".github","path":".github","contentType":"directory"},{"name":".platform","path":".platform","contentType":"directory"},{"name":"_build","path":"_build","contentType":"directory"},{"name":"_images","path":"_images","contentType":"directory"},{"name":"_includes","path":"_includes","contentType":"directory"},{"name":"assetic","path":"assetic","contentType":"directory"},{"name":"best_practices","path":"best_practices","contentType":"directory"},{"name":"bundles","path":"bundles","contentType":"directory"},{"name":"components","path":"components","contentType":"directory"},{"name":"configuration","path":"configuration","contentType":"directory"},{"name":"console","path":"console","contentType":"directory"},{"name":"contributing","path":"contributing","contentType":"directory"},{"name":"controller","path":"controller","contentType":"directory"},{"name":"create_framework","path":"create_framework","contentType":"directory"},{"name":"debug","path":"debug","contentType":"directory"},{"name":"deployment","path":"deployment","contentType":"directory"},{"name":"doctrine","path":"doctrine","contentType":"directory"},{"name":"email","path":"email","contentType":"directory"},{"name":"event_dispatcher","path":"event_dispatcher","contentType":"directory"},{"name":"form","path":"form","contentType":"directory"},{"name":"frontend","path":"frontend","contentType":"directory"},{"name":"getting_started","path":"getting_started","contentType":"directory"},{"name":"http_cache","path":"http_cache","contentType":"directory"},{"name":"introduction","path":"introduction","contentType":"directory"},{"name":"logging","path":"logging","contentType":"directory"},{"name":"profiler","path":"profiler","contentType":"directory"},{"name":"quick_tour","path":"quick_tour","contentType":"directory"},{"name":"reference","path":"reference","contentType":"directory"},{"name":"request","path":"request","contentType":"directory"},{"name":"routing","path":"routing","contentType":"directory"},{"name":"security","path":"security","contentType":"directory"},{"name":"serializer","path":"serializer","contentType":"directory"},{"name":"service_container","path":"service_container","contentType":"directory"},{"name":"session","path":"session","contentType":"directory"},{"name":"setup","path":"setup","contentType":"directory"},{"name":"templating","path":"templating","contentType":"directory"},{"name":"testing","path":"testing","contentType":"directory"},{"name":"translation","path":"translation","contentType":"directory"},{"name":"validation","path":"validation","contentType":"directory"},{"name":".editorconfig","path":".editorconfig","contentType":"file"},{"name":".gitignore","path":".gitignore","contentType":"file"},{"name":".platform.app.yaml","path":".platform.app.yaml","contentType":"file"},{"name":".travis.yml","path":".travis.yml","contentType":"file"},{"name":"README.markdown","path":"README.markdown","contentType":"file"},{"name":"assetic.rst","path":"assetic.rst","contentType":"file"},{"name":"bundles.rst","path":"bundles.rst","contentType":"file"},{"name":"changelog.rst","path":"changelog.rst","contentType":"file"},{"name":"configuration.rst","path":"configuration.rst","contentType":"file"},{"name":"console.rst","path":"console.rst","contentType":"file"},{"name":"controller.rst","path":"controller.rst","contentType":"file"},{"name":"debug.rst","path":"debug.rst","contentType":"file"},{"name":"deployment.rst","path":"deployment.rst","contentType":"file"},{"name":"doctrine.rst","path":"doctrine.rst","contentType":"file"},{"name":"email.rst","path":"email.rst","contentType":"file"},{"name":"event_dispatcher.rst","path":"event_dispatcher.rst","contentType":"file"},{"name":"expressions.rst","path":"expressions.rst","contentType":"file"},{"name":"forms.rst","path":"forms.rst","contentType":"file"},{"name":"frontend.rst","path":"frontend.rst","contentType":"file"},{"name":"http_cache.rst","path":"http_cache.rst","contentType":"file"},{"name":"index.rst","path":"index.rst","contentType":"file"},{"name":"logging.rst","path":"logging.rst","contentType":"file"},{"name":"page_creation.rst","path":"page_creation.rst","contentType":"file"},{"name":"performance.rst","path":"performance.rst","contentType":"file"},{"name":"profiler.rst","path":"profiler.rst","contentType":"file"},{"name":"request.rst","path":"request.rst","contentType":"file"},{"name":"requirements.txt","path":"requirements.txt","contentType":"file"},{"name":"routing.rst","path":"routing.rst","contentType":"file"},{"name":"security.rst","path":"security.rst","contentType":"file"},{"name":"serializer.rst","path":"serializer.rst","contentType":"file"},{"name":"service_container.rst","path":"service_container.rst","contentType":"file"},{"name":"session.rst","path":"session.rst","contentType":"file"},{"name":"setup.rst","path":"setup.rst","contentType":"file"},{"name":"templating.rst","path":"templating.rst","contentType":"file"},{"name":"testing.rst","path":"testing.rst","contentType":"file"},{"name":"translation.rst","path":"translation.rst","contentType":"file"},{"name":"validation.rst","path":"validation.rst","contentType":"file"}],"totalCount":76}},"fileTreeProcessingTime":54.679463,"foldersToFetch":[],"incompleteFileTree":false,"repo":{"id":521583,"defaultBranch":"7.3","name":"symfony-docs","ownerLogin":"symfony","currentUserCanPush":false,"isFork":false,"isEmpty":false,"createdAt":"2010-02-17T08:43:51.000Z","ownerAvatar":"https://avatars.githubusercontent.com/u/143937?v=4","public":true,"private":false,"isOrgOwned":true},"codeLineWrapEnabled":false,"symbolsExpanded":false,"treeExpanded":true,"refInfo":{"name":"227fe4cb35ea54303bd54a1e734d9d731c8715ed","listCacheKey":"v0:1748857018.0","canEdit":false,"refType":"tree","currentOid":"227fe4cb35ea54303bd54a1e734d9d731c8715ed"},"path":"testing.rst","currentUser":null,"blob":{"rawLines":null,"stylingDirectives":null,"colorizedLines":null,"csv":null,"csvError":null,"dependabotInfo":{"showConfigurationBanner":false,"configFilePath":null,"networkDependabotPath":"/symfony/symfony-docs/network/updates","dismissConfigurationNoticePath":"/settings/dismiss-notice/dependabot_configuration_notice","configurationNoticeDismissed":null},"displayName":"testing.rst","displayUrl":"https://github.com/symfony/symfony-docs/blob/227fe4cb35ea54303bd54a1e734d9d731c8715ed/testing.rst?raw=true","headerInfo":{"blobSize":"28 KB","deleteTooltip":"You must be signed in to make or propose changes","editTooltip":"You must be signed in to make or propose changes","ghDesktopPath":null,"isGitLfs":false,"onBranch":false,"shortPath":"dbb42ff","siteNavLoginPath":"/login?return_to=https%3A%2F%2Fgithub.com%2Fsymfony%2Fsymfony-docs%2Fblob%2F227fe4cb35ea54303bd54a1e734d9d731c8715ed%2Ftesting.rst","isCSV":false,"isRichtext":true,"toc":[{"level":2,"text":"Testing","anchor":"testing","htmlText":"Testing"},{"level":3,"text":"The PHPUnit Testing Framework","anchor":"the-phpunit-testing-framework","htmlText":"The PHPUnit Testing Framework"},{"level":3,"text":"Unit Tests","anchor":"unit-tests","htmlText":"Unit Tests"},{"level":3,"text":"Functional Tests","anchor":"functional-tests","htmlText":"Functional Tests"},{"level":4,"text":"Your First Functional Test","anchor":"your-first-functional-test","htmlText":"Your First Functional Test"},{"level":3,"text":"Working with the Test Client","anchor":"working-with-the-test-client","htmlText":"Working with the Test Client"},{"level":4,"text":"Browsing","anchor":"browsing","htmlText":"Browsing"},{"level":4,"text":"Accessing Internal Objects","anchor":"accessing-internal-objects","htmlText":"Accessing Internal Objects"},{"level":4,"text":"Accessing the Container","anchor":"accessing-the-container","htmlText":"Accessing the Container"},{"level":4,"text":"Accessing the Profiler Data","anchor":"accessing-the-profiler-data","htmlText":"Accessing the Profiler Data"},{"level":4,"text":"Redirecting","anchor":"redirecting","htmlText":"Redirecting"},{"level":3,"text":"The Crawler","anchor":"the-crawler","htmlText":"The Crawler"},{"level":4,"text":"Traversing","anchor":"traversing","htmlText":"Traversing"},{"level":4,"text":"Extracting Information","anchor":"extracting-information","htmlText":"Extracting Information"},{"level":4,"text":"Links","anchor":"links","htmlText":"Links"},{"level":4,"text":"Forms","anchor":"forms","htmlText":"Forms"},{"level":5,"text":"Adding and Removing Forms to a Collection","anchor":"adding-and-removing-forms-to-a-collection","htmlText":"Adding and Removing Forms to a Collection"},{"level":3,"text":"Testing Configuration","anchor":"testing-configuration","htmlText":"Testing Configuration"},{"level":4,"text":"PHPUnit Configuration","anchor":"phpunit-configuration","htmlText":"PHPUnit Configuration"},{"level":3,"text":"Learn more","anchor":"learn-more","htmlText":"Learn more"}],"lineInfo":{"truncatedLoc":"927","truncatedSloc":"681"},"mode":"file"},"image":false,"isCodeownersFile":null,"isPlain":false,"isValidLegacyIssueTemplate":false,"issueTemplate":null,"discussionTemplate":null,"language":"reStructuredText","languageID":419,"large":false,"planSupportInfo":{"repoIsFork":null,"repoOwnedByCurrentUser":null,"requestFullPath":"/symfony/symfony-docs/blob/227fe4cb35ea54303bd54a1e734d9d731c8715ed/testing.rst","showFreeOrgGatedFeatureMessage":null,"showPlanSupportBanner":null,"upgradeDataAttributes":null,"upgradePath":null},"publishBannersInfo":{"dismissActionNoticePath":"/settings/dismiss-notice/publish_action_from_dockerfile","releasePath":"/symfony/symfony-docs/releases/new?marketplace=true","showPublishActionBanner":false},"rawBlobUrl":"https://github.com/symfony/symfony-docs/raw/227fe4cb35ea54303bd54a1e734d9d731c8715ed/testing.rst","renderImageOrRaw":false,"richText":"\u003carticle class=\"markdown-body entry-content container-lg\" itemprop=\"text\"\u003e\u003cpre\u003e.. index::\n single: Tests\n\n\u003c/pre\u003e\n\u003ca name=\"user-content-testing\"\u003e\u003c/a\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch2 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eTesting\u003c/h2\u003e\u003ca id=\"user-content-testing\" class=\"anchor\" aria-label=\"Permalink: Testing\" href=\"#testing\"\u003e\u003csvg class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eWhenever you write a new line of code, you also potentially add new bugs.\nTo build better and more reliable applications, you should test your code\nusing both functional and unit tests.\u003c/p\u003e\n\u003ca name=\"user-content-the-phpunit-testing-framework\"\u003e\u003c/a\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch3 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eThe PHPUnit Testing Framework\u003c/h3\u003e\u003ca id=\"user-content-the-phpunit-testing-framework\" class=\"anchor\" aria-label=\"Permalink: The PHPUnit Testing Framework\" href=\"#the-phpunit-testing-framework\"\u003e\u003csvg class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eSymfony integrates with an independent library - called PHPUnit - to give\nyou a rich testing framework. This article won't cover PHPUnit itself, but\nit has its own excellent \u003ca href=\"https://phpunit.de/manual/current/en/\" rel=\"nofollow\"\u003edocumentation\u003c/a\u003e.\u003c/p\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eNote\u003c/p\u003e\n\u003cp dir=\"auto\"\u003eIt's recommended to use the latest stable PHPUnit version, \u003ca href=\"https://phpunit.de/manual/current/en/installation.html#installation.phar\" rel=\"nofollow\"\u003einstalled as\nPHAR\u003c/a\u003e.\u003c/p\u003e\n\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eEach test - whether it's a unit test or a functional test - is a PHP class\nthat should live in the \u003ccode\u003eTests/\u003c/code\u003e subdirectory of your bundles. If you follow\nthis rule, then you can run all of your application's tests with the following\ncommand:\u003c/p\u003e\n\u003cpre lang=\"terminal\"\u003e# specify the configuration directory on the command line\n$ phpunit -c app/\n\u003c/pre\u003e\n\u003cp dir=\"auto\"\u003eThe \u003ccode\u003e-c\u003c/code\u003e option tells PHPUnit to look in the \u003ccode\u003eapp/\u003c/code\u003e directory for a configuration\nfile. If you're curious about the PHPUnit options, check out the \u003ccode\u003eapp/phpunit.xml.dist\u003c/code\u003e\nfile.\u003c/p\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eTip\u003c/p\u003e\n\u003cp dir=\"auto\"\u003eCode coverage can be generated with the \u003ccode\u003e--coverage-*\u003c/code\u003e options, see the\nhelp information that is shown when using \u003ccode\u003e--help\u003c/code\u003e for more information.\u003c/p\u003e\n\u003c/div\u003e\n\u003cpre\u003e.. index::\n single: Tests; Unit tests\n\n\u003c/pre\u003e\n\u003ca name=\"user-content-unit-tests\"\u003e\u003c/a\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch3 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eUnit Tests\u003c/h3\u003e\u003ca id=\"user-content-unit-tests\" class=\"anchor\" aria-label=\"Permalink: Unit Tests\" href=\"#unit-tests\"\u003e\u003csvg class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eA unit test is a test against a single PHP class, also called a \u003cem\u003eunit\u003c/em\u003e. If you\nwant to test the overall behavior of your application, see the section about\n\u003ca href=\"#functional-tests\"\u003eFunctional Tests\u003c/a\u003e.\u003c/p\u003e\n\u003cp dir=\"auto\"\u003eWriting Symfony unit tests is no different from writing standard PHPUnit\nunit tests. Suppose, for example, that you have an \u003cem\u003eincredibly\u003c/em\u003e simple class\ncalled \u003ccode\u003eCalculator\u003c/code\u003e in the \u003ccode\u003eUtil/\u003c/code\u003e directory of the app bundle:\u003c/p\u003e\n\u003cpre\u003e// src/AppBundle/Util/Calculator.php\nnamespace AppBundle\\Util;\n\nclass Calculator\n{\n public function add($a, $b)\n {\n return $a + $b;\n }\n}\n\u003c/pre\u003e\n\u003cp dir=\"auto\"\u003eTo test this, create a \u003ccode\u003eCalculatorTest\u003c/code\u003e file in the \u003ccode\u003eTests/Util\u003c/code\u003e directory\nof your bundle:\u003c/p\u003e\n\u003cpre\u003e// src/AppBundle/Tests/Util/CalculatorTest.php\nnamespace AppBundle\\Tests\\Util;\n\nuse AppBundle\\Util\\Calculator;\nuse PHPUnit\\Framework\\TestCase;\n\nclass CalculatorTest extends TestCase\n{\n public function testAdd()\n {\n $calc = new Calculator();\n $result = $calc-\u0026gt;add(30, 12);\n\n // assert that your calculator added the numbers correctly!\n $this-\u0026gt;assertEquals(42, $result);\n }\n}\n\u003c/pre\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eNote\u003c/p\u003e\n\u003cp dir=\"auto\"\u003eBy convention, the \u003ccode\u003eTests/\u003c/code\u003e sub-directory should replicate the directory\nof your bundle for unit tests. So, if you're testing a class in your\nbundle's \u003ccode\u003eUtil/\u003c/code\u003e directory, put the test in the \u003ccode\u003eTests/Util/\u003c/code\u003e\ndirectory.\u003c/p\u003e\n\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eJust like in your real application - autoloading is automatically enabled\nvia the \u003ccode\u003ebootstrap.php.cache\u003c/code\u003e file (as configured by default in the\n\u003ccode\u003eapp/phpunit.xml.dist\u003c/code\u003e file).\u003c/p\u003e\n\u003cp dir=\"auto\"\u003eRunning tests for a given file or directory is also very easy:\u003c/p\u003e\n\u003cpre lang=\"terminal\"\u003e# run all tests of the application\n$ phpunit -c app\n\n# run all tests in the Util directory\n$ phpunit -c app src/AppBundle/Tests/Util\n\n# run tests for the Calculator class\n$ phpunit -c app src/AppBundle/Tests/Util/CalculatorTest.php\n\n# run all tests for the entire Bundle\n$ phpunit -c app src/AppBundle/\n\u003c/pre\u003e\n\u003cpre\u003e.. index::\n single: Tests; Functional tests\n\n\u003c/pre\u003e\n\u003ca name=\"user-content-functional-tests\"\u003e\u003c/a\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch3 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eFunctional Tests\u003c/h3\u003e\u003ca id=\"user-content-functional-tests\" class=\"anchor\" aria-label=\"Permalink: Functional Tests\" href=\"#functional-tests\"\u003e\u003csvg class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eFunctional tests check the integration of the different layers of an\napplication (from the routing to the views). They are no different from unit\ntests as far as PHPUnit is concerned, but they have a very specific workflow:\u003c/p\u003e\n\u003cul dir=\"auto\"\u003e\n\u003cli\u003eMake a request;\u003c/li\u003e\n\u003cli\u003eTest the response;\u003c/li\u003e\n\u003cli\u003eClick on a link or submit a form;\u003c/li\u003e\n\u003cli\u003eTest the response;\u003c/li\u003e\n\u003cli\u003eRinse and repeat.\u003c/li\u003e\n\u003c/ul\u003e\n\u003ca name=\"user-content-your-first-functional-test\"\u003e\u003c/a\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch4 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eYour First Functional Test\u003c/h4\u003e\u003ca id=\"user-content-your-first-functional-test\" class=\"anchor\" aria-label=\"Permalink: Your First Functional Test\" href=\"#your-first-functional-test\"\u003e\u003csvg class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eFunctional tests are simple PHP files that typically live in the \u003ccode\u003eTests/Controller\u003c/code\u003e\ndirectory of your bundle. If you want to test the pages handled by your\n\u003ccode\u003ePostController\u003c/code\u003e class, start by creating a new \u003ccode\u003ePostControllerTest.php\u003c/code\u003e\nfile that extends a special \u003ccode\u003eWebTestCase\u003c/code\u003e class.\u003c/p\u003e\n\u003cp dir=\"auto\"\u003eAs an example, a test could look like this:\u003c/p\u003e\n\u003cpre\u003e// src/AppBundle/Tests/Controller/PostControllerTest.php\nnamespace AppBundle\\Tests\\Controller;\n\nuse Symfony\\Bundle\\FrameworkBundle\\Test\\WebTestCase;\n\nclass PostControllerTest extends WebTestCase\n{\n public function testShowPost()\n {\n $client = static::createClient();\n\n $crawler = $client-\u0026gt;request('GET', '/post/hello-world');\n\n $this-\u0026gt;assertGreaterThan(\n 0,\n $crawler-\u0026gt;filter('html:contains(\"Hello World\")')-\u0026gt;count()\n );\n }\n}\n\u003c/pre\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eTip\u003c/p\u003e\n\u003cp dir=\"auto\"\u003eTo run your functional tests, the \u003ccode\u003eWebTestCase\u003c/code\u003e class bootstraps the\nkernel of your application. In most cases, this happens automatically.\nHowever, if your kernel is in a non-standard directory, you'll need\nto modify your \u003ccode\u003ephpunit.xml.dist\u003c/code\u003e file to set the \u003ccode\u003eKERNEL_DIR\u003c/code\u003e\nenvironment variable to the directory of your kernel:\u003c/p\u003e\n\u003cdiv class=\"highlight highlight-text-xml notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"\u0026lt;?xml version=\u0026quot;1.0\u0026quot; charset=\u0026quot;utf-8\u0026quot; ?\u0026gt;\n\u0026lt;phpunit\u0026gt;\n \u0026lt;php\u0026gt;\n \u0026lt;server name=\u0026quot;KERNEL_DIR\u0026quot; value=\u0026quot;/path/to/your/app/\u0026quot; /\u0026gt;\n \u0026lt;/php\u0026gt;\n \u0026lt;!-- ... --\u0026gt;\n\u0026lt;/phpunit\u0026gt;\"\u003e\u003cpre\u003e\u0026lt;?\u003cspan class=\"pl-ent\"\u003exml\u003c/span\u003e\u003cspan class=\"pl-e\"\u003e version\u003c/span\u003e=\u003cspan class=\"pl-s\"\u003e\u003cspan class=\"pl-pds\"\u003e\"\u003c/span\u003e1.0\u003cspan class=\"pl-pds\"\u003e\"\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"pl-e\"\u003e charset\u003c/span\u003e=\u003cspan class=\"pl-s\"\u003e\u003cspan class=\"pl-pds\"\u003e\"\u003c/span\u003eutf-8\u003cspan class=\"pl-pds\"\u003e\"\u003c/span\u003e\u003c/span\u003e ?\u0026gt;\n\u0026lt;\u003cspan class=\"pl-ent\"\u003ephpunit\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003ephp\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003eserver\u003c/span\u003e \u003cspan class=\"pl-e\"\u003ename\u003c/span\u003e=\u003cspan class=\"pl-s\"\u003e\u003cspan class=\"pl-pds\"\u003e\"\u003c/span\u003eKERNEL_DIR\u003cspan class=\"pl-pds\"\u003e\"\u003c/span\u003e\u003c/span\u003e \u003cspan class=\"pl-e\"\u003evalue\u003c/span\u003e=\u003cspan class=\"pl-s\"\u003e\u003cspan class=\"pl-pds\"\u003e\"\u003c/span\u003e/path/to/your/app/\u003cspan class=\"pl-pds\"\u003e\"\u003c/span\u003e\u003c/span\u003e /\u0026gt;\n \u0026lt;/\u003cspan class=\"pl-ent\"\u003ephp\u003c/span\u003e\u0026gt;\n \u003cspan class=\"pl-c\"\u003e\u003cspan class=\"pl-c\"\u003e\u0026lt;!--\u003c/span\u003e ... \u003cspan class=\"pl-c\"\u003e--\u0026gt;\u003c/span\u003e\u003c/span\u003e\n\u0026lt;/\u003cspan class=\"pl-ent\"\u003ephpunit\u003c/span\u003e\u0026gt;\u003c/pre\u003e\u003c/div\u003e\n\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eThe \u003ccode\u003ecreateClient()\u003c/code\u003e method returns a client, which is like a browser that\nyou'll use to crawl your site:\u003c/p\u003e\n\u003cpre\u003e$crawler = $client-\u0026gt;request('GET', '/post/hello-world');\n\u003c/pre\u003e\n\u003cp dir=\"auto\"\u003eThe \u003ccode\u003erequest()\u003c/code\u003e method (read\n\u003ca href=\"#id1\"\u003e\u003cspan id=\"user-content-id2\"\u003e:ref:`more about the request method \u0026lt;testing-request-method-sidebar\u0026gt;`\u003c/span\u003e\u003c/a\u003e)\nreturns a \u003ca href=\"#id3\"\u003e\u003cspan id=\"user-content-id4\"\u003e:class:`Symfony\\\\Component\\\\DomCrawler\\\\Crawler`\u003c/span\u003e\u003c/a\u003e object which can\nbe used to select elements in the response, click on links and submit forms.\u003c/p\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eTip\u003c/p\u003e\n\u003cp dir=\"auto\"\u003eThe \u003ccode\u003eCrawler\u003c/code\u003e only works when the response is an XML or an HTML document.\nTo get the raw content response, call \u003ccode\u003e$client-\u0026gt;getResponse()-\u0026gt;getContent()\u003c/code\u003e.\u003c/p\u003e\n\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eClick on a link by first selecting it with the crawler using either an XPath\nexpression or a CSS selector, then use the client to click on it. For example:\u003c/p\u003e\n\u003cpre\u003e$link = $crawler\n -\u0026gt;filter('a:contains(\"Greet\")') // find all links with the text \"Greet\"\n -\u0026gt;eq(1) // select the second link in the list\n -\u0026gt;link()\n;\n\n// and click it\n$crawler = $client-\u0026gt;click($link);\n\u003c/pre\u003e\n\u003cp dir=\"auto\"\u003eSubmitting a form is very similar: select a form button, optionally override\nsome form values and submit the corresponding form:\u003c/p\u003e\n\u003cpre\u003e$form = $crawler-\u0026gt;selectButton('submit')-\u0026gt;form();\n\n// set some values\n$form['name'] = 'Lucas';\n$form['form_name[subject]'] = 'Hey there!';\n\n// submit the form\n$crawler = $client-\u0026gt;submit($form);\n\u003c/pre\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eTip\u003c/p\u003e\n\u003cp dir=\"auto\"\u003eThe form can also handle uploads and contains methods to fill in different types\nof form fields (e.g. \u003ccode\u003eselect()\u003c/code\u003e and \u003ccode\u003etick()\u003c/code\u003e). For details, see the\n\u003ca href=\"#forms\"\u003eForms\u003c/a\u003e section below.\u003c/p\u003e\n\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eNow that you can easily navigate through an application, use assertions to test\nthat it actually does what you expect it to. Use the Crawler to make assertions\non the DOM:\u003c/p\u003e\n\u003cpre\u003e// Assert that the response matches a given CSS selector.\n$this-\u0026gt;assertGreaterThan(0, $crawler-\u0026gt;filter('h1')-\u0026gt;count());\n\u003c/pre\u003e\n\u003cp dir=\"auto\"\u003eOr test against the response content directly if you just want to assert that\nthe content contains some text or in case that the response is not an XML/HTML\ndocument:\u003c/p\u003e\n\u003cpre\u003e$this-\u0026gt;assertContains(\n 'Hello World',\n $client-\u0026gt;getResponse()-\u0026gt;getContent()\n);\n\u003c/pre\u003e\n\u003cpre\u003e.. index::\n single: Tests; Assertions\n\n\u003c/pre\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eUseful Assertions\u003c/p\u003e\n\u003cp dir=\"auto\"\u003eTo get you started faster, here is a list of the most common and\nuseful test assertions:\u003c/p\u003e\n\u003cpre\u003euse Symfony\\Component\\HttpFoundation\\Response;\n\n// ...\n\n// Assert that there is at least one h2 tag\n// with the class \"subtitle\"\n$this-\u0026gt;assertGreaterThan(\n 0,\n $crawler-\u0026gt;filter('h2.subtitle')-\u0026gt;count()\n);\n\n// Assert that there are exactly 4 h2 tags on the page\n$this-\u0026gt;assertCount(4, $crawler-\u0026gt;filter('h2'));\n\n// Assert that the \"Content-Type\" header is \"application/json\"\n$this-\u0026gt;assertTrue(\n $client-\u0026gt;getResponse()-\u0026gt;headers-\u0026gt;contains(\n 'Content-Type',\n 'application/json'\n ),\n 'the \"Content-Type\" header is \"application/json\"' // optional message shown on failure\n);\n\n// Assert that the response content contains a string\n$this-\u0026gt;assertContains('foo', $client-\u0026gt;getResponse()-\u0026gt;getContent());\n// ...or matches a regex\n$this-\u0026gt;assertRegExp('/foo(bar)?/', $client-\u0026gt;getResponse()-\u0026gt;getContent());\n\n// Assert that the response status code is 2xx\n$this-\u0026gt;assertTrue($client-\u0026gt;getResponse()-\u0026gt;isSuccessful(), 'response status is 2xx');\n// Assert that the response status code is 404\n$this-\u0026gt;assertTrue($client-\u0026gt;getResponse()-\u0026gt;isNotFound());\n// Assert a specific 200 status code\n$this-\u0026gt;assertEquals(\n 200, // or Symfony\\Component\\HttpFoundation\\Response::HTTP_OK\n $client-\u0026gt;getResponse()-\u0026gt;getStatusCode()\n);\n\n// Assert that the response is a redirect to /demo/contact\n$this-\u0026gt;assertTrue(\n $client-\u0026gt;getResponse()-\u0026gt;isRedirect('/demo/contact'),\n 'response is a redirect to /demo/contact'\n);\n// ...or simply check that the response is a redirect to any URL\n$this-\u0026gt;assertTrue($client-\u0026gt;getResponse()-\u0026gt;isRedirect());\n\u003c/pre\u003e\n\u003c/div\u003e\n\u003cpre\u003e.. index::\n single: Tests; Client\n\n\u003c/pre\u003e\n\u003ca name=\"user-content-working-with-the-test-client\"\u003e\u003c/a\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch3 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eWorking with the Test Client\u003c/h3\u003e\u003ca id=\"user-content-working-with-the-test-client\" class=\"anchor\" aria-label=\"Permalink: Working with the Test Client\" href=\"#working-with-the-test-client\"\u003e\u003csvg class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eThe test client simulates an HTTP client like a browser and makes requests\ninto your Symfony application:\u003c/p\u003e\n\u003cpre\u003e$crawler = $client-\u0026gt;request('GET', '/post/hello-world');\n\u003c/pre\u003e\n\u003cp dir=\"auto\"\u003eThe \u003ccode\u003erequest()\u003c/code\u003e method takes the HTTP method and a URL as arguments and\nreturns a \u003ccode\u003eCrawler\u003c/code\u003e instance.\u003c/p\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eTip\u003c/p\u003e\n\u003cp dir=\"auto\"\u003eHardcoding the request URLs is a best practice for functional tests. If the\ntest generates URLs using the Symfony router, it won't detect any change\nmade to the application URLs which may impact the end users.\u003c/p\u003e\n\u003c/div\u003e\n\u003cdiv id=\"user-content-testing-request-method-sidebar\" dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eMore about the \u003ccode\u003erequest()\u003c/code\u003e Method:\u003c/p\u003e\n\u003cp dir=\"auto\"\u003eThe full signature of the \u003ccode\u003erequest()\u003c/code\u003e method is:\u003c/p\u003e\n\u003cpre\u003erequest(\n $method,\n $uri,\n array $parameters = array(),\n array $files = array(),\n array $server = array(),\n $content = null,\n $changeHistory = true\n)\n\u003c/pre\u003e\n\u003cp dir=\"auto\"\u003eThe \u003ccode\u003
8000
eserver\u003c/code\u003e array is the raw values that you'd expect to normally\nfind in the PHP \u003ca href=\"http://php.net/manual/en/reserved.variables.server.php\" rel=\"nofollow\"\u003e$_SERVER\u003c/a\u003e superglobal. For example, to set the \u003ccode\u003eContent-Type\u003c/code\u003e,\n\u003ccode\u003eReferer\u003c/code\u003e and \u003ccode\u003eX-Requested-With\u003c/code\u003e HTTP headers, you'd pass the following (mind\nthe \u003ccode\u003eHTTP_\u003c/code\u003e prefix for non standard headers):\u003c/p\u003e\n\u003cpre\u003e$client-\u0026gt;request(\n 'GET',\n '/post/hello-world',\n array(),\n array(),\n array(\n 'CONTENT_TYPE' =\u0026gt; 'application/json',\n 'HTTP_REFERER' =\u0026gt; '/foo/bar',\n 'HTTP_X-Requested-With' =\u0026gt; 'XMLHttpRequest',\n )\n);\n\u003c/pre\u003e\n\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eUse the crawler to find DOM elements in the response. These elements can then\nbe used to click on links and submit forms:\u003c/p\u003e\n\u003cpre\u003e$link = $crawler-\u0026gt;selectLink('Go elsewhere...')-\u0026gt;link();\n$crawler = $client-\u0026gt;click($link);\n\n$form = $crawler-\u0026gt;selectButton('validate')-\u0026gt;form();\n$crawler = $client-\u0026gt;submit($form, array('name' =\u0026gt; 'Fabien'));\n\u003c/pre\u003e\n\u003cp dir=\"auto\"\u003eThe \u003ccode\u003eclick()\u003c/code\u003e and \u003ccode\u003esubmit()\u003c/code\u003e methods both return a \u003ccode\u003eCrawler\u003c/code\u003e object.\nThese methods are the best way to browse your application as it takes care\nof a lot of things for you, like detecting the HTTP method from a form and\ngiving you a nice API for uploading files.\u003c/p\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eTip\u003c/p\u003e\n\u003cp dir=\"auto\"\u003eYou will learn more about the \u003ccode\u003eLink\u003c/code\u003e and \u003ccode\u003eForm\u003c/code\u003e objects in the\n\u003ca href=\"#id5\"\u003e\u003cspan id=\"user-content-id6\"\u003e:ref:`Crawler \u0026lt;testing-crawler\u0026gt;`\u003c/span\u003e\u003c/a\u003e section below.\u003c/p\u003e\n\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eThe \u003ccode\u003erequest()\u003c/code\u003e method can also be used to simulate form submissions directly\nor perform more complex requests. Some useful examples:\u003c/p\u003e\n\u003cpre\u003e// Directly submit a form (but using the Crawler is easier!)\n$client-\u0026gt;request('POST', '/submit', array('name' =\u0026gt; 'Fabien'));\n\n// Submit a raw JSON string in the request body\n$client-\u0026gt;request(\n 'POST',\n '/submit',\n array(),\n array(),\n array('CONTENT_TYPE' =\u0026gt; 'application/json'),\n '{\"name\":\"Fabien\"}'\n);\n\n// Form submission with a file upload\nuse Symfony\\Component\\HttpFoundation\\File\\UploadedFile;\n\n$photo = new UploadedFile(\n '/path/to/photo.jpg',\n 'photo.jpg',\n 'image/jpeg',\n 123\n);\n$client-\u0026gt;request(\n 'POST',\n '/submit',\n array('name' =\u0026gt; 'Fabien'),\n array('photo' =\u0026gt; $photo)\n);\n\n// Perform a DELETE request and pass HTTP headers\n$client-\u0026gt;request(\n 'DELETE',\n '/post/12',\n array(),\n array(),\n array('PHP_AUTH_USER' =\u0026gt; 'username', 'PHP_AUTH_PW' =\u0026gt; 'pa$$word')\n);\n\u003c/pre\u003e\n\u003cp dir=\"auto\"\u003eLast but not least, you can force each request to be executed in its own PHP\nprocess to avoid any side-effects when working with several clients in the same\nscript:\u003c/p\u003e\n\u003cpre\u003e$client-\u0026gt;insulate();\n\u003c/pre\u003e\n\u003ca name=\"user-content-browsing\"\u003e\u003c/a\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch4 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eBrowsing\u003c/h4\u003e\u003ca id=\"user-content-browsing\" class=\"anchor\" aria-label=\"Permalink: Browsing\" href=\"#browsing\"\u003e\u003csvg class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eThe Client supports many operations that can be done in a real browser:\u003c/p\u003e\n\u003cpre\u003e$client-\u0026gt;back();\n$client-\u0026gt;forward();\n$client-\u0026gt;reload();\n\n// Clears all cookies and the history\n$client-\u0026gt;restart();\n\u003c/pre\u003e\n\u003ca name=\"user-content-accessing-internal-objects\"\u003e\u003c/a\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch4 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eAccessing Internal Objects\u003c/h4\u003e\u003ca id=\"user-content-accessing-internal-objects\" class=\"anchor\" aria-label=\"Permalink: Accessing Internal Objects\" href=\"#accessing-internal-objects\"\u003e\u003csvg class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cpre\u003e.. versionadded:: 2.3\n The :method:`Symfony\\\\Component\\\\BrowserKit\\\\Client::getInternalRequest`\n and :method:`Symfony\\\\Component\\\\BrowserKit\\\\Client::getInternalResponse`\n methods were introduced in Symfony 2.3.\n\n\u003c/pre\u003e\n\u003cp dir=\"auto\"\u003eIf you use the client to test your application, you might want to access the\nclient's internal objects:\u003c/p\u003e\n\u003cpre\u003e$history = $client-\u0026gt;getHistory();\n$cookieJar = $client-\u0026gt;getCookieJar();\n\u003c/pre\u003e\n\u003cp dir=\"auto\"\u003eYou can also get the objects related to the latest request:\u003c/p\u003e\n\u003cpre\u003e// the HttpKernel request instance\n$request = $client-\u0026gt;getRequest();\n\n// the BrowserKit request instance\n$request = $client-\u0026gt;getInternalRequest();\n\n// the HttpKernel response instance\n$response = $client-\u0026gt;getResponse();\n\n// the BrowserKit response instance\n$response = $client-\u0026gt;getInternalResponse();\n\n$crawler = $client-\u0026gt;getCrawler();\n\u003c/pre\u003e\n\u003ca name=\"user-content-accessing-the-container\"\u003e\u003c/a\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch4 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eAccessing the Container\u003c/h4\u003e\u003ca id=\"user-content-accessing-the-container\" class=\"anchor\" aria-label=\"Permalink: Accessing the Container\" href=\"#accessing-the-container\"\u003e\u003csvg class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eIt's highly recommended that a functional test only tests the response. But\nunder certain very rare circumstances, you might want to access some internal\nobjects to write assertions. In such cases, you can access the Dependency\nInjection Container:\u003c/p\u003e\n\u003cpre\u003e// will be the same container used in your test, unless you're using\n// $client-\u0026gt;insulate() or using real HTTP requests to test your application\n$container = $client-\u0026gt;getContainer();\n\u003c/pre\u003e\n\u003cp dir=\"auto\"\u003eFor a list of services available in your application, use the \u003ccode\u003edebug:container\u003c/code\u003e\ncommand.\u003c/p\u003e\n\u003cpre\u003e.. versionadded:: 2.6\n Prior to Symfony 2.6, this command was called ``container:debug``.\n\n\u003c/pre\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eTip\u003c/p\u003e\n\u003cp dir=\"auto\"\u003eIf the information you need to check is available from the profiler, use\nit instead.\u003c/p\u003e\n\u003c/div\u003e\n\u003ca name=\"user-content-accessing-the-profiler-data\"\u003e\u003c/a\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch4 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eAccessing the Profiler Data\u003c/h4\u003e\u003ca id=\"user-content-accessing-the-profiler-data\" class=\"anchor\" aria-label=\"Permalink: Accessing the Profiler Data\" href=\"#accessing-the-profiler-data\"\u003e\u003csvg class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eOn each request, you can enable the Symfony profiler to collect data about the\ninternal handling of that request. For example, the profiler could be used to\nverify that a given page executes less than a certain number of database\nqueries when loading.\u003c/p\u003e\n\u003cp dir=\"auto\"\u003eTo get the Profiler for the last request, do the following:\u003c/p\u003e\n\u003cpre\u003e// enable the profiler for the very next request\n$client-\u0026gt;enableProfiler();\n\n$crawler = $client-\u0026gt;request('GET', '/profiler');\n\n// get the profile\n$profile = $client-\u0026gt;getProfile();\n\u003c/pre\u003e\n\u003cp dir=\"auto\"\u003eFor specific details on using the profiler inside a test, see the\n\u003ca href=\"#id7\"\u003e\u003cspan id=\"user-content-id8\"\u003e:doc:`/testing/profiling`\u003c/span\u003e\u003c/a\u003e article.\u003c/p\u003e\n\u003ca name=\"user-content-redirecting\"\u003e\u003c/a\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch4 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eRedirecting\u003c/h4\u003e\u003ca id=\"user-content-redirecting\" class=\"anchor\" aria-label=\"Permalink: Redirecting\" href=\"#redirecting\"\u003e\u003csvg class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eWhen a request returns a redirect response, the client does not follow\nit automatically. You can examine the response and force a redirection\nafterwards with the \u003ccode\u003efollowRedirect()\u003c/code\u003e method:\u003c/p\u003e\n\u003cpre\u003e$crawler = $client-\u0026gt;followRedirect();\n\u003c/pre\u003e\n\u003cp dir=\"auto\"\u003eIf you want the client to automatically follow all redirects, you can\nforce them by calling the \u003ccode\u003efollowRedirects()\u003c/code\u003e method before performing the request:\u003c/p\u003e\n\u003cpre\u003e$client-\u0026gt;followRedirects();\n\u003c/pre\u003e\n\u003cp dir=\"auto\"\u003eIf you pass \u003ccode\u003efalse\u003c/code\u003e to the \u003ccode\u003efollowRedirects()\u003c/code\u003e method, the redirects\nwill no longer be followed:\u003c/p\u003e\n\u003cpre\u003e$client-\u0026gt;followRedirects(false);\n\u003c/pre\u003e\n\u003cpre\u003e.. index::\n single: Tests; Crawler\n\n\u003c/pre\u003e\n\u003ca name=\"user-content-the-crawler\"\u003e\u003c/a\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch3 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eThe Crawler\u003c/h3\u003e\u003ca id=\"user-content-the-crawler\" class=\"anchor\" aria-label=\"Permalink: The Crawler\" href=\"#the-crawler\"\u003e\u003csvg class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eA Crawler instance is returned each time you make a request with the Client.\nIt allows you to traverse HTML documents, select nodes, find links and forms.\u003c/p\u003e\n\u003ca name=\"user-content-traversing\"\u003e\u003c/a\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch4 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eTraversing\u003c/h4\u003e\u003ca id=\"user-content-traversing\" class=\"anchor\" aria-label=\"Permalink: Traversing\" href=\"#traversing\"\u003e\u003csvg class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eLike jQuery, the Crawler has methods to traverse the DOM of an HTML/XML\ndocument. For example, the following finds all \u003ccode\u003einput[type=submit]\u003c/code\u003e elements,\nselects the last one on the page, and then selects its immediate parent element:\u003c/p\u003e\n\u003cpre\u003e$newCrawler = $crawler-\u0026gt;filter('input[type=submit]')\n -\u0026gt;last()\n -\u0026gt;parents()\n -\u0026gt;first()\n;\n\u003c/pre\u003e\n\u003cp dir=\"auto\"\u003eMany other methods are also available:\u003c/p\u003e\n\u003cdl\u003e\n\u003cdt\u003e\u003ccode\u003efilter('h1.title')\u003c/code\u003e\u003c/dt\u003e\n\u003cdd\u003eNodes that match the CSS selector.\u003c/dd\u003e\n\u003cdt\u003e\u003ccode\u003efilterXpath('h1')\u003c/code\u003e\u003c/dt\u003e\n\u003cdd\u003eNodes that match the XPath expression.\u003c/dd\u003e\n\u003cdt\u003e\u003ccode\u003eeq(1)\u003c/code\u003e\u003c/dt\u003e\n\u003cdd\u003eNode for the specified index.\u003c/dd\u003e\n\u003cdt\u003e\u003ccode\u003efirst()\u003c/code\u003e\u003c/dt\u003e\n\u003cdd\u003eFirst node.\u003c/dd\u003e\n\u003cdt\u003e\u003ccode\u003elast()\u003c/code\u003e\u003c/dt\u003e\n\u003cdd\u003eLast node.\u003c/dd\u003e\n\u003cdt\u003e\u003ccode\u003esiblings()\u003c/code\u003e\u003c/dt\u003e\n\u003cdd\u003eSiblings.\u003c/dd\u003e\n\u003cdt\u003e\u003ccode\u003enextAll()\u003c/code\u003e\u003c/dt\u003e\n\u003cdd\u003eAll following siblings.\u003c/dd\u003e\n\u003cdt\u003e\u003ccode\u003epreviousAll()\u003c/code\u003e\u003c/dt\u003e\n\u003cdd\u003eAll preceding siblings.\u003c/dd\u003e\n\u003cdt\u003e\u003ccode\u003eparents()\u003c/code\u003e\u003c/dt\u003e\n\u003cdd\u003eReturns the parent nodes.\u003c/dd\u003e\n\u003cdt\u003e\u003ccode\u003echildren()\u003c/code\u003e\u003c/dt\u003e\n\u003cdd\u003eReturns children nodes.\u003c/dd\u003e\n\u003cdt\u003e\u003ccode\u003ereduce($lambda)\u003c/code\u003e\u003c/dt\u003e\n\u003cdd\u003eNodes for which the callable does not return false.\u003c/dd\u003e\n\u003c/dl\u003e\n\u003cp dir=\"auto\"\u003eSince each of these methods returns a new \u003ccode\u003eCrawler\u003c/code\u003e instance, you can\nnarrow down your node selection by chaining the method calls:\u003c/p\u003e\n\u003cpre\u003e$crawler\n -\u0026gt;filter('h1')\n -\u0026gt;reduce(function ($node, $i) {\n if (!$node-\u0026gt;getAttribute('class')) {\n return false;\n }\n })\n -\u0026gt;first()\n;\n\u003c/pre\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eTip\u003c/p\u003e\n\u003cp dir=\"auto\"\u003eUse the \u003ccode\u003ecount()\u003c/code\u003e function to get the number of nodes stored in a Crawler:\n\u003ccode\u003ecount($crawler)\u003c/code\u003e\u003c/p\u003e\n\u003c/div\u003e\n\u003ca name=\"user-content-extracting-information\"\u003e\u003c/a\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch4 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eExtracting Information\u003c/h4\u003e\u003ca id=\"user-content-extracting-information\" class=\"anchor\" aria-label=\"Permalink: Extracting Information\" href=\"#extracting-information\"\u003e\u003csvg class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eThe Crawler can extract information from the nodes:\u003c/p\u003e\n\u003cpre\u003e// Returns the attribute value for the first node\n$crawler-\u0026gt;attr('class');\n\n// Returns the node value for the first node\n$crawler-\u0026gt;text();\n\n// Extracts an array of attributes for all nodes\n// (_text returns the node value)\n// returns an array for each element in crawler,\n// each with the value and href\n$info = $crawler-\u0026gt;extract(array('_text', 'href'));\n\n// Executes a lambda for each node and return an array of results\n$data = $crawler-\u0026gt;each(function ($node, $i) {\n return $node-\u0026gt;attr('href');\n});\n\u003c/pre\u003e\n\u003ca name=\"user-content-links\"\u003e\u003c/a\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch4 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eLinks\u003c/h4\u003e\u003ca id=\"user-content-links\" class=\"anchor\" aria-label=\"Permalink: Links\" href=\"#links\"\u003e\u003csvg class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eTo select links, you can use the traversing methods above or the convenient\n\u003ccode\u003eselectLink()\u003c/code\u003e shortcut:\u003c/p\u003e\n\u003cpre\u003e$crawler-\u0026gt;selectLink('Click here');\n\u003c/pre\u003e\n\u003cp dir=\"auto\"\u003eThis selects all links that contain the given text, or clickable images for\nwhich the \u003ccode\u003ealt\u003c/code\u003e attribute contains the given text. Like the other filtering\nmethods, this returns another \u003ccode\u003eCrawler\u003c/code\u003e object.\u003c/p\u003e\n\u003cp dir=\"auto\"\u003eOnce you've selected a link, you have access to a special \u003ccode\u003eLink\u003c/code\u003e object,\nwhich has helpful methods specific to links (such as \u003ccode\u003egetMethod()\u003c/code\u003e and\n\u003ccode\u003egetUri()\u003c/code\u003e). To click on the link, use the Client's \u003ccode\u003eclick()\u003c/code\u003e method\nand pass it a \u003ccode\u003eLink\u003c/code\u003e object:\u003c/p\u003e\n\u003cpre\u003e$link = $crawler-\u0026gt;selectLink('Click here')-\u0026gt;link();\n\n$client-\u0026gt;click($link);\n\u003c/pre\u003e\n\u003ca name=\"user-content-forms\"\u003e\u003c/a\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch4 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eForms\u003c/h4\u003e\u003ca id=\"user-content-forms\" class=\"anchor\" aria-label=\"Permalink: Forms\" href=\"#forms\"\u003e\u003csvg class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eForms can be selected using their buttons, which can be selected with the\n\u003ccode\u003eselectButton()\u003c/code\u003e method, just like links:\u003c/p\u003e\n\u003cpre\u003e$buttonCrawlerNode = $crawler-\u0026gt;selectButton('submit');\n\u003c/pre\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eNote\u003c/p\u003e\n\u003cp dir=\"auto\"\u003eNotice that you select form buttons and not forms as a form can have several\nbuttons; if you use the traversing API, keep in mind that you must look for a\nbutton.\u003c/p\u003e\n\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eThe \u003ccode\u003eselectButton()\u003c/code\u003e method can select \u003ccode\u003ebutton\u003c/code\u003e tags and submit \u003ccode\u003einput\u003c/code\u003e\ntags. It uses several parts of the buttons to find them:\u003c/p\u003e\n\u003cul dir=\"auto\"\u003e\n\u003cli\u003eThe \u003ccode\u003evalue\u003c/code\u003e attribute value;\u003c/li\u003e\n\u003cli\u003eThe \u003ccode\u003eid\u003c/code\u003e or \u003ccode\u003ealt\u003c/code\u003e attribute value for images;\u003c/li\u003e\n\u003cli\u003eThe \u003ccode\u003eid\u003c/code\u003e or \u003ccode\u003ename\u003c/code\u003e attribute value for \u003ccode\u003ebutton\u003c/code\u003e tags.\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp dir=\"auto\"\u003eOnce you have a Crawler representing a button, call the \u003ccode\u003eform()\u003c/code\u003e method\nto get a \u003ccode\u003eForm\u003c/code\u003e instance for the form wrapping the button node:\u003c/p\u003e\n\u003cpre\u003e$form = $buttonCrawlerNode-\u0026gt;form();\n\u003c/pre\u003e\n\u003cp dir=\"auto\"\u003eWhen calling the \u003ccode\u003eform()\u003c/code\u003e method, you can also pass an array of field values\nthat overrides the default ones:\u003c/p\u003e\n\u003cpre\u003e$form = $buttonCrawlerNode-\u0026gt;form(array(\n 'name' =\u0026gt; 'Fabien',\n 'my_form[subject]' =\u0026gt; 'Symfony rocks!',\n));\n\u003c/pre\u003e\n\u003cp dir=\"auto\"\u003eAnd if you want to simulate a specific HTTP method for the form, pass it as a\nsecond argument:\u003c/p\u003e\n\u003cpre\u003e$form = $buttonCrawlerNode-\u0026gt;form(array(), 'DELETE');\n\u003c/pre\u003e\n\u003cp dir=\"auto\"\u003eThe Client can submit \u003ccode\u003eForm\u003c/code\u003e instances:\u003c/p\u003e\n\u003cpre\u003e$client-\u0026gt;submit($form);\n\u003c/pre\u003e\n\u003cp dir=\"auto\"\u003eThe field values can also be passed as a second argument of the \u003ccode\u003esubmit()\u003c/code\u003e\nmethod:\u003c/p\u003e\n\u003cpre\u003e$client-\u0026gt;submit($form, array(\n 'name' =\u0026gt; 'Fabien',\n 'my_form[subject]' =\u0026gt; 'Symfony rocks!',\n));\n\u003c/pre\u003e\n\u003cp dir=\"auto\"\u003eFor more complex situations, use the \u003ccode\u003eForm\u003c/code\u003e instance as an array to set the\nvalue of each field individually:\u003c/p\u003e\n\u003cpre\u003e// Change the value of a field\n$form['name'] = 'Fabien';\n$form['my_form[subject]'] = 'Symfony rocks!';\n\u003c/pre\u003e\n\u003cp dir=\"auto\"\u003eThere is also a nice API to manipulate the values of the fields according to\ntheir type:\u003c/p\u003e\n\u003cpre\u003e// Select an option or a radio\n$form['country']-\u0026gt;select('France');\n\n// Tick a checkbox\n$form['like_symfony']-\u0026gt;tick();\n\n// Upload a file\n$form['photo']-\u0026gt;upload('/path/to/lucas.jpg');\n\u003c/pre\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eTip\u003c/p\u003e\n\u003cp dir=\"auto\"\u003eIf you purposefully want to select \"invalid\" select/radio values, see\n\u003ca href=\"#id9\"\u003e\u003cspan id=\"user-content-id10\"\u003e:ref:`components-dom-crawler-invalid`\u003c/span\u003e\u003c/a\u003e.\u003c/p\u003e\n\u003c/div\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eTip\u003c/p\u003e\n\u003cp dir=\"auto\"\u003eYou can get the values that will be submitted by calling the \u003ccode\u003egetValues()\u003c/code\u003e\nmethod on the \u003ccode\u003eForm\u003c/code\u003e object. The uploaded files are available in a\nseparate array returned by \u003ccode\u003egetFiles()\u003c/code\u003e. The \u003ccode\u003egetPhpValues()\u003c/code\u003e and\n\u003ccode\u003egetPhpFiles()\u003c/code\u003e methods also return the submitted values, but in the\nPHP format (it converts the keys with square brackets notation - e.g.\n\u003ccode\u003emy_form[subject]\u003c/code\u003e - to PHP arrays).\u003c/p\u003e\n\u003c/div\u003e\n\u003ca name=\"user-content-adding-and-removing-forms-to-a-collection\"\u003e\u003c/a\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch5 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eAdding and Removing Forms to a Collection\u003c/h5\u003e\u003ca id=\"user-content-adding-and-removing-forms-to-a-collection\" class=\"anchor\" aria-label=\"Permalink: Adding and Removing Forms to a Collection\" href=\"#adding-and-removing-forms-to-a-collection\"\u003e\u003csvg class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eIf you use a \u003ca href=\"#id11\"\u003e\u003cspan id=\"user-content-id12\"\u003e:doc:`Collection of Forms \u0026lt;/form/form_collections\u0026gt;`\u003c/span\u003e\u003c/a\u003e,\nyou can't add fields to an existing form with\n\u003ccode\u003e$form['task[tags][0][name]'] = 'foo';\u003c/code\u003e. This results in an error\n\u003ccode\u003eUnreachable field \"…\"\u003c/code\u003e because \u003ccode\u003e$form\u003c/code\u003e can only be used in order to\nset values of existing fields. In order to add new fields, you have to\nadd the values to the raw data array:\u003c/p\u003e\n\u003cpre\u003e// Get the form.\n$form = $crawler-\u0026gt;filter('button')-\u0026gt;form();\n\n// Get the raw values.\n$values = $form-\u0026gt;getPhpValues();\n\n// Add fields to the raw values.\n$values['task']['tags'][0]['name'] = 'foo';\n$values['task']['tags'][1]['name'] = 'bar';\n\n// Submit the form with the existing and new values.\n$crawler = $this-\u0026gt;client-\u0026gt;request($form-\u0026gt;getMethod(), $form-\u0026gt;getUri(), $values,\n $form-\u0026gt;getPhpFiles());\n\n// The 2 tags have been added to the collection.\n$this-\u0026gt;assertEquals(2, $crawler-\u0026gt;filter('ul.tags \u0026gt; li')-\u0026gt;count());\n\u003c/pre\u003e\n\u003cp dir=\"auto\"\u003eWhere \u003ccode\u003etask[tags][0][name]\u003c/code\u003e is the name of a field created\nwith JavaScript.\u003c/p\u003e\n\u003cp dir=\"auto\"\u003eYou can remove an existing field, e.g. a tag:\u003c/p\u003e\n\u003cpre\u003e// Get the values of the form.\n$values = $form-\u0026gt;getPhpValues();\n\n// Remove the first tag.\nunset($values['task']['tags'][0]);\n\n// Submit the data.\n$crawler = $client-\u0026gt;request($form-\u0026gt;getMethod(), $form-\u0026gt;getUri(),\n $values, $form-\u0026gt;getPhpFiles());\n\n// The tag has been removed.\n$this-\u0026gt;assertEquals(0, $crawler-\u0026gt;filter('ul.tags \u0026gt; li')-\u0026gt;count());\n\u003c/pre\u003e\n\u003cpre\u003e.. index::\n pair: Tests; Configuration\n\n\u003c/pre\u003e\n\u003ca name=\"user-content-testing-configuration\"\u003e\u003c/a\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch3 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eTesting Configuration\u003c/h3\u003e\u003ca id=\"user-content-testing-configuration\" class=\"anchor\" aria-label=\"Permalink: Testing Configuration\" href=\"#testing-configuration\"\u003e\u003csvg class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eThe Client used by functional tests creates a Kernel that runs in a special\n\u003ccode\u003etest\u003c/code\u003e environment. Since Symfony loads the \u003ccode\u003eapp/config/config_test.yml\u003c/code\u003e\nin the \u0
6BBC
03ccode\u003etest\u003c/code\u003e environment, you can tweak any of your application's settings\nspecifically for testing.\u003c/p\u003e\n\u003cp dir=\"auto\"\u003eFor example, by default, the Swift Mailer is configured to \u003cem\u003enot\u003c/em\u003e actually\ndeliver emails in the \u003ccode\u003etest\u003c/code\u003e environment. You can see this under the \u003ccode\u003eswiftmailer\u003c/code\u003e\nconfiguration option:\u003c/p\u003e\n\u003cpre\u003e.. configuration-block::\n\n .. code-block:: yaml\n\n # app/config/config_test.yml\n\n # ...\n swiftmailer:\n disable_delivery: true\n\n .. code-block:: xml\n\n \u0026lt;!-- app/config/config_test.xml --\u0026gt;\n \u0026lt;?xml version=\"1.0\" encoding=\"UTF-8\" ?\u0026gt;\n \u0026lt;container xmlns=\"http://symfony.com/schema/dic/services\"\n xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n xmlns:swiftmailer=\"http://symfony.com/schema/dic/swiftmailer\"\n xsi:schemaLocation=\"http://symfony.com/schema/dic/services\n http://symfony.com/schema/dic/services/services-1.0.xsd\n http://symfony.com/schema/dic/swiftmailer\n http://symfony.com/schema/dic/swiftmailer/swiftmailer-1.0.xsd\"\u0026gt;\n\n \u0026lt;!-- ... --\u0026gt;\n \u0026lt;swiftmailer:config disable-delivery=\"true\" /\u0026gt;\n \u0026lt;/container\u0026gt;\n\n .. code-block:: php\n\n // app/config/config_test.php\n\n // ...\n $container-\u0026gt;loadFromExtension('swiftmailer', array(\n 'disable_delivery' =\u0026gt; true,\n ));\n\n\u003c/pre\u003e\n\u003cp dir=\"auto\"\u003eYou can also use a different environment entirely, or override the default\ndebug mode (\u003ccode\u003etrue\u003c/code\u003e) by passing each as options to the \u003ccode\u003ecreateClient()\u003c/code\u003e\nmethod:\u003c/p\u003e\n\u003cpre\u003e$client = static::createClient(array(\n 'environment' =\u0026gt; 'my_test_env',\n 'debug' =\u0026gt; false,\n));\n\u003c/pre\u003e\n\u003cp dir=\"auto\"\u003eIf your application behaves according to some HTTP headers, pass them as the\nsecond argument of \u003ccode\u003ecreateClient()\u003c/code\u003e:\u003c/p\u003e\n\u003cpre\u003e$client = static::createClient(array(), array(\n 'HTTP_HOST' =\u0026gt; 'en.example.com',\n 'HTTP_USER_AGENT' =\u0026gt; 'MySuperBrowser/1.0',\n));\n\u003c/pre\u003e\n\u003cp dir=\"auto\"\u003eYou can also override HTTP headers on a per request basis:\u003c/p\u003e\n\u003cpre\u003e$client-\u0026gt;request('GET', '/', array(), array(), array(\n 'HTTP_HOST' =\u0026gt; 'en.example.com',\n 'HTTP_USER_AGENT' =\u0026gt; 'MySuperBrowser/1.0',\n));\n\u003c/pre\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eTip\u003c/p\u003e\n\u003cp dir=\"auto\"\u003eThe test client is available as a service in the container in the \u003ccode\u003etest\u003c/code\u003e\nenvironment (or wherever the \u003ca href=\"#id13\"\u003e\u003cspan id=\"user-content-id14\"\u003e:ref:`framework.test \u0026lt;reference-framework-test\u0026gt;`\u003c/span\u003e\u003c/a\u003e\noption is enabled). This means you can override the service entirely\nif you need to.\u003c/p\u003e\n\u003c/div\u003e\n\u003cpre\u003e.. index::\n pair: PHPUnit; Configuration\n\n\u003c/pre\u003e\n\u003ca name=\"user-content-phpunit-configuration\"\u003e\u003c/a\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch4 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003ePHPUnit Configuration\u003c/h4\u003e\u003ca id=\"user-content-phpunit-configuration\" class=\"anchor\" aria-label=\"Permalink: PHPUnit Configuration\" href=\"#phpunit-configuration\"\u003e\u003csvg class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eEach application has its own PHPUnit configuration, stored in the\n\u003ccode\u003eapp/phpunit.xml.dist\u003c/code\u003e file. You can edit this file to change the defaults or\ncreate an \u003ccode\u003eapp/phpunit.xml\u003c/code\u003e file to set up a configuration for your local\nmachine only.\u003c/p\u003e\n\u003cdiv dir=\"auto\"\u003e\n\u003cp dir=\"auto\"\u003eTip\u003c/p\u003e\n\u003cp dir=\"auto\"\u003eStore the \u003ccode\u003eapp/phpunit.xml.dist\u003c/code\u003e file in your code repository and ignore\nthe \u003ccode\u003eapp/phpunit.xml\u003c/code\u003e file.\u003c/p\u003e\n\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eBy default, only the tests from your own custom bundles stored in the standard\ndirectories \u003ccode\u003esrc/*/*Bundle/Tests\u003c/code\u003e, \u003ccode\u003esrc/*/Bundle/*Bundle/Tests\u003c/code\u003e,\n\u003ccode\u003esrc/*Bundle/Tests\u003c/code\u003e are run by the \u003ccode\u003ephpunit\u003c/code\u003e command, as configured\nin the \u003ccode\u003eapp/phpunit.xml.dist\u003c/code\u003e file:\u003c/p\u003e\n\u003cdiv class=\"highlight highlight-text-xml notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"\u0026lt;!-- app/phpunit.xml.dist --\u0026gt;\n\u0026lt;phpunit\u0026gt;\n \u0026lt;!-- ... --\u0026gt;\n \u0026lt;testsuites\u0026gt;\n \u0026lt;testsuite name=\u0026quot;Project Test Suite\u0026quot;\u0026gt;\n \u0026lt;directory\u0026gt;../src/*/*Bundle/Tests\u0026lt;/directory\u0026gt;\n \u0026lt;directory\u0026gt;../src/*/Bundle/*Bundle/Tests\u0026lt;/directory\u0026gt;\n \u0026lt;directory\u0026gt;../src/*Bundle/Tests\u0026lt;/directory\u0026gt;\n \u0026lt;/testsuite\u0026gt;\n \u0026lt;/testsuites\u0026gt;\n \u0026lt;!-- ... --\u0026gt;\n\u0026lt;/phpunit\u0026gt;\"\u003e\u003cpre\u003e\u003cspan class=\"pl-c\"\u003e\u003cspan class=\"pl-c\"\u003e\u0026lt;!--\u003c/span\u003e app/phpunit.xml.dist \u003cspan class=\"pl-c\"\u003e--\u0026gt;\u003c/span\u003e\u003c/span\u003e\n\u0026lt;\u003cspan class=\"pl-ent\"\u003ephpunit\u003c/span\u003e\u0026gt;\n \u003cspan class=\"pl-c\"\u003e\u003cspan class=\"pl-c\"\u003e\u0026lt;!--\u003c/span\u003e ... \u003cspan class=\"pl-c\"\u003e--\u0026gt;\u003c/span\u003e\u003c/span\u003e\n \u0026lt;\u003cspan class=\"pl-ent\"\u003etestsuites\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003etestsuite\u003c/span\u003e \u003cspan class=\"pl-e\"\u003ename\u003c/span\u003e=\u003cspan class=\"pl-s\"\u003e\u003cspan class=\"pl-pds\"\u003e\"\u003c/span\u003eProject Test Suite\u003cspan class=\"pl-pds\"\u003e\"\u003c/span\u003e\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003edirectory\u003c/span\u003e\u0026gt;../src/*/*Bundle/Tests\u0026lt;/\u003cspan class=\"pl-ent\"\u003edirectory\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003edirectory\u003c/span\u003e\u0026gt;../src/*/Bundle/*Bundle/Tests\u0026lt;/\u003cspan class=\"pl-ent\"\u003edirectory\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003edirectory\u003c/span\u003e\u0026gt;../src/*Bundle/Tests\u0026lt;/\u003cspan class=\"pl-ent\"\u003edirectory\u003c/span\u003e\u0026gt;\n \u0026lt;/\u003cspan class=\"pl-ent\"\u003etestsuite\u003c/span\u003e\u0026gt;\n \u0026lt;/\u003cspan class=\"pl-ent\"\u003etestsuites\u003c/span\u003e\u0026gt;\n \u003cspan class=\"pl-c\"\u003e\u003cspan class=\"pl-c\"\u003e\u0026lt;!--\u003c/span\u003e ... \u003cspan class=\"pl-c\"\u003e--\u0026gt;\u003c/span\u003e\u003c/span\u003e\n\u0026lt;/\u003cspan class=\"pl-ent\"\u003ephpunit\u003c/span\u003e\u0026gt;\u003c/pre\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eBut you can easily add more directories. For instance, the following\nconfiguration adds tests from a custom \u003ccode\u003elib/tests\u003c/code\u003e directory:\u003c/p\u003e\n\u003cdiv class=\"highlight highlight-text-xml notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"\u0026lt;!-- app/phpunit.xml.dist --\u0026gt;\n\u0026lt;phpunit\u0026gt;\n \u0026lt;!-- ... --\u0026gt;\n \u0026lt;testsuites\u0026gt;\n \u0026lt;testsuite name=\u0026quot;Project Test Suite\u0026quot;\u0026gt;\n \u0026lt;!-- ... ---\u0026gt;\n \u0026lt;directory\u0026gt;../lib/tests\u0026lt;/directory\u0026gt;\n \u0026lt;/testsuite\u0026gt;\n \u0026lt;/testsuites\u0026gt;\n \u0026lt;!-- ... ---\u0026gt;\n\u0026lt;/phpunit\u0026gt;\"\u003e\u003cpre\u003e\u003cspan class=\"pl-c\"\u003e\u003cspan class=\"pl-c\"\u003e\u0026lt;!--\u003c/span\u003e app/phpunit.xml.dist \u003cspan class=\"pl-c\"\u003e--\u0026gt;\u003c/span\u003e\u003c/span\u003e\n\u0026lt;\u003cspan class=\"pl-ent\"\u003ephpunit\u003c/span\u003e\u0026gt;\n \u003cspan class=\"pl-c\"\u003e\u003cspan class=\"pl-c\"\u003e\u0026lt;!--\u003c/span\u003e ... \u003cspan class=\"pl-c\"\u003e--\u0026gt;\u003c/span\u003e\u003c/span\u003e\n \u0026lt;\u003cspan class=\"pl-ent\"\u003etestsuites\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003etestsuite\u003c/span\u003e \u003cspan class=\"pl-e\"\u003ename\u003c/span\u003e=\u003cspan class=\"pl-s\"\u003e\u003cspan class=\"pl-pds\"\u003e\"\u003c/span\u003eProject Test Suite\u003cspan class=\"pl-pds\"\u003e\"\u003c/span\u003e\u003c/span\u003e\u0026gt;\n \u003cspan class=\"pl-c\"\u003e\u003cspan class=\"pl-c\"\u003e\u0026lt;!--\u003c/span\u003e ... -\u003cspan class=\"pl-c\"\u003e--\u0026gt;\u003c/span\u003e\u003c/span\u003e\n \u0026lt;\u003cspan class=\"pl-ent\"\u003edirectory\u003c/span\u003e\u0026gt;../lib/tests\u0026lt;/\u003cspan class=\"pl-ent\"\u003edirectory\u003c/span\u003e\u0026gt;\n \u0026lt;/\u003cspan class=\"pl-ent\"\u003etestsuite\u003c/span\u003e\u0026gt;\n \u0026lt;/\u003cspan class=\"pl-ent\"\u003etestsuites\u003c/span\u003e\u0026gt;\n \u003cspan class=\"pl-c\"\u003e\u003cspan class=\"pl-c\"\u003e\u0026lt;!--\u003c/span\u003e ... -\u003cspan class=\"pl-c\"\u003e--\u0026gt;\u003c/span\u003e\u003c/span\u003e\n\u0026lt;/\u003cspan class=\"pl-ent\"\u003ephpunit\u003c/span\u003e\u0026gt;\u003c/pre\u003e\u003c/div\u003e\n\u003cp dir=\"auto\"\u003eTo include other directories in the code coverage, also edit the \u003ccode\u003e\u0026lt;filter\u0026gt;\u003c/code\u003e\nsection:\u003c/p\u003e\n\u003cdiv class=\"highlight highlight-text-xml notranslate position-relative overflow-auto\" dir=\"auto\" data-snippet-clipboard-copy-content=\"\u0026lt;!-- app/phpunit.xml.dist --\u0026gt;\n\u0026lt;phpunit\u0026gt;\n \u0026lt;!-- ... --\u0026gt;\n \u0026lt;filter\u0026gt;\n \u0026lt;whitelist\u0026gt;\n \u0026lt;!-- ... --\u0026gt;\n \u0026lt;directory\u0026gt;../lib\u0026lt;/directory\u0026gt;\n \u0026lt;exclude\u0026gt;\n \u0026lt;!-- ... --\u0026gt;\n \u0026lt;directory\u0026gt;../lib/tests\u0026lt;/directory\u0026gt;\n \u0026lt;/exclude\u0026gt;\n \u0026lt;/whitelist\u0026gt;\n \u0026lt;/filter\u0026gt;\n \u0026lt;!-- ... ---\u0026gt;\n\u0026lt;/phpunit\u0026gt;\"\u003e\u003cpre\u003e\u003cspan class=\"pl-c\"\u003e\u003cspan class=\"pl-c\"\u003e\u0026lt;!--\u003c/span\u003e app/phpunit.xml.dist \u003cspan class=\"pl-c\"\u003e--\u0026gt;\u003c/span\u003e\u003c/span\u003e\n\u0026lt;\u003cspan class=\"pl-ent\"\u003ephpunit\u003c/span\u003e\u0026gt;\n \u003cspan class=\"pl-c\"\u003e\u003cspan class=\"pl-c\"\u003e\u0026lt;!--\u003c/span\u003e ... \u003cspan class=\"pl-c\"\u003e--\u0026gt;\u003c/span\u003e\u003c/span\u003e\n \u0026lt;\u003cspan class=\"pl-ent\"\u003efilter\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003ewhitelist\u003c/span\u003e\u0026gt;\n \u003cspan class=\"pl-c\"\u003e\u003cspan class=\"pl-c\"\u003e\u0026lt;!--\u003c/span\u003e ... \u003cspan class=\"pl-c\"\u003e--\u0026gt;\u003c/span\u003e\u003c/span\u003e\n \u0026lt;\u003cspan class=\"pl-ent\"\u003edirectory\u003c/span\u003e\u0026gt;../lib\u0026lt;/\u003cspan class=\"pl-ent\"\u003edirectory\u003c/span\u003e\u0026gt;\n \u0026lt;\u003cspan class=\"pl-ent\"\u003eexclude\u003c/span\u003e\u0026gt;\n \u003cspan class=\"pl-c\"\u003e\u003cspan class=\"pl-c\"\u003e\u0026lt;!--\u003c/span\u003e ... \u003cspan class=\"pl-c\"\u003e--\u0026gt;\u003c/span\u003e\u003c/span\u003e\n \u0026lt;\u003cspan class=\"pl-ent\"\u003edirectory\u003c/span\u003e\u0026gt;../lib/tests\u0026lt;/\u003cspan class=\"pl-ent\"\u003edirectory\u003c/span\u003e\u0026gt;\n \u0026lt;/\u003cspan class=\"pl-ent\"\u003eexclude\u003c/span\u003e\u0026gt;\n \u0026lt;/\u003cspan class=\"pl-ent\"\u003ewhitelist\u003c/span\u003e\u0026gt;\n \u0026lt;/\u003cspan class=\"pl-ent\"\u003efilter\u003c/span\u003e\u0026gt;\n \u003cspan class=\"pl-c\"\u003e\u003cspan class=\"pl-c\"\u003e\u0026lt;!--\u003c/span\u003e ... -\u003cspan class=\"pl-c\"\u003e--\u0026gt;\u003c/span\u003e\u003c/span\u003e\n\u0026lt;/\u003cspan class=\"pl-ent\"\u003ephpunit\u003c/span\u003e\u0026gt;\u003c/pre\u003e\u003c/div\u003e\n\u003ca name=\"user-content-learn-more\"\u003e\u003c/a\u003e\n\u003cdiv class=\"markdown-heading\" dir=\"auto\"\u003e\u003ch3 tabindex=\"-1\" class=\"heading-element\" dir=\"auto\"\u003eLearn more\u003c/h3\u003e\u003ca id=\"user-content-learn-more\" class=\"anchor\" aria-label=\"Permalink: Learn more\" href=\"#learn-more\"\u003e\u003csvg class=\"octicon octicon-link\" viewBox=\"0 0 16 16\" version=\"1.1\" width=\"16\" height=\"16\" aria-hidden=\"true\"\u003e\u003cpath d=\"m7.775 3.275 1.25-1.25a3.5 3.5 0 1 1 4.95 4.95l-2.5 2.5a3.5 3.5 0 0 1-4.95 0 .751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018 1.998 1.998 0 0 0 2.83 0l2.5-2.5a2.002 2.002 0 0 0-2.83-2.83l-1.25 1.25a.751.751 0 0 1-1.042-.018.751.751 0 0 1-.018-1.042Zm-4.69 9.64a1.998 1.998 0 0 0 2.83 0l1.25-1.25a.751.751 0 0 1 1.042.018.751.751 0 0 1 .018 1.042l-1.25 1.25a3.5 3.5 0 1 1-4.95-4.95l2.5-2.5a3.5 3.5 0 0 1 4.95 0 .751.751 0 0 1-.018 1.042.751.751 0 0 1-1.042.018 1.998 1.998 0 0 0-2.83 0l-2.5 2.5a1.998 1.998 0 0 0 0 2.83Z\"\u003e\u003c/path\u003e\u003c/svg\u003e\u003c/a\u003e\u003c/div\u003e\n\u003cpre\u003e.. toctree::\n :maxdepth: 1\n :glob:\n\n testing/*\n\n\u003c/pre\u003e\n\u003cul dir=\"auto\"\u003e\n\u003cli\u003e\u003ca href=\"#id15\"\u003e\u003cspan id=\"user-content-id16\"\u003e:doc:`The chapter about tests in the Symfony Framework Best Practices \u0026lt;/best_practices/tests\u0026gt;`\u003c/span\u003e\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"#id17\"\u003e\u003cspan id=\"user-content-id18\"\u003e:doc:`/components/dom_crawler`\u003c/span\u003e\u003c/a\u003e\u003c/li\u003e\n\u003cli\u003e\u003ca href=\"#id19\"\u003e\u003cspan id=\"user-content-id20\"\u003e:doc:`/components/css_selector`\u003c/span\u003e\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\n\u003c/article\u003e","renderedFileInfo":null,"shortPath":null,"symbolsEnabled":true,"tabSize":8,"topBannersInfo":{"overridingGlobalFundingFile":false,"globalPreferredFundingPath":"/symfony/.github/blob/6f2ca452c856184a28812bb364b4e34ed50309da/FUNDING.yml","showInvalidCitationWarning":false,"citationHelpUrl":"https://docs.github.com/github/creating-cloning-and-archiving-repositories/creating-a-repository-on-github/about-citation-files","actionsOnboardingTip":null},"truncated":false,"viewable":true,"workflowRedirectUrl":null,"symbols":null},"copilotInfo":null,"copilotAccessAllowed":false,"modelsAccessAllowed":false,"modelsRepoIntegrationEnabled":false,"csrf_tokens":{"/symfony/symfony-docs/branches":{"post":"SSoAlY4qQZPjmXfGy8DPkb1sCkerK_IV3jV2v-w-NR0ugNkiOQGViv157RHQWQ-f7R7fedIvSa2QI7TQYkA6NA"},"/repos/preferences":{"post":"5XLTGGeE4SCqebgwtYZAb4HBeD1p7kSr4rAOMrX0AxuCaDE1rTV3pquw7IaJAIL1gLhx33cKojeYYGTizDNDaA"}}},"title":"symfony-docs/testing.rst at 227fe4cb35ea54303bd54a1e734d9d731c8715ed · symfony/symfony-docs","appPayload":{"helpUrl":"https://docs.github.com","findFileWorkerPath":"/assets-cdn/worker/find-file-worker-263cab1760dd.js","findInFileWorkerPath":"/assets-cdn/worker/find-in-file-worker-1b17b3e7786a.js","githubDevUrl":null,"enabled_features":{"code_nav_ui_events":false,"react_blob_overlay":false,"accessible_code_button":true}}}