Anis Uddin Ahmad Sr. Software Architect Softzino Technologies Testing in Laravel What, why and how (@ajaxray)
Why Automated Testing? Doesn’t it waste valuable developer time?
Why Automated Testing? Doesn’t it waste valuable developer time? New Feature Code Test Fixing Required Done
Why Automated Testing? Doesn’t it waste valuable developer time? New Feature Code *Test* Fixing Required Change Request Dependencies Updated Done Bug Reported
Why Automated Testing? Doesn’t it waste valuable developer time? • Reduces the time and cost of testing • Increases the accuracy of testing • Improves the quality of software • Con fi dence of changing anytime
What to test? The hidden question in every beginner’s mind
What to test? Code Coverage? (how many codes are tested) https://medium.com/@anowarhossain/code-coverage-report-in-laravel-and-make-100-coverage-of-your-code-ce27cccbc738
What to test? Things that … you want to ensure “NOT BROKEN” https://laraveldaily.com/post/matt-stau ff er-laravel-enterprise-ready
What to test? The essential things… • The routes - main entry points • Authentication and authorisations • Thing related to payment / privacy / legal issues • Business decisions • Third party dependencies (with mock or stub) • Anything that have de fi ned expectations Anything that you’d like to con fi rm after a new deployment
Perspective of testing Let’s make a toy airplane
Individual components and how they work together Perspective of testing
Unit Testing vs Functional Testing Unit Testing Functional Testing Scope Individual units of code Overall functionality of a software Target Speci fi c values, conditions, returns, exceptions Ensure features and requirements Frequency Every time any relevant code changes Every time any dependency/expectation changes Perspective Whitebox. (Transparent) Blackbox. (Machine with opaque casing)
Testing Perspectives for a software Agile testing strategy pyramid
Let’s write a Unit Test Demo session
Test writing pattern - AAA Arrange - Act - Assert
Test lifecycle Setup and Teardown fl ow tearDownAfterClass() setUpBeforeClass() tearDown() setUp() aTestCase() EXECUTE TEST GENERATE TEST REPORT
Preparing Environment Testing Environment !== Real Environment
Preparing Environment Options to de fi ne separate con fi guration <php> <env name="APP_ENV" value="testing"/> <env name="BCRYPT_ROUNDS" value="4"/> <env name="CACHE_DRIVER" value="array"/> <env name="DB_CONNECTION" value="sqlite"/> <env name="DB_DATABASE" value=":memory:"/> <env name="MAIL_MAILER" value="array"/> <env name="QUEUE_CONNECTION" value="sync"/> <env name="SESSION_DRIVER" value="array"/> <env name="TELESCOPE_ENABLED" value=“false"/> </php> phpunit.xml
Preparing Environment Options to de fi ne separate con fi guration <php> <env name="APP_ENV" value="testing"/> <env name="BCRYPT_ROUNDS" value="4"/> <env name="CACHE_DRIVER" value="array"/> <env name="DB_CONNECTION" value="sqlite"/> <env name="DB_DATABASE" value=":memory:"/> <env name="MAIL_MAILER" value="array"/> <env name="QUEUE_CONNECTION" value="sync"/> <env name="SESSION_DRIVER" value="array"/> <env name="TELESCOPE_ENABLED" value="false"/> </php> MAIL_MAILER=smtp MAIL_HOST=mailhog MAIL_PORT=1025 .env.testing phpunit.xml
Preparing Database Isolated and risk free version - for testing environment <php> ... <env name="DB_CONNECTION" value="sqlite"/> <env name="DB_DATABASE" value=":memory:"/> ... </php> phpunit.xml
Preparing Database Cleanup and prepare for testing new scenario use IlluminateFoundationTestingDatabaseMigrations; use IlluminateFoundationTestingRefreshDatabase; use TestsTestCase; class HomepageTest extends TestCase { use DatabaseMigrations, RefreshDatabase; public function test_something_with_predefined_data() { // Run the DatabaseSeeder... $this->seed(); // Run an array of specific seeders... $this->seed([ AreaListSeeder::class, DocumentCategoriesSeeder::class, ]); } // ... Execute Migrations before running test Cleanup DB after running test Stage a prede fi ned scenario in DB
Let’s write a Feature Test Demo session
What can we Assert? Various points to validate application sanity Look into Testing/TestResponse
Check if expected text is showing in page $response = $this->actingAs($user)->get('/path'); // Find text in response page $response->assertSee('Dashboard'); $response->assertDontSee('Admin'); // Can be used array of text, also can verify in order $response->assertSee(['A Menu Item', 'Another Menu Item’]); // Also can verify if they are showing in order $response->assertSeeInOrder(['A Menu Item', 'Another Menu Item']); // All of the above has a *Text() alternative // that strip_tags the response before compare $response->assertSeeText('Dashboard');
What was passed to the view? public function test_it_has_the_correct_value() { $response = $this->get('/some-route'); $this->assertEquals('John Doe', $response->viewData('name')); } public function test_it_contains_a_given_record() { $response = $this->get('/some-route'); $this->assertTrue($response->viewData('users')->contains($userA)); } public function test_it_returns_the_correct_amount_of_records() { $response = $this->get('/some-route'); $this->assertCount(10, $response->viewData('users')); } Code snippet source
Validating API Responses $response = $this->postJson('/api/user', ['name' => 'Sally']); // Validate Response JSON $response ->assertStatus(201) ->assertJson(['created' => true]); ->assertJsonPath('team.owner.name', 'Darian'); // Fluent JSON Testing $response ->assertJson(fn (AssertableJson $json) => $json->where('id', 1) ->where('name', 'Victoria Faith') ->where('email', fn (string $email) => str($email)->is('victoria@gmail.com')) ->whereNot('status', 'pending') ->missing('password') ->etc() ); // Also there are has, hasMany, hasAll, missing, missingAll etc. Code snippet source
Validating Database records $user = User::factory()->create(); // Check model existence $this->assertModelExists($user); $this->assertModelMissing($user); // SoftDeleted or not $this->assertSoftDeleted($user); $this->assertNotSoftDeleted($user); // Count records $this->assertDatabaseCount('users', 5); // Check specific record $this->assertDatabaseHas('users', [ 'email' => 'sally@example.com', ]);
Exception Handling // In feature testing $response = $this->withoutExceptionHandling()->get('/'); // Mention which exception is expected $this->expectException(MyAccessParsingException::class);
Filtering Test Run speci fi c set of tests php artisan test // Run Specific TestSuite php artisan test --testsuite Unit // Run a single class php artisan test --filter=HomepageTest // Run a single function php artisan test --filter=test_home_responds_with_success_for_Admin
@ajaxray 🐦 📬{between curly brackets} 🌎 ajaxray.com

Testing in Laravel Framework

  • 1.
    Anis Uddin Ahmad Sr.Software Architect Softzino Technologies Testing in Laravel What, why and how (@ajaxray)
  • 2.
    Why Automated Testing? Doesn’tit waste valuable developer time?
  • 3.
    Why Automated Testing? Doesn’tit waste valuable developer time? New Feature Code Test Fixing Required Done
  • 4.
    Why Automated Testing? Doesn’tit waste valuable developer time? New Feature Code *Test* Fixing Required Change Request Dependencies Updated Done Bug Reported
  • 5.
    Why Automated Testing? Doesn’tit waste valuable developer time? • Reduces the time and cost of testing • Increases the accuracy of testing • Improves the quality of software • Con fi dence of changing anytime
  • 6.
    What to test? Thehidden question in every beginner’s mind
  • 7.
    What to test? CodeCoverage? (how many codes are tested) https://medium.com/@anowarhossain/code-coverage-report-in-laravel-and-make-100-coverage-of-your-code-ce27cccbc738
  • 8.
    What to test? Thingsthat … you want to ensure “NOT BROKEN” https://laraveldaily.com/post/matt-stau ff er-laravel-enterprise-ready
  • 9.
    What to test? Theessential things… • The routes - main entry points • Authentication and authorisations • Thing related to payment / privacy / legal issues • Business decisions • Third party dependencies (with mock or stub) • Anything that have de fi ned expectations Anything that you’d like to con fi rm after a new deployment
  • 10.
    Perspective of testing Let’smake a toy airplane
  • 11.
    Individual components andhow they work together Perspective of testing
  • 12.
    Unit Testing vsFunctional Testing Unit Testing Functional Testing Scope Individual units of code Overall functionality of a software Target Speci fi c values, conditions, returns, exceptions Ensure features and requirements Frequency Every time any relevant code changes Every time any dependency/expectation changes Perspective Whitebox. (Transparent) Blackbox. (Machine with opaque casing)
  • 13.
    Testing Perspectives fora software Agile testing strategy pyramid
  • 14.
    Let’s write aUnit Test Demo session
  • 15.
    Test writing pattern- AAA Arrange - Act - Assert
  • 16.
    Test lifecycle Setup andTeardown fl ow tearDownAfterClass() setUpBeforeClass() tearDown() setUp() aTestCase() EXECUTE TEST GENERATE TEST REPORT
  • 17.
  • 18.
    Preparing Environment Options tode fi ne separate con fi guration <php> <env name="APP_ENV" value="testing"/> <env name="BCRYPT_ROUNDS" value="4"/> <env name="CACHE_DRIVER" value="array"/> <env name="DB_CONNECTION" value="sqlite"/> <env name="DB_DATABASE" value=":memory:"/> <env name="MAIL_MAILER" value="array"/> <env name="QUEUE_CONNECTION" value="sync"/> <env name="SESSION_DRIVER" value="array"/> <env name="TELESCOPE_ENABLED" value=“false"/> </php> phpunit.xml
  • 19.
    Preparing Environment Options tode fi ne separate con fi guration <php> <env name="APP_ENV" value="testing"/> <env name="BCRYPT_ROUNDS" value="4"/> <env name="CACHE_DRIVER" value="array"/> <env name="DB_CONNECTION" value="sqlite"/> <env name="DB_DATABASE" value=":memory:"/> <env name="MAIL_MAILER" value="array"/> <env name="QUEUE_CONNECTION" value="sync"/> <env name="SESSION_DRIVER" value="array"/> <env name="TELESCOPE_ENABLED" value="false"/> </php> MAIL_MAILER=smtp MAIL_HOST=mailhog MAIL_PORT=1025 .env.testing phpunit.xml
  • 20.
    Preparing Database Isolated andrisk free version - for testing environment <php> ... <env name="DB_CONNECTION" value="sqlite"/> <env name="DB_DATABASE" value=":memory:"/> ... </php> phpunit.xml
  • 21.
    Preparing Database Cleanup andprepare for testing new scenario use IlluminateFoundationTestingDatabaseMigrations; use IlluminateFoundationTestingRefreshDatabase; use TestsTestCase; class HomepageTest extends TestCase { use DatabaseMigrations, RefreshDatabase; public function test_something_with_predefined_data() { // Run the DatabaseSeeder... $this->seed(); // Run an array of specific seeders... $this->seed([ AreaListSeeder::class, DocumentCategoriesSeeder::class, ]); } // ... Execute Migrations before running test Cleanup DB after running test Stage a prede fi ned scenario in DB
  • 22.
    Let’s write aFeature Test Demo session
  • 23.
    What can weAssert? Various points to validate application sanity Look into Testing/TestResponse
  • 24.
    Check if expected text isshowing in page $response = $this->actingAs($user)->get('/path'); // Find text in response page $response->assertSee('Dashboard'); $response->assertDontSee('Admin'); // Can be used array of text, also can verify in order $response->assertSee(['A Menu Item', 'Another Menu Item’]); // Also can verify if they are showing in order $response->assertSeeInOrder(['A Menu Item', 'Another Menu Item']); // All of the above has a *Text() alternative // that strip_tags the response before compare $response->assertSeeText('Dashboard');
  • 25.
    What was passed to theview? public function test_it_has_the_correct_value() { $response = $this->get('/some-route'); $this->assertEquals('John Doe', $response->viewData('name')); } public function test_it_contains_a_given_record() { $response = $this->get('/some-route'); $this->assertTrue($response->viewData('users')->contains($userA)); } public function test_it_returns_the_correct_amount_of_records() { $response = $this->get('/some-route'); $this->assertCount(10, $response->viewData('users')); } Code snippet source
  • 26.
    Validating API Responses $response = $this->postJson('/api/user',['name' => 'Sally']); // Validate Response JSON $response ->assertStatus(201) ->assertJson(['created' => true]); ->assertJsonPath('team.owner.name', 'Darian'); // Fluent JSON Testing $response ->assertJson(fn (AssertableJson $json) => $json->where('id', 1) ->where('name', 'Victoria Faith') ->where('email', fn (string $email) => str($email)->is('victoria@gmail.com')) ->whereNot('status', 'pending') ->missing('password') ->etc() ); // Also there are has, hasMany, hasAll, missing, missingAll etc. Code snippet source
  • 27.
    Validating Database records $user = User::factory()->create(); //Check model existence $this->assertModelExists($user); $this->assertModelMissing($user); // SoftDeleted or not $this->assertSoftDeleted($user); $this->assertNotSoftDeleted($user); // Count records $this->assertDatabaseCount('users', 5); // Check specific record $this->assertDatabaseHas('users', [ 'email' => 'sally@example.com', ]);
  • 28.
    Exception Handling // In featuretesting $response = $this->withoutExceptionHandling()->get('/'); // Mention which exception is expected $this->expectException(MyAccessParsingException::class);
  • 29.
    Filtering Test Run speci fi cset of tests php artisan test // Run Specific TestSuite php artisan test --testsuite Unit // Run a single class php artisan test --filter=HomepageTest // Run a single function php artisan test --filter=test_home_responds_with_success_for_Admin
  • 30.
    @ajaxray 🐦 📬{between curlybrackets} 🌎 ajaxray.com