KEMBAR78
Testing in Laravel Framework | PDF
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