Lessons Learnt: Confident Laravel
Published: 2020-07-09 19:41:15

---
title: Lessons Learnt: Confident Laravel
published: true
date: 2020-06-08 00:00:00 UTC
tags: 
canonical_url: https://martinbetz.eu/articles/lessons-learnt-confident-laravel
---

I took the course [Confident Laravel](https://confidentlaravel.com/) when I was on vacation. It is suitable for total testing beginners.

Here are my takeaways in the format of question and answer. They are sorted by occurence in the course but you can use whatever you need for your application and testing – not everything is related to testing:

## How to create a basic PHPUnit test (= extend test case)?

```
class PHPUnit extends TestCase
{
  public function testAssertions() 
  {
    $this->assertTrue(true);
  }
}
```

## What are the testing definitions?

- What is `$subject` in our tests??
  - The thing we want to test
- What is `$expected`?
  - The result we expect!
- What is `$actual`?
  - Result we actually get
- What is the difference between `$expected` and `$actual`?
  - `$expected` is hardcoded, you just enter a number, a string, a result
  - `$actual` is a method that returns the actual result

## What is the order of loading test settings

1. `.env`
2. `.env.testing`
3. `phpunit.xml`
4. system environment variables

## How to make stuff available to all tests?

Add it to `tests/TestCase.php`

## When and why should I use `PHPUnit\Framework\TestCase` directly?

- When Laravel tests features are not needed
- May boost speed when 1000s of tests

## How to structure tests in the `feature` folder?

Just mimic the `app` folder

## How to name Dusk (= BDD) tests?

- Give it the name of the feature you test, i.e. `GithubLinksTest`
- No need to mimic the folder structure of your app

## How to customize Dusk's settings?

- Create `.env.dusk.local`
- Make your Dusk specific settings

## How to generate tests with subfolders?

- `php artisan make:test Http/Controllers/HomeControllerTest`
- No leading `app`
- No trailing `.php`

## How to name factories?

- `ModelnameFactory`
- e.g. `UserFactory` (`php artisan make:factory UserFactory`)

```
public function() {
  return $this;

}
```

## How to filter `php artisan route:list` for entries containing `invoice`?

- `php artisan route:list --name invoice`

## How to test whether the right view gets loaded?

- `$this->assertViewIs('invoice.create')`

## What is the _happy path_?

- Testing when the subject is working as expected

## How to check in controller if column `expired` with timestamp is not set?

- `Model::whereNull('expired')->get()`

## How to put sth like a coupon in the session cookie?

- `$request->session()->put('name', 'value')`

## How to name general controller tests?

- `it_does_this_and_that()`
- e.g. `it_stores_coupon_and_redirects()`

## How to check whether data exists in a session?

- `$response->assertSessionHas('key', 'value');`

## How to get the full error message in Laravel tests?

- `$this->withoutExceptionHandling();` in test method

## How many sad paths should you test?

- Enough to feel confident
- There are potentially unlimited sad paths

## How to assert that sth is not in a session?

- `$this->assertSessionMissing('key', 'value');`

## What's the running order in a test?

- Arrange
- `$response = $this->get(route('name'));`
- `$response->assert…();`

## How to mark test incomplete so it gets skipped when running the test suite?

- `$this->markTestIncomplete();`

## How to test that user is logged in (and not redirected)?

- `$this->assertAuthenticatedAs($user);`
- Do not assert on `$response` but on global state `$this`

## How to assert that a view has a variable `now_playing` with value `1`?

- `$response->assertViewHas('now_playing', 1);`

## How to fake a heading or title?

- `$faker->sentence` or `$faker->word(3, true)`
- Use `true` as 2nd argument in `$faker->word` to get words, otherwise you get an array

## How to fake an ID?

- `$faker->md5`

## How to assert the model data you already have in your test?

- Goal: Refresh user record, because it go changed
- Not using: `$this->assertDatabaseHas()`
- `$user->refresh();`
- Assert properties of model
- Example: `$this->assertEquals(1, $user->last_viewed_vidoe_id)`

## How to add `$faker` to your test?

- Add `use WithFaker;` trait to your test class

## How to create a password hash?

- `Hash::make('STRING')`

## How to assert two hashes?

- NOT: `$this->assertEquals(Hash::make($password, $user->password);`
- BUT: `$this->assertTrue(Hash::check($password, $user->password);`

## What is the difference between asserting `Hash::check()` and hardcoding a password value?

- `Hash:check`: tie test to the implementation of the HASH function
- Hardcoding: Setting a sample value is more flexible but also more error prone

## How to check if session contains error 'name'?

- `$response->assertSessionHasErrors('name');`

## Why do form requests not auto redirect back and how to work around that?

- They send referrer URL
- These are fresh with every request => no history available
- Instead: Set the referrer URL manually in the setup
- `$this->from(route('user.edit'))`

## How to avoid writing dozens of assertions in form validations?

- Use Form Requests
- Assert that Form Request gets used by the controller
- Use [Jason McCreary's Laravel Test Asserts](#) for that

```
<?php

// In test method
$this->assertActionUsesFormRequest(
  UsersController::class, // Controller
  'update', // Method
  UserUpdateRequest::class); // Form Request
);
```

## How to find incomplete tests?

- `phpunit -v`

## How to directly assert non-auth cannot access?

- `$this->assertForbidden();`

## How to manually set response 'forbidden' on controller?

- `abort(403)`

## How to get `Product::STARTER`?

- This is a constant in the product model

## What is a mock?

- Stand-in replacement for real object
- Emulates `echo` behaviour

```
// What is the simplest mock you can create?
class ExampleTest extends \PHPUnit\Framework\TestCase
{
  public function testMocking()
  {
    $mock = \Mockery::mock();
    $mock->shouldReceive('foo') // method name
      ->with('bar') // arg
      ->andReturn('baz'); // arg
    $this->assertEquals('baz', $mock->foo('bar'));
  }
}
```

## What are mocks great for?

- Testing integratin with 3rd party libraries without calling them
- Control testing in complex situations, because you break down problems in small chunks

## When will mocks fail?

- If not set
- If it does not have an expectation

## What is a spy?

- It secretly captures behavior
- Returns null if not setup at all
- More forgiving than mock

## What's the easiest spy you can write?

```
public function testSpying()
{
  $spy = Mockery::spy();
  $this->assertNull($spy->quix());

  // assert behavior
  $spy->shouldHaveReceived('quix')->once();
}
```

## What are test doubles?

- Replace production object for testing purposes

## Why are facades easy to test in Laravel?

- Every facade has mocks & spies

## How to return nothing (do nothing after a request)?

`return response(null, 204)`

## How to mock Laravel's `Log` facade?

```
$mock = \Mockery::mock();
$mock->shouldReceive('info') // method name
  ->once()
  ->with('video.watched', [$video->id]);
Log::swap($mock);
```

## What is the difficulty with shared facades?

- Facades like `Event` or `Log` get used all over Laravel
- If you swap them in your test, they get swapped everywhere
- This may cause trouble
- You can use `Event::fake()` instead of `Event::swap()`
- Example: To check if event was fired: `$event->assertDispatched();`

## What to do instead of mocks?

- When you can't fake or don't want to create dozens of mock classes
- Wrape it and decouple from tests

## What happens in this mock?

- The real `Log::` class gets swapped by the mock
- The mock tells what should happen as a result
- As there is an expectation (= log vars), you need no extra assertion

## Why do I need the `Log::` facade at all?

- It's the original code
- It would go untested otherwise

## Why do you use `->once()` in a mock?

- Otherwise the test will pass if the double in the real code is not called at all
- i.e. only with `->once()`, you can make sure that the facade got used in the real code

## What is a shortcut for `$mock->shouldReceive('info')->once()`?

- `$mock->expects('info');` // method name

## How to create a setup method that runs for all tests in a file?

```
protected function setUp(): void
{
  parent::setUp();
  $this->subject = new UserUpdateRequest();
}
```

Use it with `$this->subject` in the tests

## How to run a validation in an unit test?

```
$subject = new UserUpdateRequest();
// will call the file App\Http\Requests\UserUpdateRequest;
```

## How to create a coverage report

- You need xdebug
- You can use a docker image with xdebug
- Run `phpunit --coverage-html report`
- Spin up a PHP server to view reports with `php -S 0:8080 -t report/`