MainTestCase class
Introduction
The MainTestCase class is a powerful testing utility designed to simplify and standardize API testing in Laravel applications. It extends Laravel's TestCase and incorporates the TestHelpers trait to provide a comprehensive set of methods for testing CRUD operations.
This class is particularly useful for testing RESTful APIs, as it provides methods for testing index, show, store, update, and delete operations with minimal code duplication.
Class Overview
namespace Tests\Contracts;
use App\Models\User;
use App\Traits\TestHelpers;
use Tests\TestCase;
class MainTestCase extends TestCase
{
use TestHelpers;
// Methods for testing CRUD operations
}Required Properties
When extending the MainTestCase class, you should define the following properties in your test class:
| Property | Type | Description |
|---|---|---|
$model | string | The fully qualified class name of the model being tested |
$resource | string | The fully qualified class name of the resource being tested |
$userType | string | The type of user to authenticate as (e.g., 'admin', 'user', 'none') |
$baseUrl | string | The base URL for the routes being tested |
$relations | array | An array of relations to load with the model |
Available Methods
deleteTest
Tests the deletion of a resource.
public function deleteTest(array $additionalFactoryData = [], bool $ownership = false): staticParameters:
$additionalFactoryData(array): Optional data to pass to the model factory$ownership(bool): Determines if the action has to be on the authenticated user's data
What it tests:
- Attempting to delete a resource with an invalid ID
- If
$ownershipis true, attempting to delete another user's resource - Successfully deleting a resource
- Verifying the resource is deleted (soft or hard delete)
indexTest
Tests the listing/index functionality of resources.
public function indexTest(array $additionalFactoryData = [], bool $ownership = false): staticParameters:
$additionalFactoryData(array): Optional data to pass to the model factory$ownership(bool): Determines if the action has to be on the authenticated user's data
What it tests:
- Requesting the index when no resources exist
- If
$ownershipis true, attempting to list another user's resources - Successfully listing resources
- Verifying pagination and resource transformation
showTest
Tests the retrieval of a single resource.
public function showTest(array $additionalFactoryData = [], bool $ownership = false): staticParameters:
$additionalFactoryData(array): Optional data to pass to the model factory$ownership(bool): Determines if the action has to be on the authenticated user's data
What it tests:
- Attempting to show a resource with an invalid ID
- If
$ownershipis true, attempting to show another user's resource - Successfully showing a resource
- Verifying resource transformation
storeTest
Tests the creation of a new resource.
public function storeTest(array $additionalAttributes = [], mixed $requestParams = null): staticParameters:
$additionalAttributes(array): Optional data to pass to the model factory$requestParams(mixed): Optional parameters to include in the request URL
What it tests:
- Successfully creating a resource with the provided attributes
- Verifying the resource is created in the database
- Verifying resource transformation
updateTest
Tests the updating of an existing resource.
public function updateTest(array $attributes = [], array $additionalFactoryData = [], bool $ownership = false): staticParameters:
$attributes(array): Data to use for the update request$additionalFactoryData(array): Optional data to pass to the model factory$ownership(bool): Determines if the action has to be on the authenticated user's data
What it tests:
- Attempting to update a resource with an invalid ID
- If
$ownershipis true, attempting to update another user's resource - Successfully updating a resource
- Verifying the resource is updated in the database
- Verifying resource transformation
TestHelpers Trait
The MainTestCase class uses the TestHelpers trait, which provides numerous helper methods for testing. Some of the most commonly used methods include:
Authentication Methods
login(string $email, string $password): Logs in a user with the given credentialssignIn($type): Signs in a user of the specified type
Response Assertion Methods
statusOk(): Asserts that the response has a 200 status codestatusNotFound(): Asserts that the response has a 404 status codestatusBadRequest(): Asserts that the response has a 400 status codestatusForbidden(): Asserts that the response has a 403 status codestatusNotAuthorized(): Asserts that the response has a 401 status codestatusValidationError(): Asserts that the response has a 422 status code
Data Preparation Methods
dataResource(mixed $data): Sets the expected data resourcedata(mixed $data): Sets the expected datanoData(): Sets the expected data to null
Success State Helpers
getSuccess(): Sets up expectations for a successful GET requeststoreSuccess(): Sets up expectations for a successful POST requestupdateSuccess(): Sets up expectations for a successful PUT requestdeleteSuccess(): Sets up expectations for a successful DELETE request
Pagination Helpers
paginate(int $total): Sets up expectations for paginated dataemptyPagination(): Sets up expectations for empty paginated dataunPaginate(): Sets up expectations for unpaginated data
Other Helpers
requestPathHook(string $routeName): Sets the request path for the testmultiple(): Indicates that multiple resources are expectedsingle(): Indicates that a single resource is expectedmessage(string $message): Sets the expected message in the response
Usage Example
Here's an example of how to use the MainTestCase class to test a Category model:
<?php
namespace Tests\Feature;
use App\Http\Resources\v1\CategoryResource;
use App\Models\Category;
use Illuminate\Http\UploadedFile;
use Tests\Contracts\MainTestCase;
class CategoryTest extends MainTestCase
{
/** @var class-string<Category> */
protected string $model = Category::class;
/** @var class-string<CategoryResource> */
protected string $resource = CategoryResource::class;
protected string $userType = 'none';
protected string $baseUrl = 'v1.api.public.categories.';
protected array $relations = [];
public function test_user_can_index_category()
{
$this->requestPathHook($this->baseUrl.'index');
$this->indexTest();
}
public function test_user_can_show_a_category()
{
$this->requestPathHook($this->baseUrl.'show');
$this->showTest();
}
public function test_user_can_create_a_category()
{
$this->requestPathHook($this->baseUrl.'store');
$this->storeTest([
'launch_date' => now()->format('Y-m-d'),
'daily_update_time' => now()->format('H:i'),
'last_promotion_date' => now()->format('Y-m-d H:i'),
'last_accessed_at' => now()->format('Y-m-d H:i'),
'image' => UploadedFile::fake()->image('image.png'),
]);
}
public function test_user_can_update_category()
{
$this->requestPathHook($this->baseUrl.'update');
$this->updateTest([
'launch_date' => now()->format('Y-m-d'),
'daily_update_time' => now()->format('H:i'),
'last_promotion_date' => now()->format('Y-m-d H:i'),
'last_accessed_at' => now()->format('Y-m-d H:i'),
'image' => UploadedFile::fake()->image('image.png'),
]);
}
public function test_user_can_delete_a_category()
{
$this->requestPathHook($this->baseUrl.'destroy');
$this->deleteTest();
}
}Best Practices
Define Required Properties: Always define the required properties (
$model,$resource,$userType,$baseUrl,$relations) in your test class.Use requestPathHook: Always call
requestPathHook()before calling any of the test methods to set the correct route.Customize Factory Data: Pass additional factory data to the test methods when needed to test specific scenarios.
