Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

Browse all series

Pest Driven Laravel

Learn to write applications that are fully tested and a joy to work with now and in the future through TDD. Tests are the base for writing code that is easy to change, refactor and maintain. There is no reason anymore to be afraid to touch your own code. Following test-driven development establishes the mindset that integrates testing and refactoring in your daily workflow.

Progress

Series Info

Episodes
48
Run Time
6h 53m
Difficulty
Advanced
Last Updated
Sep 1, 2023
Version
Latest

Series Episodes

  1. Workflow (45)
    1. Welcome!

      In this series, we will learn how to build an entire application using Test-Driven Development (TDD). We will start by writing tests first, which will help us stay focused and integrate refactoring into our workflow. We will use the PEST testing framework and Laravel Livewire to create dynamic interfaces for our users.
    2. Prepare our Todos

      In this lesson, we start by defining the scope of our project and breaking down the tasks into separate categories. We create a Todoist Markdown file to outline what a guest user and a member user can do, such as viewing course overviews, purchasing courses, logging in, and watching purchased courses. This approach helps us think about specific tasks and plan for testing, which is crucial when following TDD (Test-Driven Development).
    3. Testing Setup

      In this lesson, we learn how to set up our test environment using PEST as our testing framework. We install PEST and the Laravel PEST plugin, and create a PEST file for configuration. We also see how to run PEST tests in PhpStorm and check our PHPUnit configuration.
    4. Write Your First PEST Test

      In this lesson, we learn how to create a basic test for our application using PEST. We start by making a GET request to our homepage and asserting that we receive a successful response. We then refactor the test to use a route name instead of a specific route, and use the OK method for the assertion. Finally, we demonstrate how to make the test fail by not defining the route, and then make it pass by adding the route. This is an introduction to test-driven development (TDD).
    5. Show Course Overview - Part 1

      In this lesson, we learn about Test-Driven Development (TDD) and how it guides us in creating new features. We start by writing tests for our home page, ensuring that it shows the titles and descriptions of our courses. We then create the necessary models, factories, and migrations, and connect our controller to the view. Finally, we enhance our tests to include more specific assertions, ensuring that only released courses are shown and that they are displayed in the correct order.
    6. Show Course Overview - Part 2

      In this lesson, we write tests to ensure that only released courses are shown on the homepage. We create two courses, one released and one not released, and verify that only the released course is displayed. We also order the courses by release date, ensuring that the latest release is shown first.
    7. Refactoring Tests for Clean and Readable Code

      In this lesson, we learn about the importance of refactoring in test-driven development. We refactor our tests by replacing static text with variables, making them cleaner and easier to read. We also explore the use of factory states and scopes in Laravel to improve our code and make it more readable.
    8. Use Laravel Pint to Fix Code Style Issues

      In this lesson, we will install Laravel Pint, a code style fixer, to ensure consistency in our PHP code. By using Laravel Pint, we can easily maintain a consistent code style and improve readability. Although creating a GitHub action or running Laravel Pint before every push is beyond the scope of this course, we will periodically run it to maintain code consistency.
    9. Show Course Details

      In this lesson, we start by creating a new test for the course details page. We create a course object using a factory and make a GET request to a new route. We then assert that the response contains the course title, description, tagline, learnings, and image. We also fix any errors that arise during the process.
    10. Show Course Details - Part 2

      In this lesson, we create a new course and add videos to it. We then make a GET request to the course details route and ensure that the correct number of videos is displayed on the page. We also encounter some errors related to missing taglines, images, and a default learning field, which we address using factories and migrations. Finally, we define a relationship between the course and video models and write tests to verify the relationship.
    11. Refactoring Course Details

      In this lesson, I refactor code to improve readability and make it easier for other developers to understand. I also add tests to ensure that only released courses are shown and handle the case when a course is not released yet.
    12. Refactoring Course Details - Part 2

      In this lesson, we improve our code by using the foreignIdFor() method in our videos migration, which simplifies the definition of a relationship to the course. We also restructure our file system to have dedicated directories for our pages and their corresponding tests. Additionally, we enhance performance by using the load count method to get the count of videos, instead of loading all video data. Lastly, we refactor our route names to reflect the new directory structure, ensuring all tests pass after each change.
    13. Displaying Purchased Courses

      In this lesson, we start by creating a new page response test for the dashboard page. We then install the Level Chat Stream package and set up the dashboard route. Next, we create a new feature test for the dashboard page, where we test that a guest cannot access the page, that purchased courses are listed, that no other courses are listed, and that the courses are displayed in a specific order. Finally, we create a relationship between users and courses and update the dashboard page to display the purchased courses.
    14. Displaying Purchased Courses - Part 2

      In this lesson, we're focusing on testing and refining our course dashboard. We start by ensuring that the dashboard does not list unrelated courses, and then move on to testing the order of displayed courses, with the most recently purchased course appearing first. We also ensure that each course includes a link to its associated videos. We use Laravel's factory method to create users and courses, and the assert method to verify our expectations. We also touch on the use of pivot tables and the route helper to create URLs.
    15. Refactoring Code and Improving Test Performance

      In this lesson, we're focusing on refactoring our code and improving our testing strategy. We'll clean up unnecessary tests that came with the Jetstream package, and reorganize the remaining ones into a separate directory for better organization. We'll also introduce a new helper method, loginAsUser, to streamline our testing process. This method will automatically create and log in a user, reducing the amount of code we need to write in each test. We'll also discuss the benefits of using the lazilyRefreshDatabase trait for performance optimization in our tests.
    16. Implement a Login/Logout Functionality

      In this lesson, we focus on ensuring that a member can log in and log out from the home page and the members page. We add tests to our page home test to verify that the login and logout links are displayed correctly based on the user's login status. We also ensure that the registration feature is disabled in our application, as we want to create accounts for our users ourselves when they have purchased something.
    17. Creating a Video Player with Laravel Livewire

      In this lesson, we're focusing on creating a dedicated page for users to watch their purchased courses. We start by creating a new page response test and adding a feature test for a successful response for the videos page. We then create new models and ensure that a user is logged in to access the course videos page. We also create a new live wire component called "video player" and ensure it's included on the page. Finally, we write tests to ensure that the first course video is shown by default and that a specific video can be shown when provided.
    18. Creating and Testing Video Player Components in Livewire

      In this lesson, we're creating tests for our video player component within a Livewire directory. We ensure that the component displays details for a given video, including its title, description, and duration. We also test that the video player shows the actual video, using a Vimeo iframe. This involves adding new columns to our database for video description, duration, and Vimeo ID, and adjusting our component to display these details. We encounter and fix several errors along the way, ensuring all tests pass for our video player component.
    19. Always Make Time for Refactoring

      In this lesson, we focus on refactoring our code after building a new feature, emphasizing the importance of doing so while the code is still fresh in our minds. We refactor our code to replace static text with variables, improve naming conventions, and create a new method for displaying video duration in a more readable format. We also introduce the use of the assertSeeHTML method in Livewire for testing, which simplifies our code by eliminating the need for a second parameter.
    20. Creating and Testing a Video Player in Laravel

      In this lesson, we enhance our video player by adding a list of all the videos in a course, allowing users to easily switch between videos. We create a new sequence of videos with distinct titles for testing, and use the Livewire test helper to ensure that the video player displays the correct details for each video. We also add links to each video in the list, and test that these links are correctly displayed.
    21. Mark a Video As Completed

      In this lesson, we're adding a feature that allows a user to mark a video as completed or not completed. We're creating two new tests for this feature: one to mark a video as completed and another to mark it as not completed. We're using Laravel's auth helper to get the current authenticated user and the attach and detach methods to add and remove a video from the user's watched videos.
    22. Refactoring Code and Enhancing PHP Relationships

      In this lesson, we refactor our code to improve readability and efficiency. We replace static text with dynamic content using the spread operator in PHP, and we rename our relationships to more accurately reflect their purpose. For example, we change "videos" to "watched videos" and "courses" to "purchased courses". This makes our code more understandable for future reference and for other developers.
    23. Creating Helper Methods for Efficient Testing

      In this lesson, we focus on refactoring our code to improve readability and efficiency. We create a helper method, createCourseAndVideos, to streamline the process of creating a course and its associated videos. We also introduce the concept of named arguments to make our code more understandable. Additionally, we enhance our video player by adding a method, isCurrentVideo, to check if a video is the current one being played. This refactoring process is guided by our tests, ensuring we don't break anything along the way.
    24. Testing Livewire Components

      In this lesson, we're exploring how to test our Livewire components more effectively. We discover that while our methods are working as expected, they aren't wired to any buttons in our template, meaning the user can't trigger them. To solve this, I introduce my package, "missing Livewire assertions", which allows us to assert that a method is wired to a specific HTML element. We then use this package to ensure our methods are connected to the appropriate elements in our view file, providing a more comprehensive test of our Livewire components.
    25. Check if a User Has Already Watched a Video

      In this lesson, we refactor our code to introduce a new method, alreadyWatchedByCurrentUser, which checks if a user has already watched a specific video. We then create tests to ensure this method works correctly, both when a user has and hasn't watched a video. We also introduce a helper method to authenticate a user before each test, improving the efficiency of our testing process.
    26. Managing Database Entries with Laravel Seeders and Migrations

      In this lesson, we discuss how to add non-user-generated data, such as courses and videos, to our database. We explore two main approaches: using Laravel seeders and migrations. We discuss the pros and cons of each method, and ultimately decide to use a combination of both. We also write tests to ensure that our data is added correctly and only once, regardless of how many times we run the command.
    27. Creating and Testing Database Seeders in Laravel

      In this lesson, we focus on adding specific videos to our courses and ensuring that our video table is correctly populated. We write tests to verify that we have the correct number of videos for each course and that videos are only added once. We also create a local test user for our application, ensuring that this user is only added in a local environment and not in production.
    28. First Look Through the Browser

      In this lesson, we're exploring how to build and test our application without relying on the browser. We've already implemented a lot using Test-Driven Development (TDD), and now we're going to check our progress in the browser. We'll then fix a bug related to video routing, demonstrating the TDD approach of writing a failing test before fixing the code.
    29. Debugging Livewire Components in Devtools

      In this lesson, we troubleshoot a non-functioning 'mark as completed' button on our page, discovering that the issue lies in the absence of the Livewire component in our course videos page. We resolve this by using the given app layout from Chatstream, which successfully includes the necessary assets for Livewire. We also refine our testing methods to ensure they're working as expected, specifically by checking for an h3 element in our HTML to verify that the correct video title is displayed.
    30. Integrating Paddle

      In this lesson, we're integrating the Paddle payment provider into our video platform to enable course purchases. We'll use a checkout overlay from Paddle, which also handles taxes, making it ideal for selling to users inside Europe. We'll start by writing a test to ensure the presence of a checkout button on our course details page, then proceed to implement the button and the necessary Paddle scripts.
    31. Testing the Checkout Functionality in Courses

      In this lesson, we troubleshoot failing tests and resolve issues related to the "not null constraint" for the "Paddle product ID". We add the "Paddle product ID" to our factory and regenerate helper code for "level idea". We also ensure that our homepage displays links to course details and that these links lead to the correct course details page. We integrate the checkout overlay from Paddle to our courses and set up the environment to work with the sandbox version of Paddle when working locally.
    32. Validate and Store Paddle Requests

      In this lesson, we're focusing on handling and storing requests from the checkout overlay by Paddle in our application. We'll create a new feature test, webhook_pedal_purchase_test, to ensure that all valid requests are stored and invalid ones are not. We'll also use the Laravel package, webhook_client, to receive webhooks in our Laravel application. We'll create a custom webhook validator and a job to handle the request.
    33. Managing Valid and Invalid Job Dispatching

      In this lesson, we're focusing on dispatching a specific job for valid requests and ensuring no job is dispatched for invalid ones. We'll be faking our queuing system, making requests, and using assertions like assertPushed and assertNothingPushed to verify our job handling. We'll also run all our tests to ensure nothing breaks, and by the end, we'll have almost 70 tests in our application.
    34. Refactoring Code for Efficient Test Creation

      In this lesson, we focus on refactoring our code to improve readability and maintainability. We extract some of the messy code into helper methods, such as getValidPedalRequestData and getInvalidPedalRequestData, to make our tests cleaner and easier to understand. We also discuss the importance of faking systems like the queue system in our tests, and we extract validation logic into a separate method, isPedalRequestValid, to make our code more organized.
    35. Creating and Testing a Purchase Handler Job

      In this lesson, we're focusing on handling valid purchases in our application. We'll start by creating a new feature test called handle_pedal_purchase_job_test. We'll ensure that our application can store a purchase, which is a product connected to a specific user. We'll also make sure that if there is an existing user, we use that user. Lastly, we'll ensure that an email is sent to the user about the purchase. We'll use a job to store a Paddle purchase, use an existing user if available, and send out an email to the user about their purchase.
    36. Creating User-Specific Paddle Purchase Tests

      In this lesson, we're focusing on testing and refining our code. We start by creating a test to store Paddle purchases for a given user, ensuring that we only have one user in the database. We then encounter a unique constraint error, which leads us to adjust our code to check for an existing user before creating a new one. Finally, we create a test to send an email to the user, which involves faking our email system and creating a new NewPurchaseMail class. We end the lesson with all tests passing, but note that our code could use some cleaning up, which we'll tackle in the next video.
    37. Refactoring Code and Testing Email Content

      In this lesson, we're refactoring our code and improving our tests. We start by creating a dummy webhook call in a setup method, which we can use in every test, making our tests cleaner and more readable. We also add a new feature test for our email functionality, ensuring that the email includes purchase details and a login button. We run all our tests to ensure nothing is broken, demonstrating the importance of testing not just your code, but also your emails.
    38. Testing the Payment Process Flow

      In this lesson, we're testing the payment process for users in our application, ensuring that the checkout overlay from Paddle is properly integrated and functioning. We're using tools like Expose by BeyondCode for a public URL and Halo for email testing, and we're also setting up webhooks with Paddle to handle requests. We're making sure that new users are properly created, emails are sent out, and purchases are recorded in our database.
    39. Creating a Command to Auto-Tweet New Courses

      In this lesson, we learn how to create a console command in Laravel that sends out a tweet about a newly released course. We use facades and fake methods to simulate the Twitter client and store the tweets in an array for testing purposes.
    40. Implementing Real and Fake Twitter Clients

      In this lesson, we're testing our Twitter client, both with a fake and real implementation. We start by ensuring our test with the fake Twitter client is working, then we move on to test the real client. We encounter an issue because we haven't implemented the real Twitter client yet. We solve this by creating a new Twitter client, installing the "TwitterOAuth" package by Abraham, and providing the necessary keys for authentication. We also create a service provider to handle the creation of the Twitter client instance. Finally, we test our implementation by sending a real tweet, confirming that our setup works as expected.
    41. Twitter Client Refactoring

      In this lesson, we're refactoring our code and improving the organization of our files. We move the TwitterClient and TwitterFacade classes to a new Services directory. We also introduce a NullTwitter class and a TwitterClientInterface to ensure that we don't accidentally use the real Twitter client during testing. This provides a safety net in case we forget to fake the Twitter service.
    42. Enhancing the User Experience

      In this lesson, I'm updating the design of our application to make it more modern and user-friendly. While this isn't the main focus of our course, you can follow along with the changes in the repository commit. The new design includes a clean layout for our courses, a detailed view for each course, a login feature, and a video player with a sidebar for easy navigation.
    43. Measuring Code Coverage with PEST

      "In this lesson, we learn about code coverage and how to measure the percentage of code covered by tests using PEST. We also explore how to enable code coverage with XDebug and use the coverage flag in our tests. Finally, we see how to improve code coverage by adding tests for specific files and aim for at least 80% coverage."
    44. Social Meta Tags

      In this lesson, we learn about the importance of implementing and testing social meta tags on websites. These tags are used by social networks to display preview images, titles, and descriptions when a link is shared. We implement and test these meta tags on a homepage and a course details page, ensuring that the correct information is displayed when these pages are shared.
    45. Efficient SEO Testing with the Test-SEO Plugin

      In this lesson, we're exploring how to use the test-ceo plugin by drumpy92 to simplify and clean up our testing of meta tags and social tags. We'll learn how to create a new CEO object from the response of a specific request, and then use this object to test the content of specific tags, making our tests more readable and easier to change.
  2. Pest 2 Upgrade (3)
    1. Upgrading to PEST 2

      In this lesson, we're exploring the newly released PES version 2, focusing on its new features like speed improvements for parallel testing, profile option, compact printer, retry option, and dirty build. We'll walk through the process of upgrading our application to PES version 2 and Laravel 10, ensuring all dependencies are correctly installed and tests are running as expected. We'll also test the speed improvement for parallel testing, which shows a significant improvement.
    2. Exploring New Features in Pest 2

      In this lesson, we explore the new features introduced in Pest version 2. We start with the "compact" mode, which simplifies test output by only displaying a dot instead of the full test name. We then discuss the "profile" flag, which checks the performance of your tests and displays the 10 slowest ones. We also cover the new "to-do" status for tests that haven't been implemented yet, and the "retry" method, which only runs the test file where a failure occurred. These features enhance the developer experience by making testing more convenient and efficient.
    3. Architecture Testing in Pest 2

      In this lesson, we explore the architecture testing feature in Pest version 2. This feature allows us to make assertions about the architecture of our application, such as ensuring certain classes are restricted to specific functional classes. We also learn how to create tests to check for the usage of specific global methods or facades, helping us maintain clean and production-ready code.

Continue Learning