Testing Flashcards
By default, your application’s test directory contains two directories, Unit and Feature. what are Unit tests used for?
Unit tests are tests that focus on a very small, isolated portion of your code. In face, most unit tests probably focus on a single method. Tests within your “Unit” test directory do not boot your Laravel application and therefore are unable to access your application’s database or other framework services.
What are Feature tests and what are they used for?
Feature tests may test a larger portion of your code, including how several objects interact with each other or even a full HTTP request to a JSON endpoint. Generally, most of your tests should be feature tests. These types of tests provide the most confidence that your system as a whole is functioning as intended.
When running tests, Laravel will automatically set the configuration environment to testing because of the environment variables. Laravel also automatically configures the session and cache to the array driver so that no session or cache data will be persisted while testing. Where can you define other testing environment configuration values?
The testing environment variables may be configured in your application’s phpunit.xml file, but make sure to clear your configuration cache using the config:clear Artisan command before running your tests!
How can you create a new feature test?
php artisan make:test UserTest
How can you create a new unit test?
php artisan make:test UserTest –unit
Run a test using Artisan!
php artisan test
What do you need to do to split up a test run across multiple processes simultaneously?
First, you should install the brianium/paratest Composer package as a “dev” dependency. Then, include the –parallel option when executing the test Artisan command:
composer require brianium/paratest --dev php artisan test --parallel
By default, Laravel will create as many processes as there are available CPU cores on your machine. However, you may adjust the number of processes using the –processes option:php artisan test --parallel --processes=4
As long as you have configured a primary database connection, Laravel automatically handles creating and migrating a test database for each parallel process that is running your tests. The test databases will be suffixed with a process token which is unique per process. By default, test databases persists between calls to the test Artisan command so that they can be used again by subsequent test invocations. How can you re-create the databases?
You may re-create them using the –recreate-databases option:php artisan test --parallel --recreate-databases
What is Laravel Dusk?
Laravel Dusk provides an expressive, easy-to-use browser automation and testing API. By default, Dusk does not require you to install JDK or Selenium on your local computer. Instead, Dusk uses a standalone ChromeDriver installation. However, you are free to utilize any other Selenium compatible driver you wish.
How do you set up Laravel Dusk?
To get started, you should install Google Chrome and add the laravel/dusk Composer dependency to your project:
composer require laravel/dusk –dev
If you are manually registering Dusk’s service provider, you should NEVER register it in your production environment, as doing so could lead to arbitrary users being able to authenticate with your application.
After installing the Dusk package, execute the dusk:install Artisan command. The dusk:install command will create a tests/Browser directory, an example Dusk test and install the Chrome Driver binary for your operating system:
php artisan dusk:install
How can you create a Dusk test?
Use the dusk:make Artisan command. The generated test will be placed in the tests/Browser directory:php artisan dusk:make LoginTest
Most of the tests you write will interact with pages that retrieve data from your application’s database; however, your Dusk tests should never use the RefreshDatabase trait. The RefreshDatabase trait leverages database transactions which will not be applicable or available across HTTP requests. What should you use instead?
Instead, you have two options: the DatabaseMigrations trait and the DatabaseTruncation trait.
What does the DatabaseMigrations trait do?
The DatabaseMigrations trait will run your database migrations before each test. However, dropping and re-creating your database tables for each test is typically slower than truncating the tables.
What does the DatabaseTruncation trait do?
The DatabaseTruncation trait will migrate your database on the first test in order to ensure your database tables have been properly created. However, on subsequent tests, the database’s tables will simply be truncated - providing a speed boost over re-running all of your database migrations. By default, this trait will truncate all tables except the migrations table. If you would like to customize the tables that should be truncated, you may define a $tablesToTruncate property on your test class. Alternatively, you may define an $exceptTables property on your test class to specify which tables should be excluded from truncation.
Run a browser test!
php artisan dusk
If you had test failures the last time you ran the dusk command, how can you save time when wanting to run another test?
You may save time by re-running the failing tests first using the dusk:fails command:
php artisan dusk:fails
How can you create a browser instance for your Dusk tests?
To create a browser instance, you may call the browse method from within your Dusk test:
class ExampleTest extends DuskTestCase { // ... $this->browse(function (Browser $browser) use ($user) { $browser->visit('/login') ->type('email', $user->email) ->type('password', 'password') ->press('Login') ->assertPathIs('/home'); // ... }
How can you create multiple browser instances if needed?
Just pass multiple browsers as arguments and give them whatever attributes you need:
$this->browse(function (Browser $first, Browser $second) { $first->loginAs(User::find(1)) //... $second->loginAs(User::find(2)) // ... $first->waitForText('example') // ... });
For navigation, which method lets you navigate to a given URI within your application?
$browser->visit('/login');
How can you visit a named route?
$browser->visitRoute($routeName, $parameters);
How can you navigate back and forth?
$browser->back(); $browser->forward();
How can you refresh the page?
$browser->refresh();
How can you adjust the size of the browser window?
$browser->resize(1920, 1080);
How can you maximize the window?
$browser->maximize();
How can you resize the browser window to fit the size of its content?
$browser->fitContent();
When a test fails, Dusk will automatically resize the browser to fit the content prior to taking a screenshot. How can you disable this behavior?
$browser->disableFitOnFailure();
How can you move the browser window to a different position on your screen?
$browser->move($x = 100, $y = 100);
Often you will be testing pages that require authentication. How can you avoid interacting with your application’s login screen during every test?
The loginAs method accepts a primary key associated with your authenticatable model or an authenticatable model instance:
$this->browse(function (Browser $browser) { $browser->loginAs(User::find(1)) ->visit('/home'); });
After using the loginAs method, the user session will be maintained for all tests within the file.
How can you get an encrypted cookie value?
$browser->cookie('name');
How can you set an encrypted cookie value?
$browser->cookie('name', 'John');
How can you get an unencrypted cookie value?
$browser->plainCookie('name');
How can you set an unencrypted cookie value?
$browser->plainCookie('name', 'John');
How can you delete a cookie?
$browser->deleteCookie('name');
Which method takes a screenshot of the current browser?
$browser->screenshot('filename');
What do you need to do to take screenshots at various breakpoints?
$browser->responsiveScreenshots('filename');
How can you take a screenshot of a specific element on the page?
$browser->screenshotElement('#selector', 'filename');
Which method lets you store the console log to a file?
$browser->storeConsoleLog('filename');
What do you need to do to store the page source to a file?
$browser->storeSource('filename');
How should you use Dusk selectors when interacting with elements?
First, you should add a dusk attribute to your HTML element. Then, when interacting with a Dusk browser, prefix the selector with @ to manipulate the attached element within your test:
// HTML... <button dusk="login-button">Login</button> // Test... $browser->click('@login-button');
How can you retrieve and set values using Dusk?
// Retrieve the value... $value = $browser->value('selector'); // Set the value... $browser->value('selector', 'value');
How can you retrieve the value of an input element with a given field name?
$value = $browser->inputValue('field');
How can you retrieve the display text of an element that matches the given selector?
$text = $browser->text('selector');
Which method lets you retrieve the value of an attribute?
$attribute = $browser->attribute('selector', 'value');
How can you make Dusk enter something into a form / input element?
$browser->type('email', 'john@example.com');
How can you enter text into a field without clearing its already existing content?
$browser->type('email', 'john@example') ->append('email', '.com');
How can you clear out the current input of an input field?
$browser->clear('email');
How can you make Dusk type at a certain speed?
$browser->typeSlowly('mobile', '+1 234 567 890', 300)
This would now have a 300ms delay between each character being input, if you don’t provide the third argument Laravel will default to 100ms
You may also use the ->appendSlowly method taking the same optional time argument, that typeSlowly takes.
How can you select a specific element of a dropdown menu?
$browser->select('size', 'Large');
Like the type method, the select method does not require a full CSS selector. When passing a value to the select method, you should pass the underlying option value instead of the display text.
By providing an array as the second argument to the select method, you can instruct the method to select multiple options.
How can you select a random element of a dropdown menu?
$browser->select('size');
How can you “check” a checkbox input?
$browser->check('terms');
How can you “uncheck” a checkbox input?
$browser->uncheck('terms');
How can you “select” a radio input option?
$browser->radio('size', 'large');
How can you attach files to an input element?
The attach method may be used to attach a file to a file input element. Like many other input related methods, a full CSS selector is not required. If a CSS selector match can’t be found, Dusk will search for a file input with a matching name attribute:$browser->attach('photo', \_\_DIR\_\_.'/photos/mountains.png');
The attach function requires the Zip PHP extension to be installed and enabled on your server.
When submitting forms, many applications disable the form’s submission button after it is pressed and then re-enable the button when the form submission’s HTTP request is complete. Which method makes Dusk press a button and wait for the button to be re-enabled?
// Press the button and wait a maximum of 5 seconds for it to be enabled... $browser->pressAndWaitFor('Save'); // Press the button and wait a maximum of 1 second for it to be enabled... $browser->pressAndWaitFor('Save', 1);
How can you click a link?
$browser->(clickLink($linkTest);
How can you provide more complex input sequences than just simple text?
The keys method allows you to provide more complex input sequences to a given element than normally allowed by the type method. For example, you may instruct Dusk to hold modifier keys while entering values. In this example, the shift key will be held while “taylor” is entered into the element matching the given selector. After taylor is typed, swift will be typed without any modifier keys:$browser->keys('selector', ['{shift}', 'taylor'], 'swift');
Another valuable use case for the keys method is sending a “keyboard shortcut” combination to the primary CSS selector for your application:$browser->keys('.app', ['{command}', 'j']);
How can you provide even more complex input sequences than the keys method?
Dusk also provides a withKeyboard method, allowing you to fluently perform complex keyboard interactions via the Laravel\Dusk\Keyboard class. The Keyboard class provides press, release, type and pause methods:
$browser->withKeyboard(function (Keyboard $keyboard) { $keyboard->press('c') ->pause(1000) ->release('c') ->type(['c', 'e', 'o']); });
How can you make Dusk click on an element matching the given CSS or Dusk selector?
$browser->click('.selector');
How can you make Dusk click on specific coordinates?
$browser->clickAtPoint($x = 0, $y = 0);
How can you double click on an element?
$browser->doubleClick('.selector');
How can you right click an element?
$browser->rightClick('.selector');
How can you click on a button, hold it for a specific amount of time and then release it?
$browser->clickandHold('.selector') ->pause(1000) ->releaseMouse();
How can you click an element while holding CTRL?
$browser->controlClick('.selector');
How can you hover your move over an element?
$browser->mouseover('.selector');
How can you drag and drop an element to another element?
$browser->drag('.from-selector', '.to-selector');
How can you drag and drop an element a certain amount of pixels into a direction?
$browser->dragLeft('.selector', $pixels = 10); $browser->dragRight('.selector', $pixels = 10); $browser->dragUp('.selector', $pixels = 10); $browser->dragDown('.selector', $pixels = 10);
How can you drag an element by a given offset?
$browser->dragOffset('.selector', $x = 10, $y = 10);
How can you make an interaction with a JavaScript dialog wait until the dialog actually appears?
$browser->waitForDialog($seconds = null);
Which method let’s you type a value into a JavaScript prompt?
$browser->typeInDialog('Hello World');
How can you click the “OK” or “Cancel” button in a Javascript dialog?
$browser->acceptDialog(); $browser->dismissDialog();
How can you pause the test for x amount of time?
$browser->pause(1000);
If you need to pause the test only if a given condition is true, use the pauseIf method:$browser->pauseIf(App::environment('production'), 1000);
Likewise, if you need to pause the test unless a given condition is true, you may use the pauseUnless method:$browser->pauseUnless(App::environment('testing'), 1000);
What does the waitFor method do?
The waitFor method may be used to pause the execution of the test until the element matching the given CSS or Dusk selector is displayed on the page. By default, this will pause the test for a maximum of five seconds before throwing an exception. If necessary, you may pass a custom timeout threshold as the second argument to the method:
// Wait for a maximum of five seconds for the selector... $browser->waitFor('.selector'); // Wait a maximum of one second for the selector... $browser->waitFor('.selector', 1);
How can model factories be used for testing?
When testing, you may need to insert a few records into your database before executing your test. Instead of manually specifying the value of each column when you create this test data, Laravel allows you to define a set of default attributes for each of your Eloquent models using model factories. Once you hvae defined a model factory, you may utilize the factory within your test to create models.
What is the point of mocking something during testing?
When testing Laravel applications, you may wish to “mock” certain aspects of your application so they are not actually executed during a given test. For example, when testing a controller that dispatches an event, you may wish to mock the event listeners so they are not actually executed during the test. This allows you to only test the controller’s HTTP response without worrying about the execution of the event listeners since the event listeners can be tested in their own test case
How can you turn a class into a mocked instance of itself for testing?
Using the mock method of the Mockery class you can mock a service, for example:
public function test_something_can_be_mocked(): void { $this->instance( Service::class, Mockery::mock(Service::class, function (MockInterface $mock) { $mock->shouldReceive('process')->once(); }) ); }
What is the difference between a spy and a mock?
Spies are similar to mocks; however, spies record any interaction between the spy and the code being tested, allowing you to make assertions after the code is executed
How can you modify the time returned by helpers during testing?
// Travel into the future... $this->travel(5)->milliseconds(); $this->travel(5)->seconds(); $this->travel(5)->minutes(); ... $this->travel(5)->years(); // Travel into the past... $this->travel(-5)->hours(); // Travel to an explicit time... $this->travelTo(now()->subHours(6)); // Return back to the present time... $this->travelBack();
Write a test method that sets a variable $response as the response you would get by accessing “/” as a banned user!
public function test_interacting_with_the_session(): void { $response = $this->withSession(['banned' => true])->get('/'); }
How can you test something as an authenticated user?
Within a test method in a test class:
// You could also use a specific user but for this example I'll just do this... $user = User::factory()->create(); $response = $this->actingAs($user) ->withSession(['banned' => false]) ->get('/');
After making a test request to your application, how can you examine and debug the response contents?
With the dump, dumpHeaders or dumpSession methods. There are also dd varieties of these methods, for example you may do:
$response = $this->get('/'); $response = ddSession();
How can you test the behavior of your application if a specific exception is thrown?
You may fake the exception handler via the Exceptions facade. Once the exception handler has been faked, you may utilize the assertReported and assertNotReported methods to make assertions against expections that were thrown during the request:
public function test_exception_is_thrown(): void { Exceptions::fake(); $response = $this->get('/'); // Assert an exception was thrown... Exceptions::assertReported(InvalidOrderException::class); // Assert against the exception... Exceptions::assertReported(function (InvalidOrderException $e) { return $e_>getMessage() === 'The order was invalid.'; }); }
How can you check if an exception was not thrown or that no exceptions were thrown during a test?
// Check if an exception was not thrown... Exceptions::assertNotReported(InvalidOrderException::class); // Check if no exceptions were thrown... Exceptions::assertNothingReport();
How can you make a request without any handling for exceptions?
$response = $this->withwithoutExceptionHandling()->get('/');
How can you test JSON APIs and their responses?
Laravel provides several helpers for testing JSON APIs and their responses. For example, the json, getJson, postJson, putJson, patchJson, deleteJson and optionsJson methods may be used to issue JSON requests with various HTTP verbs. You may also easily pass data and headers to the methods. To get started, let’s write a test to make a POST request to /api/user and assert that the expected JSON data was returned:
public function test_making_an_api_request(): void { $response = $this->postJson('/api/user', ['name' => 'Sally']); $response->assertStatus(201) ->assertJson(['created => true,]); }
The assertJson method converts the response to an array to verify that the given array exists within the JSON response returned by the application. So, if there are other properties in the JSON respose, this test will still pass as long as the given fragment is present.
The assertJson method may be used to assert that a fragment of JSON exists within the JSON response. How can you verify an exact match of an array and a JSON response?
If you would like to verify that a given array exactly matches the JSON returned by your application, you should use the assertExactJson method:$response->assertStatus(201)->assertExactJson(['created' => true,]);
How can you fluently test your application’s JSON responses?
To get started, pass a closure to the assertJson method. This closure will be invoked with an instance of Illuminate\Testing\Fluent\AssertableJson which can be used to make assertions against the JSON that was returned by your application. The where method may be used to make assertions against a particular attribute of the JSON, while the missing method may be used to assert that a particular attribute is missing from the JSON:
$response = $this->getJson('/users/1'); $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() );
What does the etc() method do when testing a JSON API fluently?
This method informs Laravel that there may be other attributes present on the JSON object. If the etc method is not used, the test will fail if other attributes that you did not make assertions against exist on the JSON object.
The intention behind this behavior is to protect you from unintentionally exposing sensitive information in your JSON responses by forcing you to either explicitly make an assertion against the attribute or explicitly allow additional attributes via the etc method. However, you should be aware that not including the etc method in your assertion chain does that ensure that additional attributes are not being added to arrays that are nested within your JSON OBJECT. The etc method only ensures that no additional attributes exist at the nesting level in which the etc method is invoked.
How can you assert if an attribute is present or absent in a JSON during a test?
By using the has and missing methods:
$response->assertJson(fn (AssertableJson $json) => $json->has('data') ->missing('message') );
If you want to test for multiple attributes being present or missing you may use the hasAll and missingAll methods and pass them an array or if you want to check if one of multiple attributes is present you can use the hasAny method which you do not pass an array to and instead just list the attributes divided by a comma
How can you limit the type that properties in a JSON response should be of to assert certain properties?
The Illuminate\Testing\Fluent\AssertableJson class provides the whereType and whereAllType methods for doing just that:
$response->assertJson(fn (AssertableJson $json) => $json->whereType('id', 'integer') ->whereAllType([ 'users.0.name' => 'string', 'meta' => 'array' ]) );
You may also specify multiple types using the | character, or passing an array of types as the second parameter to the whereType method. The assertion will be successful if the response value is any of the listed types.
How can you generate dummy files or images for testing with the UploadedFile method?
The Illuminate\Http\UploadedFile class provides a fake method which may be used to generate dummy files or images for testing. This, combined with the Storage facade’s fake method, greatly simplifies the testing of file uploads. For example, you may combine these two features to easily test an avatar upload form:
public function test_avatars_can_be_uploaded(): void { Storage::fake('avatars'); $file = UploadedFile::fake()->image('avatar.jpg'); $response = $this->post('/avatar', [ 'avatar' => $file, ]); Storage::disk('avatars')_>assertExists(file->hashName()); }
For testing your application’s validation rules, how can you set a custom width or height to a fake image by the UploadedFile’s fake method and customize the file size?
When creating files using the fake method provided by the UploadedFile class, you may specify the width, height and size of the image (in kilobytes) in order to better test your application’s validation rules:
UploadedFile::fake()->image('avatar.jpg', $width, $height)->size(100);
How can you fake a non-image file using the fake method provided by the UploadedFile class?
You may create files of any other type using the create method:
UploadedFile::fake()->create('document.pdf', $sizeInKilobytes);
Laravel allows you to render a view without making a simulated HTTP request to the pplication. How can you accomplish this?
To accomplish this, you may call the view method within your test. The view method accepts the view name and an optional array of data. The method returns an instance of the Illuminate\Testing\TestView, which offers several methods to conveniently make assertions about the view’s contents:
public function test_a_welcome_view_can_be_rendered(): void { $view = $this->view('welcome', ['name' => 'Taylor']); $view->assertSee('Taylor'); }
The TestView class provides the following assertion methods:
assertSee, assertSeeInOrder, assertSeeTest, assertSeeTextInOrder, assertDontSee, and assertDontSeeText.
How can you evaluate and render a raw Blade string?
Like the view method, the blade method returns an instance of Illuminate\Testing\TestView:
$view = $this->blade( '<x-component :name="$name" />', ['name' => 'Taylor'] ); $view-> assertSee('Taylor');
You may use the component method to evaluate and render a Blade component. The component method returns an instance of Illuminate\Testing\TestComponent:
$view = $this->component(Profile::class, ['name' => 'Taylor']); $view->assertSee('Taylor');