{"id":625,"date":"2025-09-02T11:57:09","date_gmt":"2025-09-02T11:57:09","guid":{"rendered":"https:\/\/1v0.net\/blog\/?p=625"},"modified":"2025-09-02T20:05:14","modified_gmt":"2025-09-02T20:05:14","slug":"how-to-use-laravel-dusk-for-browser-testing","status":"publish","type":"post","link":"https:\/\/1v0.net\/blog\/how-to-use-laravel-dusk-for-browser-testing\/","title":{"rendered":"How to Use Laravel Dusk for Browser Testing"},"content":{"rendered":"\n<p>End-to-end browser testing ensures that your application works exactly as a user would experience it. Laravel Dusk provides a simple API to automate browser actions like clicking links, filling forms, and asserting page content \u2014 all without writing raw Selenium code. In this article, we\u2019ll set up Dusk, write real tests for authentication and forms, handle JavaScript interactions, capture screenshots and logs, and explore best practices for organizing browser tests in large projects.<\/p>\n\n\n\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Installing and Setting Up Laravel Dusk<\/strong><\/h2>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"Bash\" data-shcb-language-slug=\"bash\"><span><code class=\"hljs language-bash\">composer require --dev laravel\/dusk\nphp artisan dusk:install<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Bash<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">bash<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>After installation, Dusk creates a <code>tests\/Browser<\/code> directory with example tests, a <code>DuskTestCase.php<\/code> base class, and updates your <code>phpunit.xml<\/code> to include the Dusk environment. Run Dusk tests with <code>php artisan dusk<\/code>. The package ships with a bundled ChromeDriver binary for headless or full-browser testing.<\/p>\n\n\n\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Writing Your First Dusk Test<\/strong><\/h2>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-comment\">\/\/ tests\/Browser\/ExampleTest.php<\/span>\n<span class=\"hljs-keyword\">namespace<\/span> <span class=\"hljs-title\">Tests<\/span>\\<span class=\"hljs-title\">Browser<\/span>;\n\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Laravel<\/span>\\<span class=\"hljs-title\">Dusk<\/span>\\<span class=\"hljs-title\">Browser<\/span>;\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Tests<\/span>\\<span class=\"hljs-title\">DuskTestCase<\/span>;\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">ExampleTest<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">DuskTestCase<\/span>\n<\/span>{\n    <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">test_homepage_loads<\/span><span class=\"hljs-params\">()<\/span>: <span class=\"hljs-title\">void<\/span>\n    <\/span>{\n        <span class=\"hljs-keyword\">$this<\/span>-&gt;browse(<span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-params\">(Browser $browser)<\/span> <\/span>{\n            $browser-&gt;visit(<span class=\"hljs-string\">'\/'<\/span>)\n                -&gt;assertSee(<span class=\"hljs-string\">'Welcome'<\/span>)\n                -&gt;assertTitleContains(<span class=\"hljs-string\">'MyApp'<\/span>);\n        });\n    }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>This simple test checks that the homepage loads, the word \u201cWelcome\u201d is visible, and the page title contains \u201cMyApp.\u201d Dusk uses a fluent API to chain actions and assertions.<\/p>\n\n\n\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Testing Authentication Flows<\/strong><\/h2>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-comment\">\/\/ tests\/Browser\/LoginTest.php<\/span>\n<span class=\"hljs-keyword\">namespace<\/span> <span class=\"hljs-title\">Tests<\/span>\\<span class=\"hljs-title\">Browser<\/span>;\n\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">App<\/span>\\<span class=\"hljs-title\">Models<\/span>\\<span class=\"hljs-title\">User<\/span>;\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Laravel<\/span>\\<span class=\"hljs-title\">Dusk<\/span>\\<span class=\"hljs-title\">Browser<\/span>;\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Tests<\/span>\\<span class=\"hljs-title\">DuskTestCase<\/span>;\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">LoginTest<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">DuskTestCase<\/span>\n<\/span>{\n    <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">test_user_can_login<\/span><span class=\"hljs-params\">()<\/span>: <span class=\"hljs-title\">void<\/span>\n    <\/span>{\n        $user = User::factory()-&gt;create(&#91;\n            <span class=\"hljs-string\">'email'<\/span> =&gt; <span class=\"hljs-string\">'dusk@example.com'<\/span>,\n            <span class=\"hljs-string\">'password'<\/span> =&gt; bcrypt(<span class=\"hljs-string\">'secret'<\/span>),\n        ]);\n\n        <span class=\"hljs-keyword\">$this<\/span>-&gt;browse(<span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-params\">(Browser $browser)<\/span> <span class=\"hljs-title\">use<\/span> <span class=\"hljs-params\">($user)<\/span> <\/span>{\n            $browser-&gt;visit(<span class=\"hljs-string\">'\/login'<\/span>)\n                -&gt;type(<span class=\"hljs-string\">'email'<\/span>, $user-&gt;email)\n                -&gt;type(<span class=\"hljs-string\">'password'<\/span>, <span class=\"hljs-string\">'secret'<\/span>)\n                -&gt;press(<span class=\"hljs-string\">'Login'<\/span>)\n                -&gt;assertPathIs(<span class=\"hljs-string\">'\/dashboard'<\/span>)\n                -&gt;assertSee($user-&gt;name);\n        });\n    }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>This Dusk test creates a user with a factory, fills in the login form, presses the login button, and verifies redirection to the dashboard with the user\u2019s name visible. This mimics the exact user workflow in the browser.<\/p>\n\n\n\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Handling JavaScript and Vue\/React Components<\/strong><\/h2>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-keyword\">$this<\/span>-&gt;browse(<span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-params\">(Browser $browser)<\/span> <\/span>{\n    $browser-&gt;visit(<span class=\"hljs-string\">'\/posts\/create'<\/span>)\n        -&gt;type(<span class=\"hljs-string\">'title'<\/span>, <span class=\"hljs-string\">'Dusk Post'<\/span>)\n        -&gt;type(<span class=\"hljs-string\">'content'<\/span>, <span class=\"hljs-string\">'Testing with Dusk.'<\/span>)\n        -&gt;check(<span class=\"hljs-string\">'published'<\/span>)\n        -&gt;click(<span class=\"hljs-string\">'@save-button'<\/span>) <span class=\"hljs-comment\">\/\/ using dusk selectors<\/span>\n        -&gt;waitForText(<span class=\"hljs-string\">'Post created'<\/span>)\n        -&gt;assertSee(<span class=\"hljs-string\">'Dusk Post'<\/span>);\n});<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Dusk works seamlessly with JavaScript-heavy UIs built in Vue or React. Use <code>waitForText<\/code>, <code>waitFor<\/code>, and <code>@dusk<\/code> selectors in Blade to target specific elements: <code>&lt;button dusk=\"save-button\"&gt;Save&lt;\/button&gt;<\/code>. This ensures reliable tests even with dynamic content.<\/p>\n\n\n\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Capturing Screenshots and Console Logs<\/strong><\/h2>\n\n\n\n<p>Debugging browser tests is easier when you can see what happened at failure time. Laravel Dusk automatically captures screenshots on failure and stores them in <code>tests\/Browser\/screenshots<\/code>. You can also capture them manually or store console logs for deeper insights.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Manual Screenshots<\/strong><\/h3>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-keyword\">$this<\/span>-&gt;browse(<span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-params\">(Browser $browser)<\/span> <\/span>{\n    $browser-&gt;visit(<span class=\"hljs-string\">'\/profile'<\/span>)\n        -&gt;screenshot(<span class=\"hljs-string\">'profile-page'<\/span>); <span class=\"hljs-comment\">\/\/ saves to tests\/Browser\/screenshots<\/span>\n});<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Manual screenshots are useful in complex flows when you want to verify specific steps. Screenshots are stored as PNG files and can be viewed anytime after test runs.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Console Logs<\/strong><\/h3>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-keyword\">$this<\/span>-&gt;browse(<span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-params\">(Browser $browser)<\/span> <\/span>{\n    $browser-&gt;visit(<span class=\"hljs-string\">'\/dashboard'<\/span>);\n\n    $logs = $browser-&gt;driver-&gt;manage()-&gt;logs()-&gt;get(<span class=\"hljs-string\">'browser'<\/span>);\n    <span class=\"hljs-keyword\">foreach<\/span> ($logs <span class=\"hljs-keyword\">as<\/span> $log) {\n        dump($log-&gt;getMessage());\n    }\n});<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Accessing the underlying WebDriver allows you to retrieve JavaScript console logs. This is invaluable when diagnosing Vue\/React errors or failed AJAX calls that might not show in the DOM.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Video Recording (Third-Party)<\/strong><\/h3>\n\n\n\n<p>While not built into Dusk, you can run Dusk inside a container with tools like Selenium Grid + ffmpeg to record videos of test runs. This is useful in CI pipelines for diagnosing flaky tests.<\/p>\n\n\n\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Organizing Browser Tests<\/strong><\/h2>\n\n\n\n<p>For large projects, structure your browser tests just like your app\u2019s domains:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-7\" data-shcb-language-name=\"Bash\" data-shcb-language-slug=\"bash\"><span><code class=\"hljs language-bash\">tests\/Browser\/\n\u251c\u2500 Auth\/\n\u2502  \u251c\u2500 LoginTest.php\n\u2502  \u251c\u2500 RegisterTest.php\n\u251c\u2500 Blog\/\n\u2502  \u251c\u2500 CreatePostTest.php\n\u2502  \u251c\u2500 EditPostTest.php\n\u251c\u2500 Components\/\n\u2502  \u251c\u2500 ModalTest.php\n\u2502  \u251c\u2500 PaginationTest.php<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-7\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Bash<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">bash<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>This folder structure keeps test coverage modular and easier to navigate. For reusable actions, create custom <code>Page<\/code> objects with Dusk\u2019s <code>php artisan dusk:page<\/code> command.<\/p>\n\n\n\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Database and Session Handling<\/strong><\/h2>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-8\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-comment\">\/\/ Using DatabaseMigrations trait in a Dusk test<\/span>\n<span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">Illuminate<\/span>\\<span class=\"hljs-title\">Foundation<\/span>\\<span class=\"hljs-title\">Testing<\/span>\\<span class=\"hljs-title\">DatabaseMigrations<\/span>;\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">RegisterTest<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">DuskTestCase<\/span>\n<\/span>{\n    <span class=\"hljs-keyword\">use<\/span> <span class=\"hljs-title\">DatabaseMigrations<\/span>;\n\n    <span class=\"hljs-keyword\">public<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">test_user_can_register<\/span><span class=\"hljs-params\">()<\/span>: <span class=\"hljs-title\">void<\/span>\n    <\/span>{\n        <span class=\"hljs-keyword\">$this<\/span>-&gt;browse(<span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-params\">(Browser $browser)<\/span> <\/span>{\n            $browser-&gt;visit(<span class=\"hljs-string\">'\/register'<\/span>)\n                -&gt;type(<span class=\"hljs-string\">'name'<\/span>, <span class=\"hljs-string\">'Test User'<\/span>)\n                -&gt;type(<span class=\"hljs-string\">'email'<\/span>, <span class=\"hljs-string\">'newuser@example.com'<\/span>)\n                -&gt;type(<span class=\"hljs-string\">'password'<\/span>, <span class=\"hljs-string\">'password'<\/span>)\n                -&gt;type(<span class=\"hljs-string\">'password_confirmation'<\/span>, <span class=\"hljs-string\">'password'<\/span>)\n                -&gt;press(<span class=\"hljs-string\">'Register'<\/span>)\n                -&gt;assertPathIs(<span class=\"hljs-string\">'\/dashboard'<\/span>);\n        });\n    }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-8\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Use <code>DatabaseMigrations<\/code> or <code>RefreshDatabase<\/code> traits in your Dusk tests to start with a clean database every time. This ensures tests don\u2019t bleed state across runs.<\/p>\n\n\n\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Running Tests in CI\/CD Pipelines<\/strong><\/h2>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-9\" data-shcb-language-name=\"YAML\" data-shcb-language-slug=\"yaml\"><span><code class=\"hljs language-yaml\"><span class=\"hljs-comment\"># GitHub Actions example for running Laravel Dusk<\/span>\n<span class=\"hljs-attr\">jobs:<\/span>\n  <span class=\"hljs-attr\">dusk-tests:<\/span>\n    <span class=\"hljs-attr\">runs-on:<\/span> <span class=\"hljs-string\">ubuntu-latest<\/span>\n    <span class=\"hljs-attr\">services:<\/span>\n      <span class=\"hljs-attr\">mysql:<\/span>\n        <span class=\"hljs-attr\">image:<\/span> <span class=\"hljs-string\">mysql:8<\/span>\n        <span class=\"hljs-attr\">env:<\/span>\n          <span class=\"hljs-attr\">MYSQL_DATABASE:<\/span> <span class=\"hljs-string\">laravel<\/span>\n          <span class=\"hljs-attr\">MYSQL_ROOT_PASSWORD:<\/span> <span class=\"hljs-string\">root<\/span>\n        <span class=\"hljs-attr\">ports:<\/span>\n          <span class=\"hljs-bullet\">-<\/span> <span class=\"hljs-number\">3306<\/span><span class=\"hljs-string\">:3306<\/span>\n    <span class=\"hljs-attr\">steps:<\/span>\n      <span class=\"hljs-bullet\">-<\/span> <span class=\"hljs-attr\">uses:<\/span> <span class=\"hljs-string\">actions\/checkout@v4<\/span>\n      <span class=\"hljs-bullet\">-<\/span> <span class=\"hljs-attr\">uses:<\/span> <span class=\"hljs-string\">shivammathur\/setup-php@v2<\/span>\n        <span class=\"hljs-attr\">with:<\/span>\n          <span class=\"hljs-attr\">php-version:<\/span> <span class=\"hljs-string\">'8.3'<\/span>\n      <span class=\"hljs-bullet\">-<\/span> <span class=\"hljs-attr\">run:<\/span> <span class=\"hljs-string\">composer<\/span> <span class=\"hljs-string\">install<\/span>\n      <span class=\"hljs-bullet\">-<\/span> <span class=\"hljs-attr\">run:<\/span> <span class=\"hljs-string\">php<\/span> <span class=\"hljs-string\">artisan<\/span> <span class=\"hljs-string\">migrate<\/span>\n      <span class=\"hljs-bullet\">-<\/span> <span class=\"hljs-attr\">run:<\/span> <span class=\"hljs-string\">php<\/span> <span class=\"hljs-string\">artisan<\/span> <span class=\"hljs-string\">serve<\/span> <span class=\"hljs-string\">&amp;<\/span> <span class=\"hljs-comment\"># run server in background<\/span>\n      <span class=\"hljs-bullet\">-<\/span> <span class=\"hljs-attr\">run:<\/span> <span class=\"hljs-string\">php<\/span> <span class=\"hljs-string\">artisan<\/span> <span class=\"hljs-string\">dusk<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-9\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">YAML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">yaml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Dusk integrates with CI tools like GitHub Actions, GitLab CI, or Jenkins. Use headless mode for speed, or full browser mode when debugging with screenshots (<code>--env=chrome<\/code>).<\/p>\n\n\n\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Feature Tests vs Browser Tests<\/strong><\/h2>\n\n\n\n<p>Feature tests and Dusk browser tests often overlap, but they serve different purposes. Here\u2019s a comparison to decide when to use each:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Aspect<\/th><th>Feature Tests<\/th><th>Dusk Browser Tests<\/th><\/tr><\/thead><tbody><tr><td>Scope<\/td><td>HTTP layer, controllers, database<\/td><td>Full browser (DOM, JS, CSS, user flows)<\/td><\/tr><tr><td>Speed<\/td><td>Fast (no browser overhead)<\/td><td>Slower (ChromeDriver involved)<\/td><\/tr><tr><td>Best for<\/td><td>API responses, redirects, auth rules<\/td><td>Forms, clicks, Vue\/React interactions<\/td><\/tr><tr><td>Setup<\/td><td>No extra drivers<\/td><td>Requires ChromeDriver \/ headless setup<\/td><\/tr><tr><td>Example<\/td><td>Assert JSON returned from API<\/td><td>Click \u201cLogin\u201d and check UI redirect<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p>Use Feature Tests for logic and data integrity, and Browser Tests for validating the actual user experience in a browser.<\/p>\n\n\n\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Wrapping Up<\/strong><\/h2>\n\n\n\n<p>Laravel Dusk makes browser testing straightforward by combining expressive syntax with ChromeDriver. We covered installation, writing tests, handling authentication, JavaScript interactions, capturing screenshots, console logs, organizing test suites, database resets, and CI\/CD integration. With Dusk in place, you can confidently verify your app\u2019s real user experience and debug failures effectively.<\/p>\n\n\n\n<div style=\"height:100px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>What\u2019s Next<\/strong><\/h2>\n\n\n\n<p>Expand your testing skills with these guides:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"\/blog\/testing-laravel-applications-with-phpunit\">Testing Laravel Applications with PHPUnit<\/a><\/li>\n\n\n\n<li><a href=\"\/blog\/how-to-write-feature-tests-in-laravel-for-apis\">How to Write Feature Tests in Laravel for APIs<\/a><\/li>\n\n\n\n<li><a href=\"\/blog\/using-laravel-factories-and-seeders-for-test-data\">Using Laravel Factories and Seeders for Test Data<\/a><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>End-to-end browser testing ensures that your application works exactly as a user would experience it. Laravel Dusk provides a simple API to automate browser actions like clicking links, filling forms, and asserting page content \u2014 all without writing raw Selenium code. In this article, we\u2019ll set up Dusk, write real tests for authentication and forms, [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":629,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[7],"tags":[85,145,80,146,147,87],"class_list":["post-625","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-laravel","tag-automation","tag-browser","tag-ci-cd","tag-dusk","tag-javascript","tag-testing"],"_links":{"self":[{"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/posts\/625","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/comments?post=625"}],"version-history":[{"count":1,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/posts\/625\/revisions"}],"predecessor-version":[{"id":628,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/posts\/625\/revisions\/628"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/media\/629"}],"wp:attachment":[{"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/media?parent=625"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/categories?post=625"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/1v0.net\/blog\/wp-json\/wp\/v2\/tags?post=625"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}