worksome/request-factories

repository·main·Indexed 20 days ago

https://github.com/worksome/request-factories

A Laravel testing utility that simplifies route testing by automatically faking required data for FormRequests. It provides a `make:request-factory` Artisan command to generate factories, allowing developers to populate requests with valid data via `fake()` or `factory()` macros, reducing boilerplate validation data in test cases. Supports Faker, file uploads, state manipulation, and integration with Pest PHP via `fakeRequest()`.

Tokens
3.9K
Snippets
15
Records
16
Agent score
71%

What's inside request-factories

  1. Overview of Request Factories

    main

    Request Factories allow you to test Laravel routes by faking FormRequest validation requirements. Instead of manually providing every field required by a FormRequest in every test, you can call the fake() method on the request class. This method automatically populates the request with valid data for all required fields, allowing you to focus your test on the specific field or behavior you are actually testing.

    // Without Request Factories (Boilerplate heavy)
    $this->put('/users', [
        'phone' => '+375 154 767 1088',
        'email' => 'foo@bar.com',
        'name' => 'Luke Downing',
        'company' => 'Worksome',
        'bio' => 'Blah blah blah',
        'profile_picture' => UploadedFile::fake()->image('luke.png', 200, 200),
        'accepts_terms_and_conditions' => true,
    ]);
    
    // With Request Factories (Clean and focused)
    SignupRequest::fake();
    $this->put('/users', ['phone' => '+375 154 767 1088']);
  2. Override Request Factory data

    main

    When multiple sources provide data for a request, they follow this order of precedence (highest to lowest):

    1. Explicit Request Data: Data passed directly to get, post, put, patch, delete, etc.
    2. Factory State: Data defined via the state() method or other state-transforming methods on the factory.
    3. Factory Definition: Data defined in the definition() and files() methods.
    // 'email' in the put() call will win over the state and the definition
    it('can sign up', function () {
        SignupRequest::factory()
            ->state(['name' => 'Oliver Nybroe', 'email' => 'oliver@worksome.com'])
            ->fake();
        
        $this->put('/users', ['email' => 'luke@worksome.com'])->assertValid();
    });
  3. Escape dots in `state` method keys (v2.0.0+)

    main

    Since v2.0.0, the state method supports dot notation for updating attribute state. If your property keys contain literal dots (e.g., in a domain name), you must escape them with a backslash (\) to prevent them from being interpreted as nested attribute paths.

    // Before
    $data = $factory->state(['worksome.co.uk' => 'Worksome UK'])->create();
    
    // After
    $data = $factory->state(['worksome\.co\.uk' => 'Worksome UK'])->create();
  4. Run tests and static analysis

    main

    You can run the project's test suite and static analysis checks using the following methods:

    Using Composer:

    composer test

    Using Docker Compose: If you prefer using the provided Docker environment, run:

    docker-compose run --rm composer test
  5. Create a Request Factory

    main

    Use the make:request-factory Artisan command to generate a new factory. You can pass the Fully Qualified Class Name (FQCN) of a FormRequest to automatically link it, or provide a custom name for the factory.

    Recommended defaults:

    1. Store factories in tests/RequestFactories.
    2. Use the Factory suffix (e.g., SignupRequestFactory for SignupRequest).
    # Create a factory for a specific FormRequest
    php artisan make:request-factory "App\Http\Requests\SignupRequest"
    
    # Create a factory with a custom name
    php artisan make:request-factory SignupRequestFactory
  6. Remove the `HasFactory` trait in v3.0.0

    main

    In v3.0.0, the HasFactory trait has been removed to avoid requiring the package as a production dependency. The same functionality is now achieved using macros on the FormRequest class. To upgrade, remove use Worksome equest-factories\Concerns\HasFactory; and the use HasFactory; statement from your FormRequest classes.

    // Before
    use Illuminate\Foundation\Http\FormRequest;
    use Worksome\RequestFactories\Concerns\HasFactory;
    
    class MyFormRequest extends FormRequest
    {
        use HasFactory;
    }
    
    // After
    use Illuminate\Foundation\Http\FormRequest;
    
    class MyFormRequest extends FormRequest
    {
    }
  7. Use Request Factories in tests

    main

    There are several ways to apply factory data to your tests:

    1. create(): Returns an array of data. Best for passing data explicitly to request methods like post() or put().
    2. fake() on the Factory: Registers the factory globally. Must be the last method called on the factory and called before the request.
    3. fake() or factory() on the FormRequest: Uses macros automatically added to all FormRequest classes. This is the most seamless way to register a factory for a specific request class.
    4. fakeRequest() (Pest PHP only): A higher-order method for Pest tests that accepts a FormRequest FQCN, a Factory FQCN, or a closure.
    // 1. Using create()
    $data = SignupRequest::factory()->create(['phone' => '+44 1234 567890']);
    $this->put('/users', $data)->assertValid();
    
    // 2. Using fake() on the factory instance
    SignupRequestFactory::new()->fake();
    $this->put('/users')->assertValid();
    
    // 3. Using factory() or fake() macros on the FormRequest
    SignupRequest::factory()->fake();
    // OR
    SignupRequest::fake();
    $this->put('/users')->assertValid();
    
    // 4. Using fakeRequest() in Pest
    // With FormRequest FQCN
    it('test', function () {
        $this->put('/users')->assertValid();
    })->fakeRequest(SignupRequest::class);
    
    // With Factory FQCN
    it('test', function () {
        $this->put('/users')->assertValid();
    })->fakeRequest(SignupRequestFactory::class);
    
    // With a closure
    it('test', function () {
        $this->put('/users')->assertValid();
    })->fakeRequest(fn () => SignupRequest::factory());
    
    // Chaining state to fakeRequest
    it('test', function () {
        $this->put('/users')->assertValid();
    })->fakeRequest(SignupRequest::class)->state(['name' => 'Jane Bloggs']);
  8. Use factories without FormRequests

    main

    You can use Request Factories to fake data for generic requests that do not have a dedicated FormRequest class. Simply instantiate the factory and call fake() on it.

    // Faking a generic request without a FormRequest class
    it('lets a guest sign up to the newsletter', function () {
        NewsletterSignupFactory::new()->fake();
        
        post('/newsletter', ['email' => 'foo@bar.com'])->assertRedirect('/thanks');
    });
  9. Change the default location for request factories

    main

    If you do not want to use the default directory or namespace for your request factories, you can customize them via a configuration file.

    1. Publish the configuration file:
    php artisan vendor:publish --tag=request-factories
    1. Edit config/request-factories.php to update the path and namespace keys.
    return [
        'path' => base_path('request_factories'),
        'namespace' => 'App\\RequestFactories',
    ];
  10. Resolve `CouldNotLocateRequestFactoryException`

    main

    If you encounter a CouldNotLocateRequestFactoryException when calling ::fake() or ::factory() on a FormRequest, it means the library cannot automatically find your request factory based on your directory structure.

    You can resolve this by explicitly defining the factory class in your FormRequest using the public static $factory property.

    class SignupRequest extends FormRequest
    {
        public static $factory = SignupRequestFactory::class; 
    }