Pest-Driven Development: Literally The First Test
Tags: laravel, pest, opensource, testing
Published: 2023-09-08 03:12:18

_I'm blogging along with the laracasts series [Pest Driven Laravel](https://laracasts.com/series/pest-driven-laravel) created by [Christoph Rumpel](https://twitter.com/christophrumpel) as I work on a project I hope to open source. I'm just documenting how I'm working, the resources I'm using, the problems I run into and some cool things I'm reminded of as I develop._ 

The next step is to actually install pest. If I was truly practicing TDD, I would have done this when I started my laravel application because [Test-Driven Development is a lifestyle with a history and a community](https://www.agilealliance.org/glossary/tdd#q=~(infinite~false~filters~(postType~(~'page~'post~'aa_book~'aa_event_session~'aa_experience_report~'aa_glossary~'aa_research_paper~'aa_video)~tags~(~'tdd))~searchTerm~'~sort~false~sortDirection~'asc~page~1))  I may as well take a moment to put my hand over my heart and remember out loud that I'm [uncovering better ways of developing software](https://agilemanifesto.org/).  But that's okay, because pest works 'on top of' an existing repository, and when you install it a folder of tests is created.

```sh
> composer require pestphp/pest --dev --with-all-dependencies
> php artisan pest:install
> composer require pestphp/pest-plugin-laravel --dev
```

Protip for my fellow beginners and tired people: definitely run these commands _inside_ the directory that contains the `app` folder; it's not a global install. I also [forgot to add the pest plugin and curly braces](https://laracasts.com/discuss/channels/testing/no-definition-found-use-function-pestlaravelget) which threw me for an hour or so, so do remember that last line. We'll need it, and vs code is different than phpstorm. 

Also, it's my personal opinion that when your terminal asks you to give a star to [a repository on github that's going to save you hours of toil and heartache](https://github.com/pestphp/pest), actually taking the time to do it is the same thing as unlocking an achievement in real life. And then you can go back someday and see all those stars and realize how connected you are to all these people who are a lot like you and how much you learned from their hard work. But again, I digress.

Then you can open your `/tests/Feature/pest.php` file and check out your new pest file, which looks like this on my fresh install:

```php
<?php

namespace Tests\Unit;

use PHPUnit\Framework\TestCase;

class ExampleTest extends TestCase
{
    /**
     * A basic test example.
     */
    public function test_that_true_is_true(): void
    {
        $this->assertTrue(true);
    }
}
```


But the very first test in the course confirms that a request to a route returns a status code of 200. We'll take out all of the code in the file and replace it with

```php
<?php

it('gives back successful response for the home page', function () {

    $response = $this->get('/');

    $response->assertStatus(200);
});

```

So what's happening here? I think it's a fair time to stop and think about this question, especially because the code contains `it` and `this`. 

### Let's just talk about `it` for a minute.

From passing knowledge, to me `it` indicates [Keyword-driven testing](https://en.wikipedia.org/wiki/Keyword-driven_testing), which "separates the documentation of test cases – including both the data and functionality to use – from the prescription of the way the test cases are executed". Pest isn't the only option for testing, even in laravel, but I'm choosing it because its 'claim to fame' is allowing a developer to use less code to write tests and allow that code to be more readable. So it makes sense that people new to testing (me) are going to run into some unfamiliar keywords. The apperance of `it` combined with the extremely human-readable description as the first function parameter gave me pause. So I looked [`it` up the pest source code](https://github.com/pestphp/pest/blob/2b0aa4b9c93138c033a6da27cbbd3bd9bbd2d218/src/Functions.php#L139), and got

```php
if (! function_exists('it')) {
    /**
     * Adds the given closure as a test. The first argument
     * is the test description; the second argument is
     * a closure that contains the test expectations.
     *
     * @return Expectable|TestCall|TestCase|mixed
     */
    function it(string $description, Closure $closure = null): TestCall
    {
        $description = sprintf('it %s', $description);

        /** @var TestCall $test */
        $test = test($description, $closure);

        return $test;
    }
}
```

It looks like `it` takes the description, adds it to the string 'it' and formats it to use it as a label for the callback we're passing in the second parameter. Then, when pest actually calls `test()` we have the super readable label and the function. `test()` has the same method description as `it`, but now our description is formatted for later and we can see things starting to really move inside the `test()` function (despite the syntax of the two function calls looking similar):

```php
if (! function_exists('test')) {
    /**
     * Adds the given closure as a test. The first argument
     * is the test description; the second argument is
     * a closure that contains the test expectations.
     *
     * @return Expectable|TestCall|TestCase|mixed
     */
    function test(string $description = null, Closure $closure = null): HigherOrderTapProxy|TestCall
    {
        if ($description === null && TestSuite::getInstance()->test instanceof \PHPUnit\Framework\TestCase) {
            return new HigherOrderTapProxy(TestSuite::getInstance()->test);
        }

        $filename = Backtrace::testFile();

        return new TestCall(TestSuite::getInstance(), $filename, $description, $closure);
    }
}
```

`it` and `test` mean so much in the context of testing they mean almost nothing, so I think this bit of new context is enough to give a little more meaning to what I'm reading.

### What even is `this`?
I want to take a minute to dive into `this`, but I won't. Without fail, when I'm in a pairing situation and I ask a php dev what `this` is, I'm told we'll 'get to it later' and then we write actual code and that's probably a good thing. I know that `this` refers to the calling object, and there's always a moment when I'm reading or writing code in laravel as a beginner where I guess at what `this` is in a new context. This is no exception. I'm definitely going to come back to it in a later post, but if someone wants to have mercy and just tell me what I am calling `get` on (is it the whole app??) that would be great.

### Some More Setup
Before I stepped back, I took a minute to create a pest alias. By default, you can run a test with `./vendor/bin/pest` but I changed it to `pest`. If you're reading this and you haven't already-- don't forget to alias `sudo` to `please`. 

Then I hopped into the `phpunit.xml` to uncomment:

```xml
<env name="DB_CONNECTION" value="sqlite"/>
<env name="DB_DATABASE" value=":memory:"/>
```

which results in a super lightweight and separate testing database. And that reminded me of [this one episode of No Compromises that I heard while driving to Nashville](https://show.nocompromises.io/episodes/lots-of-different-ways-to-test-record-creation). Super hype for my first testing database.

### Getting Pesty

The next part is to refactor the test code to make it more read more pest-y. First, we'll 
1. change `$response = $this->get('/');` to `get('/');`. That requires `use Pest\Laravel\get`, but it looks much nicer.
2. specify a named route using the `route()` helper instead of passing the string `/`. Now we have `get(route('home'))`. 
3. remove `$response->assertStatus(200);` and call `assertStatus(200)` passing the 200 OK status code as a parameter. Now the entire file looks like:

```php
<?php

use function Pest\Laravel\{get};

it('gives back successful response for the home page', function () {

    get(route('home'))->assertStatus(200);
});
```
Another tangent: I listened to [APIs, with Jess Archer](https://podcasts.apple.com/us/podcast/the-laravel-podcast/id653204183?i=1000503209050) on [The Laravel Podcast](https://laravelpodcast.com/) today and there's a ton of useful information about returning HTTP status codes and why being specific about them is useful, and I'm really looking forward to seeing what can be done with that and pest.

Since I'm using jetstream, I saw a lot of output. They're listed by path alphabetically. But check it out: it *does* gives back successful response for the home page. Feels good, man. 

![A screenshot of pest output with passing tests and warnings listed](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gs0wx8nfaqdy6pjm3eiv.png)