# request-factories: testing Laravel form requests cleanly

> worksome/request-factories gives every Laravel FormRequest a factory, so a test can send the one field it cares about instead of every field the validation rules demand. The package generates the factory class with an Artisan command, mirrors the model factory idea, and works with Pest or with PHPUnit.

**worksome/request-factories** — Test requests in Laravel without all the boilerplate.

- Repository: https://github.com/worksome/request-factories
- Stars: 622 · Forks: 16
- Language: PHP
- License: MIT
- Published: 2026-10-22 · Updated: 2026-10-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/worksome-request-factories

## The problem is that FormRequest validation is contagious

The package starts from a complaint that anyone with a Laravel test suite will recognise. You want to test one behaviour of a route, but the route is backed by a FormRequest with validation rules, and Laravel refuses the request unless every required field is present. So the test ends up sending a phone number, an email, a name, a company, a bio, an uploaded file and a terms flag, and the assertion is on the phone number.

The readme lists three downsides to that approach, and they are worth repeating because each one points at a different kind of waste.

It muddies the test, because tests are supposed to be terse and easy to read. It makes writing tests annoying, because you probably have more than one test per route and every test repeats the same fields. And it requires knowledge of the FormRequest, because you have to understand what each field does before you can write a passing test, and if you do not, you are likely to be caught in a trial-and-error loop, or worse, you create a false positive while creating the test.

That third point is the serious one. A test that passes because you filled in the form correctly says nothing about the behaviour you were checking, and the effort of filling it in correctly is effort that is repeated in every other test on that route.

## A factory stands in for the valid payload

The replacement is a single call before the request:

```php
it('can sign up a user with an international phone number', function () {
    SignupRequest::fake();

    $this->put('/users', ['phone' => '+375 154 767 1088']);

    expect(User::latest()->first()->phone)->toBe('+375 154 767 1088');
});
```

The factory is registered for the request, and the test then sends only the field it actually cares about. The assertion is still on the phone number, but now the test says what it means.

The mechanics are described in the readme in terms of registering the factory globally with `fake`, and the note that you should make it the last method you call on the factory and call it before making the request to the relevant endpoint. What the readme does not do is describe the internals of how the request is satisfied, so if you want to know whether the validation rules still run, whether the request is bypassed, or how the factory data is merged with what you passed in, that is not answered here and you would have to read the source or ask.

The examples use Pest PHP, but the readme says plainly that this works just as well in PHPUnit, so the test runner is not a constraint.

## Artisan generates the class for you

You create a factory with an Artisan command, and the command takes either the FormRequest class or the factory name.

Passing the fully qualified class name:

```bash
php artisan make:request-factory "App\Http\Requests\SignupRequest"
```

That creates a request factory at `tests/RequestFactories/SignupRequestFactory.php`.

Or you can name the factory yourself:

```bash
php artisan make:request-factory SignupRequestFactory
```

The naming is free, but the readme recommends two defaults that make the package easier to live with: put the factories in `tests/RequestFactories`, which the Artisan command does for you, and use a `Factory` suffix, so `SignupRequest` becomes `SignupRequestFactory`.

The second recommendation is not cosmetic. A request factory usually complements a FormRequest, and the readme notes that request factories also work with standard requests, so the pairing is a convention rather than a requirement. Keeping the two side by side in the same directory makes it obvious which request a factory belongs to, and the suffix keeps the class names distinct in the same test file.

## The definition method mirrors Laravel model factories

The generated class extends `Worksome\RequestFactories\RequestFactory` and has an empty `definition` method:

```php
namespace Tests\RequestFactories;

use Worksome\RequestFactories\RequestFactory;

class SignupRequestFactory extends RequestFactory
{
    public function definition(): array
    {
        return [
            'phone' => '01234567890',
            'email' => 'foo@bar.com',
            'name' => 'Luke Downing',
            'company' => 'Worksome',
            'bio' => $this->faker->words(300, true),
            'accepts_terms_and_conditions' => true,
        ];
    }
```

If you have used Laravel's model factories, the readme says this will look familiar, and explains why: a model factory generates data for Eloquent models, a request factory generates data for form requests.

The `definition` method should return an array of valid data that can be used when submitting the form, so the values in it have to satisfy the validation rules. The `faker` property is available for generating fake content, and in the example the bio is a generated paragraph rather than a literal string, so the payload is not a fixed shape.

File uploads are kept separate, in a `files` method, so that the data destined for a normal input and the data destined for a file input do not get mixed:

```php
    public function files(): array
    {
        return [
            'profile_picture' => $this->file()->image('luke.png', 200, 200),
        ];
    }
```

The `file()` helper with an image name and dimensions produces a fake uploaded file, which is the same shape Laravel's own `UploadedFile::fake()` produces.

## Four ways to use a factory in a test

The readme offers several options, and the choice is mostly about how much ceremony you want in a given test.

The `create` method is the one most similar to Laravel's model factories. It returns an array you pass to the request yourself:

```php
it('can sign up a user with an international phone number', function () {
    $data = SignupRequest::factory()->create(['phone' => '+44 1234 567890']);
    
    $this->put('/users', $data)->assertValid();
});
```

The `fake` method on the factory registers it globally, which suits the common case of one request per test:

```php
it('can sign up a user with an international phone number', function () {
    SignupRequestFactory::new()->fake();
    
    $this->put('/users')->assertValid();
});
```

And if you are used to Laravel model factories, the familiar `::factory()` is available on all FormRequests, because the package automatically registers the `::fake()` and `::factory()` methods on them via macros. You do not have to enable anything:

```php
it('can sign up a user with an international phone number', function () {
    // Using the factory method...
    SignupRequest::factory()->fake();
    
    // ...or using the fake method
    SignupRequest::fake();
    
    $this->put('/users')->assertValid();
});
```

For Pest there is also a higher order `fakeRequest` method you can chain onto, which the readme introduces at the end of the section before it is truncated.

The macros deserve a note. Registering methods on every FormRequest at runtime is convenient, and it is also the kind of global behaviour that can surprise you when a method name collides with something in your own codebase.

## The package's own tests run in containers

The repository has its own development environment in containers. The compose file defines two services, both built from the `docker` directory, with the project mounted at `/var/www/html`.

The `php` service carries two environment variables for debugging:

```yaml
  php:
    build: ./docker
    volumes:
      - .:/var/www/html
    environment:
      - XDEBUG_MODE=${XDEBUG_MODE:-off}
      - XDEBUG_CONFIG=${XDEBUG_CONFIG:-client_host=host.docker.internal}
```

Both default to off, so a plain run has no debugger overhead, and you switch the remote debugging on per session rather than editing the compose file. The `composer` service uses the same image with a different entrypoint, which is a common trick for running Composer against the same mounted project without a separate container definition.

The rest of the repository shows a project with its quality tooling in place: a `phpunit.xml.dist` for the test configuration, `ecs.php` for the coding standard, `phpstan.neon.dist` with a `phpstan-baseline.neon` next to it, and `rector.php` for automated refactoring. A baseline file next to a static analysis configuration is a pragmatic choice, since it lets the project fix new findings while deferring the existing ones, and it also tells you the code is not yet clean under the current rules.

There is a `stubs` directory, which is where the package's stub files for its Artisan command and any generated class templates live, a `config` directory, and an `UPGRADE.md` file, which is the document to read before you move between major versions.

## Licence, releases and the shape of the project

request-factories is MIT licensed, with the licence file at the root of the repository, and the default branch is `main`. The repository is not archived.

The release history shows active work: version 3.4.0 on 2025-02-26, 3.5.0 on 2026-03-02 and 3.5.1 on 2026-04-09, and the last push was on 2026-10-12, six months after the most recent tag. The version numbering is the ordinary semantic versioning convention with a major version above 2, so breaking changes to the factory API are possible between majors, and `UPGRADE.md` is the place they are recorded.

The package is a developer dependency, installed with Composer using the `--dev` flag:

```bash
composer require --dev worksome/request-factories 
```

That is the right shape for it, because a factory only exists in the test suite and should never end up in a production dependency graph.

The repository also carries a `.github` directory with workflows for unit tests and for static analysis, the latter corresponding to the PHPStan configuration, so the package is checked by the same kind of tooling it asks its users to run.

## Conclusion

request-factories suits a Laravel codebase with a lot of FormRequest classes and a test suite that has grown tired of repeating every validated field in every request test. It is a poor fit for a project with few forms, where the extra class per request is more machinery than the problem deserves, and it is a poor fit if you want the request validation to actually run against your payload, since the factory is explicitly there to stand in for the full valid payload. Before you adopt it, decide whether your tests should assert on validation or on behaviour, because that choice determines whether a factory helps or hides the thing you meant to test.

## FAQ

### What problem does worksome/request-factories solve?

It removes the need to repeat every field required by a FormRequest's validation rules in each request test. A factory supplies valid data for the request, so a test can send only the field it is actually asserting on.

### How do I create a request factory?

Run `php artisan make:request-factory "App\Http\Requests\SignupRequest"` with the FormRequest class name, or `php artisan make:request-factory SignupRequestFactory` with the name you want. The command creates the class in tests/RequestFactories.

### Does it work with PHPUnit as well as Pest?

Yes. The examples in the readme use Pest PHP, but it says explicitly that this works just as well in PHPUnit, and the package also provides a higher order fakeRequest method for Pest users.

### How do I handle file uploads in a request factory?

File data is declared separately in a `files` method, so it does not get mixed with the ordinary request data. The example uses `$this->file()->image('luke.png', 200, 200)` to produce a fake image upload.

### What licence is request-factories under and how is it installed?

It is MIT licensed, and it is installed as a developer dependency with `composer require --dev worksome/request-factories`. The most recent tags are 3.4.0, 3.5.0 and 3.5.1, and the repository keeps an UPGRADE.md for breaking changes between versions.

## Sources

- [Issues](https://github.com/worksome/request-factories/issues)
- [License: MIT](https://github.com/worksome/request-factories/blob/main/LICENSE)
- [README](https://github.com/worksome/request-factories/blob/main/README.md)
- [Releases](https://github.com/worksome/request-factories/releases)
- [worksome/request-factories on GitHub](https://github.com/worksome/request-factories)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/worksome-request-factories
