CoderFunda
  • Home
  • About us
    • Contact Us
    • Disclaimer
    • Privacy Policy
    • About us
  • Home
  • Php
  • HTML
  • CSS
  • JavaScript
    • JavaScript
    • Jquery
    • JqueryUI
    • Stock
  • SQL
  • Vue.Js
  • Python
  • Wordpress
  • C++
    • C++
    • C
  • Laravel
    • Laravel
      • Overview
      • Namespaces
      • Middleware
      • Routing
      • Configuration
      • Application Structure
      • Installation
    • Overview
  • DBMS
    • DBMS
      • PL/SQL
      • SQLite
      • MongoDB
      • Cassandra
      • MySQL
      • Oracle
      • CouchDB
      • Neo4j
      • DB2
      • Quiz
    • Overview
  • Entertainment
    • TV Series Update
    • Movie Review
    • Movie Review
  • More
    • Vue. Js
    • Php Question
    • Php Interview Question
    • Laravel Interview Question
    • SQL Interview Question
    • IAS Interview Question
    • PCS Interview Question
    • Technology
    • Other

19 August, 2022

Test form requests in Laravel with Request Factories by Worksome

 Programing Coderfunda     August 19, 2022     Laravel, Packages     No comments   

 A month or two ago, I was busy writing tests for a Laravel app. More specifically, I was testing a Laravel post request...

  • it allows a user to sign up
  • it requires a valid email
  • it does not allow a user to sign up twice
  • it allows a user to fill in address details

You get the picture. The more tests for this sign up flow I created, the more frustrated I became. Why? Because I had to keep repeating myself over and over and over again. See, any request worth its salt is going to have validation. Because of that, even if our test only deals with a small subset of fields in the request, we have to include everything!

Let's take the case of 'it allows a user to fill in address details'. I'm actually interested in perhaps 4 fields: address_line_1, address_line_2, city, and postcode. Buuuuuut, because of validation I have to pass email, first_name, last_name, telephone, accepts_terms and every other required fields.

1it('allows a user to fill in address details', function () {
2 $this->post('/signup', [
3 'first_name' => 'Luke', // Don't care
4 'last_name' => 'Downing', // Not what I'm testing
5 'email' => 'foo@bar.com', // Sigh
6 'telephone' => '01234567890', // Okay, I'm getting annoyed now
7 'accepts_terms' => true, // Are we there yet?
8 'address_line_1' => '1 Test Street', // Finally!
9 'address_line_2' => 'Test Area',
10 'city' => 'Testerfield',
11 'postcode' => 'T35T 1NG',
12 ]);
13 
14 expect(User::latest()->first())
15 ->address_line_1->toBe('1 Test Street')
16 ->address_line_2->toBe('Test Area')
17 ->city->toBe('Testerfield')
18 ->postcode->toBe('T35T 1NG');
19});

This problem only gets more frustrating as we add more tests. And that's a real issue, because it can actually lead to not writing tests at all! Further, imagine adding a new required field to this form 6 months down the line. We're going to have to update all of these tests and add more data that doesn't add value to the test just to make them pass.

This got me thinking. See, we had a similar problem with eloquent models a while back. But we solved that with model factories: classes that would do all of the laborious work for you so that you could just write User::factory()->create() in your test. Why not apply that same approach to requests?

1it('allows a user to fill in address details', function () {
2 SignupRequest::fake();
3 
4 $this->post('/signup', [
5 'address_line_1' => '1 Test Street',
6 'address_line_2' => 'Test Area',
7 'city' => 'Testerfield',
8 'postcode' => 'T35T 1NG',
9 ]);
10 
11 expect(User::latest()->first())
12 ->address_line_1->toBe('1 Test Street')
13 ->address_line_2->toBe('Test Area')
14 ->city->toBe('Testerfield')
15 ->postcode->toBe('T35T 1NG');
16});

Oh, that's so much nicer. It's shorter, easier to write, simpler to maintain and conveys the purpose of the test without uncecessary detail. Perfection!

So, without further ado, I'm pleased to present Request Factories by Worksome!

Setting up our request factory

The package ships with an artisan command to easily generate new factories for your project. You can pass the desired name of your factory, or the Fully Qualified Class Name (FQCN) of a form request from your app.

It's important to note that form requests are completely optional. Request factories work just fine with standard requests too.

1# Using a custom name
2php artisan make:request-factory SignupRequestFactory
3 
4# Using a form request FQCN
5php artisan make:request-factory "App\Http\Requests\SignupRequest"

By default, your factory will be placed under tests/RequestFactories. When you open your new request factory, you'll notice a definition method that returns an array. Much like model factories, this is where we define any attributes we want to be present when faking a request.

My recommendation is that you only include the minimum number of attributes to complete a valid request in the definition method, and use methods to decorate your factory as desired.

1class SignupRequestFactory extends RequestFactory
2{
3 public function definition(): array
4 {
5 return [
6 'email' => $this->faker->safeEmail,
7 'first_name' => $this->faker->firstName,
8 'last_name' => $this->faker->lastName,
9 'telephone' => '01234567890',
10 'accepts_terms' => true,
11 ];
12 }
13}

Note that request factories have a $faker property we can access to create randomised data rather than hardcoding values. With our required fields defined, we can now fake the data in our request.

1it('allows a user to fill in address details', function () {
2 SignupRequestFactory::new()->fake();
3 
4 $this->post('/signup', [
5 'address_line_1' => '1 Test Street',
6 'address_line_2' => 'Test Area',
7 'city' => 'Testerfield',
8 'postcode' => 'T35T 1NG',
9 ]);
10 
11 expect(User::latest()->first())
12 ->address_line_1->toBe('1 Test Street')
13 ->address_line_2->toBe('Test Area')
14 ->city->toBe('Testerfield')
15 ->postcode->toBe('T35T 1NG');
16});

One cool (although perhaps divisive) feature of eloquent model factories is the ability to create a new factory instance directly from a model: User::factory(). I wanted to bring a little bit of that magic to request factories too, so there is a Worksome\RequestFactories\Concerns\HasFactory trait available that you may add to your form requests if you'd like. Once added, you fake directly from the form request:

1it('allows a user to fill in address details', function () {
2 SignupRequest::fake();
3 
4 $this->post('/signup', [
5 'address_line_1' => '1 Test Street',
6 'address_line_2' => 'Test Area',
7 'city' => 'Testerfield',
8 'postcode' => 'T35T 1NG',
9 ]);
10 
11 expect(User::latest()->first())
12 ->address_line_1->toBe('1 Test Street')
13 ->address_line_2->toBe('Test Area')
14 ->city->toBe('Testerfield')
15 ->postcode->toBe('T35T 1NG');
16});

Of course, this is completely optional, and only really makes sense if you make use of form requests in your application. But it's a nice-to-have if that's your style.

Adding methods to our factory

One of the great things about factories is the ability to add methods that transform factory state. For example, let's say we have a test for postcode formatting. We need to provided other address fields to create a valid request, so why not add a withAddress() method to our factory?

1class SignupRequestFactory extends RequestFactory
2{
3 public function definition(): array
4 {
5 return [
6 'email' => $this->faker->safeEmail,
7 'first_name' => $this->faker->firstName,
8 'last_name' => $this->faker->lastName,
9 'telephone' => '01234567890',
10 'accepts_terms' => true,
11 ];
12 }
13 
14 public function withAddress(): self
15 {
16 return $this->state([
17 'address_line_1' => '1 Test Street',
18 'address_line_2' => 'Test Area',
19 'city' => 'Testerfield',
20 'postcode' => 'T35T 1NG',
21 ]);
22 }
23}

You'll note the state method, similar to Laravel's model factories. We've tried to keep the APIs very similar, so it will feel immediately familiar. Now, let's go write our test:

1it('correctly formats the postcode', function () {
2 SignupRequestFactory::new()->withAddress()->fake();
3 
4 $this->post('/signup', ['postcode' => 'T3 5T1N G']);
5 
6 expect(User::latest()->first()->postcode)->toBe('T35T 1NG')
7});

How nice is that? Our test maintains its focus; we only have to provide the field we're actually testing. Also, note that any data you pass to post (or any of the Laravel request testing methods for that matter), will override data defined in the factory.

Other goodies

We're really just scratching the surface of what's possible with request factories in this post. Here's a quick showcase of some other goodies available.

Omitting fields from a request

1it('requires an email', function () {
2 SignupRequestFactory::new()->without(['email'])->fake();
3 
4 $this->post('/signup')->assertInvalid(['email']);
5});

Using nested factories for shared fields

1class MailingRequestFactory extends RequestFactory
2{
3 public function definition(): array
4 {
5 return [
6 'email' => $this->faker->safeEmail,
7 'address' => AddressRequestFactory::new(),
8 ];
9 }
10}

Using closures for lazily defined attributes

1class MailingRequestFactory extends RequestFactory
2{
3 public function definition(): array
4 {
5 return [
6 'first_name' => $this->faker->firstName,
7 'last_name' => $this->faker->lastName,
8 'email' => fn ($attrs) => "{$attrs['first_name']}.{$attr['last_name']}@foo.com",
9 ];
10 }
11}

Using create on a factory

If you don't want to fake a request globally or prefer a syntax closer to model factories, you can call create on a request factory to return an array of input that can then be passed to post or other request methods.

1it('requires an email', function () {
2 $data = SignupRequestFactory::new()->create();
3 
4 $this->post('/signup', $data)->assertInvalid(['email']);
5});

Using the fakeRequest helper with Pest PHP

If you're using Pest PHP for testing, we provide a groovy fakeRequest helper that can be chained onto the end of your test if you prefer.

1it('correctly formats the postcode', function () {
2 $this->post('/signup', ['postcode' => 'T3 5T1N G']);
3 
4 expect(User::latest()->first()->postcode)->toBe('T35T 1NG')
5})
6 ->fakeRequest(SignupRequestFactory::class)
7 ->withAddress();

Wrapping up

For more details on all the things you can do with request factories, be sure to check out the detailed documentation!

I think request factories have huge potential in making testing requests simpler to write, easier to maintain and providing greater clarity on the purpose of each test.

I look forward to seeing how you use them in your projects! Make sure to reach out on Twitter to let me know what you think.

Take care!

Read More
  • Share This:  
  •  Facebook
  •  Twitter
  •  Google+
  •  Stumble
  •  Digg

Kinetic: A view-composer package for Inertia.js

 Programing Coderfunda     August 19, 2022     Laravel, Packages     No comments   

 Kinetic adds view-composer-like features to the Inertia.js Laravel adapter. Like Laravel view composers, Kinetic can bind data each time a component is rendered from a single location.

Within a service provider, you can call the composer() method to define Inertia composers:

1// In a service provider
2public function boot()
3{
4 // Class-based composer..
5 Inertia::composer('User/Profile', UserComposer::class);
6}
7 
8// Composer class
9class UserComposer
10{
11 public function compose(ResponseFactory $inertia)
12 {
13 $inertia->with('list', [
14 'foo' => 'bar',
15 'baz' => 'buzz'
16 ]);
17 }
18}

The composer() method supports closure-based composers as well:

1Inertia::composer('User/Profile', function (ResponseFactory $inertia) {
2 $inertia->with([
3 'post' => [
4 'subject' => 'Hello World!',
5 'description' => 'This is a description.'
6 ]
7 ]);
8});

With composers defined in a service provider, your props will include the composing data when you call render():

1// Includes bound data from `Inertia::composer('User/Profile')`
2Inertia::render('User/Profile');

You can learn more about this package, get full installation instructions, and view the source code on GitHub.

Read More
  • Share This:  
  •  Facebook
  •  Twitter
  •  Google+
  •  Stumble
  •  Digg
Newer Posts Older Posts Home

Meta

Popular Posts

  • Generate Migrations from an Existing Database With the Migration Generator Package
    Laravel Migration Generator Migration Generator for Laravel is a package by Bennett Treptow to generate migrations from existing database ...
  • Laravel Razorpay Integration | Payment gateway integration Laravel in 30 mins | Laravel Razorpay
    1 Integration of Razorpay with Laravel. In this tutorial, I have taught how to integrate payment gateway with laravel with mini-project. If...
  • Search Through Models with Laravel Searchable
      Laravel Searchable   is a package by   Spatie   to search through models and other sources pragmatically. Using this package, you can get ...
  • .Net 8 XUnit: Use an In-Memory DBConnection for testing as a replacement for the real MySqlConnection
    I'm creating tests for my .Net 8 API, and as I want to test with fake self created data (instead of using the real MySql connection) I...
  • Bagisto E-commerce Platform
      Bagisto is an open-source E-commerce platform built on top of Laravel and Vue.js by Webkul. Bagisto is an E-commerce ecosystem designed...

Categories

  • Ajax (26)
  • Bootstrap (30)
  • DBMS (42)
  • HTML (12)
  • HTML5 (45)
  • JavaScript (10)
  • Jquery (34)
  • Jquery UI (2)
  • JqueryUI (32)
  • Laravel (1017)
  • Laravel Tutorials (23)
  • Laravel-Question (6)
  • Magento (9)
  • Magento 2 (95)
  • MariaDB (1)
  • MySql Tutorial (2)
  • PHP-Interview-Questions (3)
  • Php Question (13)
  • Python (36)
  • RDBMS (13)
  • SQL Tutorial (79)
  • Vue.js Tutorial (69)
  • Wordpress (150)
  • Wordpress Theme (3)
  • codeigniter (108)
  • oops (4)
  • php (853)

Social Media Links

  • Follow on Twitter
  • Like on Facebook
  • Subscribe on Youtube
  • Follow on Instagram

Pages

  • Home
  • Contact Us
  • Privacy Policy
  • About us

Blog Archive

  • ▼  2026 (52)
    • ▼  07/26 - 08/02 (51)
      • Manoj Bajpayee on RGV reunion: 'Ramu doesn't need ...
      • Spider-Man: Brand New Day gets UA 13+ in India as ...
      • Priyanka gives a relatable peek into bedtime with ...
      • Adil reveals his nieces are stranded and their hom...
      • ‘The Walking Dead: Dead City’ Season 3: All about ...
      • 'Jana Nayagan' BO day 5: Vijay's film sees big Mon...
      • Justin Baldoni ordered to pay USD 171K in lawsuit
      • Kangana Ranaut praises PM Modi’s response to NEET-...
      • Inside Sonam Wangchuk’s sustainable ‘Tribal House’...
      • Feige claims Thanos was 'built up in people's head...
      • Tiger Shroff all set for professional football debut
      • Sunny Deol dedicates 'Batwara 1947' to his mother:...
      • Historian reveals why the royal life of Harry and ...
      • Somy Ali announces $5,000 reward to find missing a...
      • D4vd to stand trial in murder of 14-year-old: Judge
      • Ram Charan health update: Actor undergoes successf...
      • Delhi HC orders removal of Kala Hiran teaser, gran...
      • Ruchir Arun opens up on Vikrant Massey and 'Musafi...
      • Timothée Chalamet’s mother pays tribute to Kris Je...
      • Kourtney Kardashian pays tribute to her grandmother
      • Built in 1948, Mud Mountain Dam gets a $112 millio...
      • This 71-year-old US mayor goes to a homeless shelt...
      • Female African treefrogs mate with several males s...
      • D4vd murder case: Court reveals Celeste Rivas alle...
      • Quote of the day by Marco Rubio: 'American Dream i...
      • Quote by Andrei Tarkovsky: 'A book read by a thous...
      • Paramount-WB merger: Benedict Cumberbatch, Alan Cu...
      • Can ICE or CBP read your deleted messages? Attorne...
      • Quote of the day by Sun Tzu: 'If you know the enem...
      • Who is Luplupka? Russian Kick streamer involved in...
      • Hummingbird hawkmoths may have their own version o...
      • Quote by Bob Ross: 'The very fact that you're awar...
      • Who is HMBLZAYY? The Twitch streamer who walked 3,...
      • In 1802, an explorer released pigs on Kangaroo Isl...
      • For nearly 250 years, a notebook lay unnoticed unt...
      • Netflix executive reveals at company's trust-build...
      • Quote of the day by Taylor Swift's husband Travis ...
      • Seth Rogen’s blunt response to YouTuber Yung Mooch...
      • Iran's shadow oil billions vanish as trusties flee...
      • What is Tana Mongeau and Clavicular’s feud? Podcas...
      • 'The Odyssey' crosses USD 639.6 million globally
      • 'Ramayana' to have 'Baahubali'-style cliffhanger e...
      • Kim Kardashian remember late granny on 92nd heaven...
      • Emma Roberts weds Cody John in intimate Idaho cere...
      • 'Jana Nayagan' BO day 4: Film crosses Rs 100 Cr Net
      • 'Chennai Love Story' BO jumps 11.1%
      • China’s new hybrid rice can preserve its high-yiel...
      • Quote of the day by Thucydides: 'Self-control is t...
      • Quote of the day by Slavoj Žižek: 'The problem for...
      • A stormwater pond in Calgary appears filled with P...
      • Inside Kriti Sanon's duplex sea-facing penthouse w...
    • ►  06/28 - 07/05 (1)
  • ►  2025 (4)
    • ►  07/06 - 07/13 (2)
    • ►  06/29 - 07/06 (2)
  • ►  2024 (486)
    • ►  09/15 - 09/22 (30)
    • ►  09/08 - 09/15 (35)
    • ►  09/01 - 09/08 (35)
    • ►  08/11 - 08/18 (2)
    • ►  08/04 - 08/11 (33)
    • ►  07/28 - 08/04 (30)
    • ►  07/07 - 07/14 (11)
    • ►  06/30 - 07/07 (35)
    • ►  06/23 - 06/30 (5)
    • ►  06/02 - 06/09 (31)
    • ►  05/26 - 06/02 (20)
    • ►  05/05 - 05/12 (29)
    • ►  04/28 - 05/05 (26)
    • ►  04/07 - 04/14 (10)
    • ►  03/31 - 04/07 (34)
    • ►  03/24 - 03/31 (10)
    • ►  03/03 - 03/10 (35)
    • ►  02/25 - 03/03 (15)
    • ►  02/04 - 02/11 (22)
    • ►  01/28 - 02/04 (30)
    • ►  01/07 - 01/14 (8)
  • ►  2023 (484)
    • ►  12/31 - 01/07 (35)
    • ►  12/24 - 12/31 (10)
    • ►  12/03 - 12/10 (33)
    • ►  11/26 - 12/03 (20)
    • ►  11/05 - 11/12 (35)
    • ►  10/29 - 11/05 (20)
    • ►  10/22 - 10/29 (9)
    • ►  10/15 - 10/22 (7)
    • ►  10/08 - 10/15 (9)
    • ►  10/01 - 10/08 (10)
    • ►  09/24 - 10/01 (9)
    • ►  09/17 - 09/24 (9)
    • ►  09/10 - 09/17 (7)
    • ►  09/03 - 09/10 (9)
    • ►  08/27 - 09/03 (9)
    • ►  08/20 - 08/27 (8)
    • ►  08/13 - 08/20 (8)
    • ►  08/06 - 08/13 (8)
    • ►  07/30 - 08/06 (8)
    • ►  07/23 - 07/30 (7)
    • ►  07/16 - 07/23 (8)
    • ►  07/09 - 07/16 (7)
    • ►  07/02 - 07/09 (8)
    • ►  06/25 - 07/02 (7)
    • ►  06/18 - 06/25 (7)
    • ►  06/11 - 06/18 (7)
    • ►  06/04 - 06/11 (11)
    • ►  05/28 - 06/04 (7)
    • ►  05/21 - 05/28 (8)
    • ►  05/14 - 05/21 (11)
    • ►  05/07 - 05/14 (7)
    • ►  04/30 - 05/07 (7)
    • ►  04/23 - 04/30 (8)
    • ►  04/16 - 04/23 (9)
    • ►  04/09 - 04/16 (7)
    • ►  04/02 - 04/09 (4)
    • ►  03/26 - 04/02 (21)
    • ►  03/19 - 03/26 (2)
    • ►  03/12 - 03/19 (9)
    • ►  03/05 - 03/12 (26)
    • ►  02/26 - 03/05 (25)
    • ►  01/15 - 01/22 (7)
    • ►  01/08 - 01/15 (1)
  • ►  2022 (1037)
    • ►  12/11 - 12/18 (13)
    • ►  12/04 - 12/11 (1)
    • ►  11/27 - 12/04 (40)
    • ►  11/06 - 11/13 (1)
    • ►  10/16 - 10/23 (13)
    • ►  09/04 - 09/11 (5)
    • ►  08/21 - 08/28 (24)
    • ►  08/14 - 08/21 (24)
    • ►  07/03 - 07/10 (9)
    • ►  06/19 - 06/26 (3)
    • ►  05/29 - 06/05 (3)
    • ►  05/22 - 05/29 (3)
    • ►  05/15 - 05/22 (109)
    • ►  05/01 - 05/08 (7)
    • ►  04/24 - 05/01 (7)
    • ►  04/17 - 04/24 (64)
    • ►  04/10 - 04/17 (115)
    • ►  04/03 - 04/10 (73)
    • ►  03/27 - 04/03 (77)
    • ►  03/13 - 03/20 (2)
    • ►  03/06 - 03/13 (25)
    • ►  02/27 - 03/06 (18)
    • ►  02/20 - 02/27 (153)
    • ►  02/13 - 02/20 (187)
    • ►  01/30 - 02/06 (45)
    • ►  01/23 - 01/30 (15)
    • ►  01/16 - 01/23 (1)
  • ►  2021 (412)
    • ►  10/24 - 10/31 (2)
    • ►  07/25 - 08/01 (1)
    • ►  07/11 - 07/18 (10)
    • ►  06/13 - 06/20 (29)
    • ►  05/23 - 05/30 (1)
    • ►  05/02 - 05/09 (24)
    • ►  04/25 - 05/02 (24)
    • ►  04/18 - 04/25 (112)
    • ►  04/11 - 04/18 (1)
    • ►  04/04 - 04/11 (6)
    • ►  03/28 - 04/04 (86)
    • ►  03/21 - 03/28 (19)
    • ►  03/14 - 03/21 (2)
    • ►  03/07 - 03/14 (10)
    • ►  02/28 - 03/07 (1)
    • ►  02/21 - 02/28 (29)
    • ►  02/14 - 02/21 (13)
    • ►  02/07 - 02/14 (12)
    • ►  01/31 - 02/07 (6)
    • ►  01/17 - 01/24 (2)
    • ►  01/10 - 01/17 (8)
    • ►  01/03 - 01/10 (14)
  • ►  2020 (376)
    • ►  12/27 - 01/03 (37)
    • ►  12/20 - 12/27 (92)
    • ►  12/13 - 12/20 (29)
    • ►  12/06 - 12/13 (37)
    • ►  11/29 - 12/06 (4)
    • ►  11/15 - 11/22 (14)
    • ►  11/08 - 11/15 (8)
    • ►  11/01 - 11/08 (2)
    • ►  10/18 - 10/25 (14)
    • ►  10/11 - 10/18 (16)
    • ►  10/04 - 10/11 (10)
    • ►  09/20 - 09/27 (10)
    • ►  09/06 - 09/13 (19)
    • ►  08/30 - 09/06 (26)
    • ►  08/23 - 08/30 (4)
    • ►  08/16 - 08/23 (2)
    • ►  07/12 - 07/19 (48)
    • ►  05/17 - 05/24 (2)
    • ►  01/05 - 01/12 (2)
  • ►  2019 (74)
    • ►  07/07 - 07/14 (6)
    • ►  06/16 - 06/23 (6)
    • ►  02/10 - 02/17 (17)
    • ►  01/13 - 01/20 (37)
    • ►  01/06 - 01/13 (8)
  • ►  2018 (376)
    • ►  12/30 - 01/06 (24)
    • ►  12/16 - 12/23 (8)
    • ►  12/09 - 12/16 (98)
    • ►  12/02 - 12/09 (16)
    • ►  11/18 - 11/25 (36)
    • ►  11/04 - 11/11 (18)
    • ►  10/28 - 11/04 (10)
    • ►  10/21 - 10/28 (26)
    • ►  10/14 - 10/21 (52)
    • ►  10/07 - 10/14 (4)
    • ►  09/30 - 10/07 (2)
    • ►  09/23 - 09/30 (68)
    • ►  09/16 - 09/23 (4)
    • ►  09/09 - 09/16 (4)
    • ►  08/26 - 09/02 (6)

Data Publish News

Loading...

Al Jazeera – Breaking News, World News and Video from Al Jazeera

Loading...

Laravel News

Loading...

Copyright © CoderFunda | Powered by Blogger
Design by Coderfunda | Blogger Theme by Coderfunda | Distributed By Coderfunda