Is working with test cases intimidating for you? Are you looking for a simple tutorial to get started with feature testing in Laravel? Then, we insist you stick to this step-by-step guide to clear your doubts and learn testing in the Laravel application.
When developing an application, you tend to worry about code breakage while executing a feature or modules. To avoid scenarios, it is significant to implement testing in your application.
Feature testing is one of the most used and important types of testing. It allows you to test a major portion of your application’s code that has objects interacting with each other, HTTP requests, JSON, etc.
The best part is testing is it is automated. It finds the gap in your code and allows you to develop features right.
Laravel supports PHPUnit tests. Your web app comes with a phpunit.xml file with all the settings you need to test your Laravel application. Your phpunit.xml file sets your laravel environment for testing. So there is no need to create a new XML file!
Below is a sample phpunit.xml file that comes with the Laravel 8 framework.
For this tutorial, we will be working with a hotel review api. The end-user can give reviews for a hotel so that hotel reviews can be added, updated, deleted, and hotel review list operation also.
Create the laravel application by the below command
composer create-project --prefer-dist laravel/laravel hotel_review_api_test cd hotel_review_api_test
Open the .env file and update the database details.
DB_CONNECTION=mysql DB_HOST=127.0.0.1 DB_PORT=3306 DB_DATABASE=hotel_test_api DB_USERNAME=root DB_PASSWORD=
For model, run the below commands.
For migration, run the below commands.
Develop. Maintain. Optimize. Deploy – with Bacancy!
Are you looking for proficient developers to build highly-optimized applications? Get in touch with us to hire Laravel developer. Contact the best, to get the best! Period!
Now update the migration files by below code mentioned below.
We will update the user table, hotel table, and review table files. Add the columns as directed. You can edit the columns as per your requirement.
public function up() { Schema::create('users', function (Blueprint $table) { $table->bigIncrements('id')->unsinged(); $table->string('name'); $table->string('email')->unique(); $table->timestamps(); }); }
public function up() { Schema::create('hotels', function (Blueprint $table) { $table->bigIncrements('id')->unsinged(); $table->string('name', 100); $table->text('address')->nullable(); $table->float('star')->nullable(); $table->tinyInteger('active')->default(1)->comment = '1 = active,0 = Inactive'; $table->timestamps(); $table->softDeletes(); }); }
public function up() { Schema::create('reviews', function (Blueprint $table) { $table->bigIncrements('id')->unsinged(); $table->string('title', 100); $table->text('description', 100); $table->unsignedBigInteger('user_id')->nullable(); $table->unsignedBigInteger('hotel_id')->nullable(); $table->timestamps(); $table->softDeletes(); $table->foreign('user_id')->references('id')->on('users')->onDelete('CASCADE')->onUpdate('CASCADE'); $table->foreign('hotel_id')->references('id')->on('hotels')->onDelete('CASCADE')->onUpdate('CASCADE'); }); }
Open \routes\api.php and use the below code snippet to generate routes for our APIs. We would have five APIs.
routes\api.php
// Fetch all active hotels data Route::get('hotels', [HotelController::class,'getAllHotelData']); // Fetch particular hotel data Route::get('hotel/{hotel_id}', [HotelController::class,'getHotelDataById']); // Add a new hotel review Route::post('save-hotel-review', [HotelController::class,'storeHotelReviewData']); // Update hotel review Route::put('update-hotel-review/{review_id}', [HotelController::class,'updateHotelReviewData']); // Delete hotel review Route::delete('review/{review_id}', [HotelController::class,'deleteHotelReview']);
For the business logic of CRUD operations, create a new HotelController.php file. We won’t be specifying the code snippet for the APIs. But here is the Github link to HotelController.php if you wish to have a look. We would be following these five APIs.
This section will create our first feature test for our app. Run the below command to generate a file for testing named HotelTest.
The command will create a new file named HotelTest.php in the tests > Feature folder with the below code.
We will be writing tests for each unit of our application for application. We have written tests to ensure that: a hotel review can be added, deleted, updated, and listed that are valid. Update the HotelTest.php with the following code.
To get active hotel data of specific hotel
class HotelTest extends TestCase { /** * A feature test to get active hotel data based on hotel ID * * @return void */ public function test_get_active_hotel_by_id() { $hotel_id = Hotel::where('active', 1)->get()->random()->id; $response = $this->get('/api/hotel/' . $hotel_id) ->assertStatus(200) ->assertJsonStructure( [ 'code', 'message', 'data' => [ 'id', 'name', 'star', 'review' => [ '*' => [ "id", "title", "description", "author", "create_at", "update_at" ], ] ], ] ); }
To get all active hotel data
/** * A feature test to get all active hotel data * * @return void */ public function test_get_all_active_hotels() { $response = $this->get('/api/hotels') ->assertStatus(200) ->assertJsonStructure( [ 'code', 'message', 'data' => [ '*' => [ "id", "name", "address", "star", "create_at", "update_at", "active", "review" => [ '*' => [ "id", "title", "description", "author", "create_at", "update_at" ], ], ], ], ] ); }
To get all inactive hotel data of specific hotel
/** * A feature test to get inactive hotel data based on hotel ID * * @return void */ public function test_for_get_inactive_hotel_by_id() { $hotel_id = Hotel::where('active', 0)->get()->random()->id; $response = $this->get('/api/hotel/' . $hotel_id) ->assertStatus(200) ->assertJsonStructure( [ 'code', 'message', ] ); }
To add a new review
/** * A feature test to add a new review * * @return void */ public function test_for_add_hotel_review() { $user = User::create([ 'name' => rand(), 'email' => rand() . '[email protected]', 'created_at' => Carbon::now(), 'updated_at' => Carbon::now(), ]); //$user = User::create($userData); $hotel = Hotel::create([ 'name' => rand(), 'star' => 2, 'address' => 'Opposite Town Hall, Nr. Sakar II & IV, Ashram Rd, Ellisbridge, Ahmedabad, Gujarat 380006', 'active' => 0, 'created_at' => Carbon::now(), 'updated_at' => Carbon::now(), ]); $payload = [ "hotel_id" => $hotel->id, "user_id" => $user->id, "review_title" => "test", "review_data" => "test description" ]; $this->json('POST', 'api/save-hotel-review', $payload) ->assertStatus(200) ->assertJson([ 'code' => '200', 'message' => 'Hotel Review saved.', ]); }
To update specific review
/** * A feature test to update review based on review id * * @return void */ public function test_for_update_hotel_review() { $user = User::create([ 'name' => rand(), 'email' => rand() . '[email protected]', 'created_at' => Carbon::now(), 'updated_at' => Carbon::now(), ]); //$user = User::create($userData); $hotel = Hotel::create([ 'name' => rand(), 'star' => 2, 'address' => 'Opposite Town Hall, Nr. Sakar II & IV, Ashram Rd, Ellisbridge, Ahmedabad, Gujarat 380006', 'active' => 0, 'created_at' => Carbon::now(), 'updated_at' => Carbon::now(), ]); $hotelReview = Review::create([ 'title' => 'Good Hotel HollywoodInn', 'description' => 'HollywoodInn is a very nice hotel.', 'user_id' => $user->id, 'hotel_id' => $hotel->id, 'created_at' => Carbon::now(), 'updated_at' => Carbon::now(), ]); $payload = [ "hotel_id" => $hotel->id, "user_id" => $user->id, "review_title" => "test", "review_data" => "test description" ]; $this->json('PUT', 'api/update-hotel-review/' . $hotelReview->id, $payload) ->assertStatus(200) ->assertJson([ 'code' => '200', 'message' => 'Hotel Review updated.', ]); }
To delete hotel review data
/** * A feature test to delete hotel review data * * @return void */ public function test_for_delete_hotel_review() { $user = User::create([ 'name' => rand(), 'email' => rand() . '[email protected]', 'created_at' => Carbon::now(), 'updated_at' => Carbon::now(), ]); $hotel = Hotel::create([ 'name' => rand(), 'star' => 2, 'address' => 'Opposite Town Hall, Nr. Sakar II & IV, Ashram Rd, Ellisbridge, Ahmedabad, Gujarat 380006', 'active' => 0, 'created_at' => Carbon::now(), 'updated_at' => Carbon::now(), ]); $hotelReview = Review::create([ 'title' => 'Good Hotel HollywoodInn', 'description' => 'HollywoodInn is a very nice hotel.', 'user_id' => $user->id, 'hotel_id' => $hotel->id, 'created_at' => Carbon::now(), 'updated_at' => Carbon::now(), ]); $this->json('DELETE', 'api/review/' . $hotelReview->id) ->assertStatus(200) ->assertJson([ 'code' => '200', 'message' => 'Hotel Review deleted successfully.', ]); }
To store new review required data
/** * A feature test to store new review required data * * @return void */ public function test_for_add_hotel_review_required_fields() { $this->json('POST', 'api/save-hotel-review') ->assertStatus(200) ->assertJson([ 'code' => '401', 'message' => 'The hotel id field is required. The user id field is required. The review title field is required. The review data field is required, ]); }
To update review required data
/** * A feature test to update review required data * * @return void */ public function test_for_update_hotel_review_required_fields() { $user = User::create([ 'name' => rand(), 'email' => rand() . '[email protected]', 'created_at' => Carbon::now(), 'updated_at' => Carbon::now(), ]); //$user = User::create($userData); $hotel = Hotel::create([ 'name' => rand(), 'star' => 2, 'address' => 'Opposite Town Hall, Nr. Sakar II & IV, Ashram Rd, Ellisbridge, Ahmedabad, Gujarat 380006', 'active' => 0, 'created_at' => Carbon::now(), 'updated_at' => Carbon::now(), ]); //$hotel = User::create($hotelData); $hotelReview = Review::create([ 'title' => 'Good Hotel HollywoodInn', 'description' => 'HollywoodInn is a very nice hotel.', 'user_id' => $user->id, 'hotel_id' => $hotel->id, 'created_at' => Carbon::now(), 'updated_at' => Carbon::now(), ]); $this->json('PUT', 'api/update-hotel-review/' . $hotelReview->id) ->assertStatus(200) ->assertJson([ 'code' => '401', 'message' => 'The hotel id field is required. The user id field is required. The review title field is required. The review data field is required, ]); }
To update reviews that do not existl
/** * A feature test to update reviews that do not exist * * @return void */ public function test_for_update_hotel_review_that_not_exist() { //review id that not exist in database $reviewId = random_int(100000, 999999); $payload = [ "hotel_id" => random_int(100, 999), "user_id" => random_int(100, 999), "review_title" => "test", "review_data" => "test description" ]; $this->json('PUT', 'api/update-hotel-review/' . $reviewId, $payload) ->assertStatus(200) ->assertJson([ 'code' => '401', 'message' => 'Invalid Hotel Id or User Id or Review Id', ]); }
To delete review that does not exist
/** * A feature test to delete a review that does not exist * * @return void */ public function test_for_delete_review_that_not_exist() { //review id that not exist in database $reviewId = random_int(100000, 999999); $this->json('DELETE', 'api/review/' . $reviewId) ->assertStatus(200) ->assertJson([ 'code' => '401', 'message' => 'Review not found, Please try again', ]); } }
Explanation
{ "code": "200", "message": "Hotel Data", "data": [ { "id": 1, "name": "Hyaat", "address": "17/A, Ashram Rd, Usmanpura, Ahmedabad, Gujarat 380014", "star": 4, "create_at": "11/Jan/2022 15:40:56", "update_at": "11/Jan/2022 15:40:56", "active": "Active", "review": [ { "id": 2, "title": "Good Hotel Hyaat", "description": "Hyaat is a very nice hotel.", "author": "Parth", "create_at": "11/Jan/2022 15:40:56", "update_at": "11/Jan/2022 15:40:56" } ] }, ] }
assertJson testing if the JSON result matches our expectations means the same output JSON return from API like comparing two strings.
assertTrue() and assertFalse() allow you to assert that a value is equated to either true or false. This means they are perfect for testing methods that return boolean values.
assertEquals() is used to compare the actual value of the variable to the expected value. Unlike assertTrue(), assertFalse(), and assertNull(), assertEquals() takes two parameters. The first being the expected value, and the second being the actual value.
assertContains() asserts that an expected value exists within the provided array, assertCount() asserts the number of items in the array matches the specified amount, and assertEmpty() asserts that the provided array is empty.
Visit Laravel documentation to learn about other functions.
Use the below command at the root folder for running the test cases.
You can visit the source code and clone the repository to play around with the code.
I hope the tutorial for implementing Feature Testing in Laravel for REST APIs has served you as you wanted. We have several Laravel tutorials for enthusiasts like you. If you want to learn and explore more about Laravel, please visit the Laravel tutorials page.
Your Success Is Guaranteed !
We accelerate the release of digital product and guaranteed their success
We Use Slack, Jira & GitHub for Accurate Deployment and Effective Communication.