Image for post
Image for post
Photo by Shirly Niv Marton on Unsplash

Testing Laravel Form Requests in a different way

Many developers are struggling with testing form requests in an efficient way. Most of the time you’ll end up with writing a seperate unit test for each rule that is defined in your form request. This results in a lot of tests like test_request_without_title and test_request_without_content. All of these methods are implemented in exactly the same way, only calling your endpoint with some different data. This will result in a lot of duplicate code. In this guide I will show you a different way to test your form request, which I think is more clean and improves the maintainability of your tests.

For this example I will be making a form request to save a product.

The generated file class will be placed in App/Http/Requests.

We will declare a set of validation rules for this form request:

  1. The title parameter should be a string with a maximum of 50 characters.
  2. The price parameter should be numeric.

Those are the only two validation rules for now.

This is what the SaveProductRequest class looks like:

Within the authorize method you can check if the user has permission to perform this request. For example, you could check if the user is an admin, but for now anybody is allowed to perform this request.

Let’s create a Product model:

The migration file looks like this:

Let’s set up the ProductController:

And give it a very simple implementation:

Note:
The ProductResource is a resource that you can make with:

And finally, add a route to your routes/api.php:

Before we can start making our tests we have to create a testfile:

Note:
I prefer to structure my tests in this way, but you could choose to leave out the App/Http/Requests folder.

A typical test suite for this controller could look like this:

Image for post
Image for post

This is how most of the developers would test a form request. This works and all the tests are passing, but there is a lot of duplicate code. The only thing that differs between the tests is the data that gets send to the endpoint. This can be done more efficiently.

PHPUnit’s data provider provides an elegant way to write tests for Laravel’s form requests. A data provider allows you to structure tests once and run them multiple times with different datasets.

A data provider method must be public and return an array or an object that implements the Iterator interface. You can specify the data provider by using the @dataProvider annotation.

The most basic example of a data provider looks like this:

For every array in the provider method the testAdd method will be called. The arguments that are being passed to the testAdd method are specified in the array from the provider. So the first call would be testAdd(0, 0, 0) and the second call would be testAdd(0, 1, 1).

Just like we specified the numbers for the testAdd method in a data provider, we could also specify the data which our endpoint gets called with. Then we run each of those data arrays through Laravel’s Validator class to check if the validation rules pass.

What is most important here is the structure of the data provider. In the key of the data provider array we specify the name of the test. Within this array we have two attributes: passed and data. The passed attribute is a boolean with the expected outcome of the validator. The data attribute contains the data that we want to send to the endpoint.

This is what the code will look like:

Image for post
Image for post

The result is the same, all tests still pass, but duplication is reduced and maintainability improved. What do you think about this way of testing your form requests? Do you test your form requests in a different way? Please let me know in the comments.

If you enjoyed this post or if it has helped you testing your code make sure to check out my other posts aswell. Please feel free to leave a comment if you have any feedback, questions or want me to write about another Laravel related topic.

Backend developer from The Netherlands. Crypto enthusiast.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store