All products

Test Generators

Generate starter tests for your Backpack CRUDs.

About

Testing your CRUD panels ensures that your admin interfaces work as expected and continue to function correctly as your application evolves. Backpack provides a dedicated command to generate Feature tests for your CrudControllers automatically.

These generated tests cover standard operations like:

  • List: Asserts the table loads and columns are visible.
  • Create: Asserts the form loads, validates inputs, and stores entries.
  • Update: Asserts the form loads with existing data and updates entries.
  • Delete: Asserts entries can be deleted.
  • Show: Asserts the details view loads.

The tests are designed to be "smart" — they inspect your CrudController's configuration (fields, columns, validation rules) to generate relevant assertions.

Installation

Quick Installion

In your Laravel + Backpack project, run:

php artisan backpack:require:test-generators

It will ask you for your token & password - which you get after you purchase this package. You can later see your token and password in your Backpack account.

Manual Installation

If the quick installation above doesn't work, here are the steps you can follow:

Step 1. Buy access to this package and you'll get an access token. Then:

  • add your token to your project's auth.json file by running composer config http-basic.backpackforlaravel.com [your-token-username] [your-token-password]
  • intruct your composer.json to look for packages in our private repo too:
    "repositories": [
        {
            "type": "composer",
            "url": "https://repo.backpackforlaravel.com/"
        }
    ],

Step 2. Install the package using Composer:

composer require --dev backpack/test-generators

Generate Tests

Step 1. Generate feature tests for your CRUD controllers using the artisan command:

php artisan backpack:tests

This will scan your controllers directory (configurable via backpack.testing.controllers_path) and generate test files for all supported operations.

Step 2. Configure tests/Feature/Backpack/DefaultTestBase.php to make sure the admin user that is used for testing... can actually do the things you're testing. Otherwise all your generator tests will fail (403 http status code instead of 200). This most likely means giving that admin user the correct roles/permissions. If you're using PermissionManager, that file includes some commented code for you, as example.

Step 3. The generated tests for CrudControllers need Factories and Seeders for those Eloquent Models, in order to work. If you're using our DevTools package, they should already be there. Otherwise, frontier LLMs will do a reasonable job of generating Factories and Seeders, here's a prompt you can use to get you started:

In this Laravel application, not all Eloquent Models that have a CrudController have factories and seeders. Please do a full evaluation of CrudControllers, Models and Factories and make sure we have a full suite of Factories for any model that has a CrudController, so that we can build a test suite on top of them.

Step 4. You should then run your tests, to see if there's anything left to fix (there usually is):

# how to run only the CRUD tests
php artisan test --filter="crud"

# how to run tests only for a particular CRUD
php artisan test --filter="usercrud"

Some of the errors you meet are to be expected. We've tried to cover the most common errors in the Troubleshooting section below. We recommend taking a look at it, when debugging your CRUD tests.

Options

| Option | Description | | --- | --- | | --controller=Name | Only generate tests for the specific controller class name (e.g., UserCrudController) | | --operation=list | Only generate tests for the given CRUD operation (list, create, update, etc.) | | --type=feature | The type of test to generate (feature is currently the only supported type) | | --framework=phpunit | The testing framework to use (phpunit or pest). Defaults to phpunit | | --force | Overwrite existing test classes |

Examples

Generate tests for all controllers:

php artisan backpack:tests

Generate tests for a specific controller:

php artisan backpack:tests --controller=UserCrudController

Generate only list operation tests:

php artisan backpack:tests --operation=list

Generated test file structure

Generated tests rely on a small hierarchy of base classes, reusable traits and on per-controller test files inside your app's tests/Feature folder.

You will notice that there will be a new "Backpack" folder. That folder contain the "base" tests that each of your crud controllers will re-use.

Each controller gets a single test file that extends DefaultTestBase and uses trait(s) for each operation:

tests/Feature/Admin/
├─ SomeCrudControllerTest.php  # extends DefaultTestBase, uses operation traits
├─ AnotherCrudControllerTest.php

For controllers in subfolders (e.g., PetShop), the folder structure is respected:

tests/Feature/Admin/PetShop/
├─ OwnerCrudControllerTest.php
├─ PetCrudControllerTest.php

Operation test traits and configuration variables

The operation test traits implement the assert logic and expose variables you can set in your test setUp() to customise behaviour:

  • DefaultCreateTests exposes $createInput and $assertCreateInput.
  • DefaultUpdateTests exposes $updateInput and $assertUpdateInput.
  • DefaultListTests and DefaultShowTests inspect the CRUD configuration via the test helper.

Usage pattern:

  • In your test's setUp(), create and set $this->createInput or $this->updateInput when you need to submit additional or transformed data. The trait will use those arrays when performing the POST/PUT requests.
  • Use $assertCreateInput / $assertUpdateInput when the database assertion differs from the raw submission (for example, do not include password or file upload metadata in assertions).

Example (from tests/Feature/Admin/PetShop/PetCrudControllerTest.php):

  • set an avatar URL to be submitted with the create request:
$this->createInput = array_merge($this->model::factory()->make()->toArray(), [
    'avatar' => ['url' => 'https://lorempixel.com/400/200/animals'],
]);

Route parameters and controller initialization

Controllers that require route parameters for their routes (for example nested resources like an owner ID) should define those parameters directly in the generated test class using the $routeParameters array and the $route property. Example from tests/Feature/Admin/PetShop/OwnerPetsCrudControllerTest.php:

public string $route = 'pet-shop/owner/1/pets';
public array $routeParameters = ['owner' => 1];

The $routeParameters array provides route parameter values when the test helpers mock the current route, while the $route property ensures trait requests target the correct URL with concrete values.

Overriding trait behaviour

If the default trait behaviour doesn't match your controller logic (e.g., you need to attach relationships before asserting the edit page), override the trait methods inside your test class. You can keep the original trait implementation available by aliasing it when importing the trait. Example from tests/Feature/Admin/PetShop/OwnerPetsCrudControllerTest.php:

use \\Tests\\Feature\\Backpack\\DefaultUpdateTests {
    test_update_page_loads_successfully as default_test_update_page_loads_successfully;
}

public function test_update_page_loads_successfully(): void
{
    $this->skipIfModelDoesNotHaveFactory();

    $entry = $this->model::factory()->create();
    $entry->owners()->attach(1, ['role' => 'Owner']);

    $response = $this->get($this->testHelper->getCrudUrl($entry->getKey().'/edit'));
    $response->assertStatus(200);
}

Short checklist when adapting or writing tests

  • Ensure the controller's required route parameters are provided via $routeParameters and the $route property includes concrete values in the generated test class.
  • In setUp() create any related models your controller requires (attached owners, categories, etc.).
  • Set $createInput / $updateInput in tests when the form requires additional structured data (files, nested arrays, relationship ids).
  • Use $assertCreateInput / $assertUpdateInput to shape the expected DB assertion.
  • Override trait methods only when you need custom assertions; alias the trait method if you still want to call the default behaviour.

Publishing the Configuration

You can publish the configuration by running php artisan vendor:publish --provider="Backpack\CRUD\BackpackServiceProvider" --tag=config. There you will be able to change your controllers path.

Customizing or creating test stubs

You can customize or add new operation test stubs used by the generator by publishing them to your application.

php artisan vendor:publish --provider="Backpack\CRUD\BackpackServiceProvider" --tag=stubs

This will create a resources/views/vendor/backpack/crud/stubs/testing directory in your application root. Any changes you make to these stubs will be used when generating tests. Here is an example of what a custom operation stub (e.g., clone.stub) might look like:

<?php

namespace Tests\Feature\Backpack;

trait DefaultCloneTests
{
    /**
     * Test that the list page loads and there is a clone button on page
     */
    public function test_create_clone_button_is_on_page(): void
    {
        $response = $this->get($this->testHelper->getCrudUrl('list'));
        $response->assertStatus(200);
        $response->assertSee('bp-button="clone"', true);
    }
}

Troubleshooting

The test generation highly relies on your Model Factories. It's highly important that your Factories are up-to-date with the database/model requirements. That being said, here are a few of the most common failures people see in their generated tests, and how to fix them:

The field X is required

If you see a failure like this:

 FAILED  Tests\Feature\Admin\VenueCrudControllerTest > update endpoint modifies entry in database
  Session has unexpected errors:
{
    "default": [
        "The city field is required."
    ]
}
Failed asserting that true is false.

Most likely the factory for your model isn't proper. In this example, your factory is most likely missing a related city - which is mandatory in the Update operation. The test isn't your problem, but the inconsistency between your Factory and your CrudController. To make this test pass, you need to either

  • (a) change your Venue factory to include a City;
  • (b) change your CRUD to not have the City required;

Alternatively, it's possible that your Factory creates an entry with city_id but the actual Backpack field uses a relationship field, and its name is city (not city_id). This mismatch between city and city_id can be fixed my overriding what input the CRUD tests use for the Create and Update operations, to have both city and city_id. In your XCrudControllerTest:

    protected function setUp(): void
    {
        parent::setUp();

        $data = Venue::factory()->raw();
        $data['city'] = $data['city_id'];
        // unset($data['city_id']); // if you'd like
        
        $this->createInput = $data;
        $this->updateInput = $data;
    }

The password field confirmation does not match.

It's likely that you'll see a failure like this for the Create & Update tests, when you have a password confirmation field:

  FAILED  Tests\Feature\Admin\UserCrudControllerTest > update endpoint modifies entry in database                                                                        
  Session has unexpected errors: 

{
    "default": [
        "The password field confirmation does not match."
    ]
}
Failed asserting that true is false.

One way to fix this would be to hard-code the for input that gets created/updated, in your UserCrudControllerTest:

    protected function setUp(): void
    {
        parent::setUp();

        $this->createInput = [
            'name'                  => 'Test User',
            'email'                 => '[email protected]',
            'password'              => 'Password123!',
            'password_confirmation' => 'Password123!',
        ];

        $this->assertCreateInput = [
            'name'  => 'Test User',
            'email' => '[email protected]',
        ];

        $this->updateInput = [
            'name'                  => 'Updated User',
            'email'                 => '[email protected]',
            'password'              => 'NewPassword123!',
            'password_confirmation' => 'NewPassword123!',
        ];

        $this->assertUpdateInput = [
            'name'  => 'Updated User',
            'email' => '[email protected]',
        ];
    }

Support

To submit issues, bugs and feature requests, please see our laravel-backpack/community-forum repo on Github.

Security

If you discover any security related issues, please email [email protected] instead of using the issue tracker.

License

This software is proprietary & closed-source, released under the End-User License Agreement (EULA) for Private Backpack Addons. A copy of that license is also provided inside the source code - you can read that file by using the tabs at the beginning of this page.

Package Access

You don't currently have access to this package. To gain access, go ahead and purchase it. You'll get:

Next 12 months
  • download or install using Composer;
  • all updates (major, minor and patch);
After 12 months
  • can still access all versions and updates you paid;
  • can still install using Composer;
  • no new versions or updates;
Buy for 99 EUR 49 EUR