Testing: How to actually write tests?


Why Write Tests?

In the early period of developers’ careers or in companies where there are no proper workflows, many of us got away (many still do) with not writing tests. We resort to testing our code manually, i.e., opening the page, filling out the form, and checking for potential problems.

So every time we make any changes or add new features to our app, there’s always anxiety on release day, hoping and praying that it doesn’t break.

The more features an app has, the more hectic it becomes to maintain it.

The best way to get rid of this anxiety for once and all is to write tests.

But how do I start?

Have you spent hours watching your favorite YouTube teacher take you through each step from the installation process of Laravel and PHPUnit to configuring the database and showing how to write your first test, but at the end you still find yourself not able to write any tests on your own?

Well, in this article, I’ll give you the exact blueprint that will not only help you write your first test, but also help you write tests rapidly and efficiently.

So before I give you the blueprint, let’s make some assumptions. First, you have a basic knowledge of Laravel, MVC, and CRUD. Secondly, I’m assuming you have a basic knowledge of PHPUnit and how to install it. If you don’t, I’ll recommend you check out these two amazing series on testing in Laravel by Jeffrey Way: Testing Laravel and Testing Jargon. Third, you have already setup everything you need to start writing tests. So you should get green if you run:

php artisan test

So let’s get started.

The Blueprint

So we know that every controller has at least one of these methods: index, show, create, store, edit, update, delete. We write our tests based on these actions. Let’s take a look at the blueprint for writing tests for the index and show methods:

public function test_it_shows_all_the_posts()
{
    // Given in our database, we have 5 posts
    // When we visit the posts page
    // Then we should see a paginated list of all the posts
}

public function test_it_shows_a_single_post()
{
    // Given in our database, we have a post with the title "This is a test post"
    // When we visit the post page
    // Then we should see the post with the title "This is a test post"
}

What you see above is called the Given-When-Then pattern or Arrange, Act, Assert. This allows you to structure your tests. It also makes it easier for you to read and understand your tests.

Down To The Business

Now let’s actually write the test. We’ll start by creating a new test class. We’ll call it PostControllerTest.php inside the tests/Features folder. We’ll also create a new method inside the class called test_it_shows_all_the_posts and this method represents the index method in our controller.

You can also use the artisan command to create the test class for you. Just run php artisan make:test PostControllerTest.

Remove the test_example method and replace it with the test_it_shows_all_the_posts method.

<?php

namespace Tests\Feature;

use \Illuminate\Foundation\Testing\RefreshDatabase;
use \Tests\TestCase;

class PostControllerTest extends TestCase
{
    use RefreshDatabase;

    public function test_it_shows_all_the_posts()
    {
        $posts = \App\Models\Post::factory()->count(5)->create();

        $this->get('/posts')
            ->assertStatus(200)
            ->assertSee('Posts')
            ->assertSee($posts[0]->title)
            ->assertSee($posts[1]->title)
            ->assertSee($posts[2]->title)
            ->assertSee($posts[3]->title)
            ->assertSee($posts[4]->title);
    }
}

To run the test, we’ll run the command php artisan test and we should see something like this:

img.png

In TDD, we use a term called “Red-Green-Refactor”. This means that we write a test that fails, then we write the code. So now we’re going to write the code that will make the test pass.

From the error message, we can see that the test is failing because the model Post doesn’t exist. So let’s create it. We’ll create a new model called Post. We’ll also create the migration, seeder, factory, policy, resource controller, and form request classes for the Post model. And the best way to do this is by running the command php artisan make:model Post -a.

Though we won’t be using the policy so well remove app/Policies/PostPolicy.php.

Before we move on, let’s add some columns to the posts table via the migration file. Open the database/migrations/NNNN_NN_NN_NNNNNN_create_posts_table and replace the up method with the following:

    public function up()
    {
        Schema::create('posts', function (Blueprint $table) {
            $table->id();
            $table->string('title');
            $table->string('slug')->unique();
            $table->longText('content');
            $table->string('image')->nullable();
            $table->string('status')->default('Draft');
            $table->timestamps();
            $table->softDeletes();
        });
    }

Now, let’s take a look at the PostFactory.php file. We’ll replace the definition method with the following:

    public function definition()
    {
        return [
            'title' => $this->faker->sentence,
            'slug' => $this->faker->slug,
            'content' => $this->faker->paragraph,
            'image' => $this->faker->imageUrl(1920,720, 'cats', true),
            'status' => $this->faker->randomElement([
                'Draft', 'Publish', 'Unlisted',
            ]),
        ];
    }

Now let’s run the test again. We should see something like this.

img_1.png

So at line 22, we are expecting to see the page with an unordered list five posts, but we’re getting 404 because we haven’t created the route for the posts yet.

Now we’re going to write the code that will make the test pass. We’ll edit our routes/web.php file and add the following:

\Illuminate\Support\Facades\Route::resource('posts', \App\Http\Controllers\PostController::class);

now run php artisan test again and you get this:

img_2.png

If you notice, in the previous screenshot the error is at line number 22, but this time it is at 23, so our previous test has passed. Now let’s see what’s going on at line 23?

On line 23, we’re making sure we see the text Posts when we open the page, but get nothing. Since, when we call /posts, it calls the index action of our PostController controller class, and we haven’t touched the controller, yet after we generated our model.

Here’s how our controller looks like as generated by artisan command:

<?php

namespace App\Http\Controllers;

use \AppHttpRequests\StorePostRequest;
use \App\Http\Requests\UpdatePostRequest;
use \App\Models\Post;

class PostController extends Controller
{
    public function index()
    {
        //
    }

    public function create()
    {
        //
    }

    public function store(StorePostRequest $request)
    {
        //
    }

    public function show(Post $post)
    {
        //
    }

    public function edit(Post $post)
    {
        //
    }

    public function update(UpdatePostRequest $request, Post $post)
    {
        //
    }

    public function destroy(Post $post)
    {
        //
    }
}

Let’s focus on the index method. Right now we can see that it’s doing nothing. That’s why the response from the test was returning an empty string:

    public function index()
    {
        //
    }

We know what when the posts page is loaded it should display a list of posts. So here’s how we do it:

    public function index()
    {
        $posts = \App\Models\Post::paginate();

        return view('posts.index', compact('posts'));
    }

Now let’s run the test again. We should see something like this:

img_3.png
img_4.png

So we’re getting an error because we don’t have a view for the posts.index page. So let’s create it. We’ll create a new file called index.blade.php in the resources/views/posts directory. And we’ll add the following code:

@extends('layouts.app')

@section('content')
    <div class="p-8">
        <h1 class="text-6xl my-6">Posts</h1>
        <ul>
            @foreach($posts as $post)
                <li class="my-5">
                    <a class="text-3xl hover:underline hover:text-blue-500"
                       href="{{route('posts.show', $post->id)}}">{{ $post->title }}</a>
                </li>
            @endforeach
        </ul>
    </div>
@endsection

Also, create a new file called app.blade.php in the resources/views/layouts directory. And add the following code:

<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <title>Laravel</title>
    <script src="https://cdn.tailwindcss.com"></script>

    <!-- Fonts -->
    <link href="https://fonts.bunny.net/css2?family=Nunito:[email protected];600;700&display=swap" rel="stylesheet">
    <style>
        body {
            font-family: 'Nunito', sans-serif;
        }
    </style>
</head>
<body class="antialiased">
<div class="md:w-9/12 w-full mx-auto bg-blue-200">
    <main>
        <div>
            <h1 class="text-7xl text-center py-6"><a href="/posts">My Blog</a></h1>
        </div>
        <div>
            @yield('content')
        </div>
    </main>
</div>
</body>
</html>

Now let’s run the test again. We should see something like this:

img_5.png

And we have a passing test. Now let’s add a test for the show action. We’ll add the following code to the PostTest.php file:

    public function test_it_shows_a_single_post()
    {
        $post = \App\Models\Post::factory()
            ->create(['title' => 'This is a test post']);

        $response = $this->get(route('posts.show', $post->id));

        $response->assertStatus(200);
        $response->assertSee($post->title);
    }

Run the test again. We should see something like this:

img_6.png

So we’re getting an error because we don’t have a view for the posts.show page. So let’s create it. We’ll create a file called show.blade.php in the resources/views/posts directory. And we’ll add the following code:

@extends('layouts.app')

@section('content')
    <div class="p-8">
        <div class="text-center">
            <p>{{$post->created_at->format('M d, Y')}}</p>
            <h1 class="text-4xl mb-6">{{$post->title}}</h1>
        </div>

        <img src="{{$post->image}}" alt="{{$post->title}}" class="w-full"/>

        <div class="my-5">
            {{$post->content}}
        </div>
    </div>
@endsection

and edit the PostController.php file and add the following code:

    public function show(Post $post)
    {
        return view('posts.show', compact('post'));
    }

Now let’s run the test again. We should see something like this:

img_7.png

And we have another passing test!

Hopefully, you now have an idea of how to write tests for your Laravel application.

Now it’s your turn to write some tests for your application.

Soure Code

Here’s the GitHub repo for the project:

https://github.com/JunaidQadirB/how-to-actually-write-tests


One response to “Testing: How to actually write tests?

  1. Publishing Your First Composer Package - Junaid Qadir Avatar

    Publishing your first composer package is pretty easy. In this article, we’ll go step-by-step from creating the package to publishing it on Packagist, to installing it in one of your projects.

    What is Composer?

    Composer is the de facto package manager for PHP. It allows users to install and manage dependencies in their PHP-based projects.

    According to the official website, Composer is a tool for dependency management in PHP. It allows you to declare the libraries your project depends on, and it will manage (install/update) them for you.

    Prerequisites

    PHP 5.3.2 or above

    Composer installed on your machine

    A GitHub account

    A Packagist account

    Assuming you have PHP installed on your machine, you can install Composer by running the following command in your terminal:

    curl -sS https://getcomposer.org/installer | php

    Or follow the instructions on the official website.

    Run the following command to check if Composer is installed successfully:

    composer --version

    Composer version 2.5-dev+6c85b875f27185b0e36c35aac98af634be25bdc0 (2.5-dev) 2022-11-03 20:53:42

    Alternatively, you can run composer like this:

    php composer.phar --version

    Composer version 2.5-dev+6c85b875f27185b0e36c35aac98af634be25bdc0 (2.5-dev) 2022-11-03 20:53:42

    Creating a Package

    To create a package, you need to create a directory for your package. In this case, we will create a directory called hello-world and change into it:

    mkdir hello-world
    cd hello-world

    Every package must have a composer.json file. This file contains information about your package, such as the name, version, and dependencies. You can create this file by running the following command and following the prompts:

    composer init

    But for this tutorial, we will run the following command to create the composer.json file:

    composer init -q -a src --name my-name/hello-world --description "A simple hello world package" --license MIT

    Then run composer u to install the dependencies.

    We get the following directory structure with two files and two directories:

    .
    ├── composer.json
    ├── composer.lock
    ├── src
    └── vendor
    ├── autoload.php
    └── composer
    ├── autoload_classmap.php
    ├── autoload_namespaces.php
    ├── autoload_psr4.php
    ├── autoload_real.php
    ├── autoload_static.php
    ├── ClassLoader.php
    ├── installed.json
    ├── installed.php
    ├── InstalledVersions.php
    └── LICENSE

    Vendor Directory

    The vendor directory contains all the dependencies of your package. It is automatically generated when you run composer install or composer update.

    src Directory

    The src directory contains the source code of your package. It is automatically generated when you run composer init.

    composer.json File

    As explained earlier the composer.json file contains information about your package, such as the name, version, and dependencies. It is automatically generated when you run composer init.

    composer.lock File

    The composer.lock file contains information about the dependencies of your package. It is automatically generated when you run composer install or composer update.

    Testing the Package

    It is always best to test your package before publishing it. To test the package, we will install phpunit. PhpUnit is a unit testing framework for PHP. It is used to test the functionality of your package.

    Read my article on writing tests.

    Run the following command to install phpunit:

    composer require --dev phpunit/phpunit

    We need to configure phpunit to run our tests. To do this, we will create a phpunit.xml file in the root directory of our package. Add the following code to the file:

    <?xml version="1.0" encoding="UTF-8"?>
    <phpunit bootstrap="vendor/autoload.php" colors="true">
    <testsuites>
    <testsuite name="Test Suite">
    <directory>tests</directory>
    </testsuite>
    </testsuites>
    </phpunit>

    We want to run our tests in the tests directory. So Add the following block to the composer.json:

    "scripts": {
    "test": "phpunit"
    }

    Our composer.json file should look like this:

    {
    "name": "my-name/hello-world",
    "autoload": {
    "psr-4": {
    "MyNameHelloWorld": "src"
    }
    },
    "require-dev": {
    "phpunit/phpunit": "^9"
    },
    "scripts": {
    "test": "phpunit"
    }
    }

    This allows us to run the tests by running the following command:

    composer test

    Adding Functionality

    Now that we have created our package, let’s add some functionality to it.

    We will create a class called HelloWorld in the src directory. The class will have a method called sayHello that will return the string Hello World!.

    Before we create the actual class, we need to create the test class first. Create a class called HelloWorldTest in the tests directory. The class will have a method called testSayHello that will test the sayHello method of the HelloWorld class.

    <?php

    namespace MyNameHelloWorldTests;

    use MyNameHelloWorldHelloWorld;

    class HelloWorldTest extends PHPUnitFrameworkTestCase
    {
    public function testSayHello()
    {
    $helloWorld = new HelloWorld();
    $this->assertEquals("Hello World!", $helloWorld->sayHello());
    }
    }

    Now we can create the actual class. Create a class called HelloWorld in the src directory. The class will have a method called sayHello that will return the string Hello World!.

    <?php

    namespace MyNameHelloWorld;

    class HelloWorld
    {
    public function sayHello()
    {
    return "Hello World!";
    }
    }

    Let’s run the tests to see if everything is working as expected. Run the following command to run the tests:

    composer test

    We get the following output:

    Version Control

    Now that we have created our package, we need to add it to version control. We will use git for this. Run the following command to initialize git:

    git init

    Before we add the files to git, we need to add the vendor directory to the .gitignore file. This is because the vendor directory contains all the dependencies of our package. We don’t want to add the dependencies to git because they are already available on packagist.

    We also want to ignore the phpunit cache directory. Add the following lines to the .gitignore file:

    vendor/
    .phpunit.result.cache

    Add all the files to git:

    git add .

    Commit the files:

    git commit -m "Initial commit"

    Pushing to GitHub

    Now that we have created our package, we need to push it to GitHub. Create a new repository on GitHub and push the files to it.

    Creating a GitHub Repository

    To create a new repository on GitHub, go to GitHub and fill your package details. It looks something like this:

    Make sure you choose the Public option. This will make your package available to everyone. Click on the Create repository button to create the repository.

    Pushing the Files to GitHub

    Now that we have created the repository, we need to push the files to it. Run the following commands to add the remote and push your files:

    git remote add origin https://github.com/[user-name]/repository-name.git
    git branch -M master
    git push -u origin master

    Once the files are pushed to GitHub, you can see them on the repository page.

    Publishing the Package

    Now that we have created our package, we need to publish it. Publishing a package means making it available to everyone. We will publish our package on packagist.

    Creating an Account on Packagist

    To create an account on Packagist, go to Packagist and click on the Sign Up button or go to the registration page. Fill your details and click on the Sign Up button to create an account.

    Adding the Package to Packagist

    To add the package to Packagist, copy the URL of your repo and go to the submit page. Paste the URL in the Repository URL field and click on the Check button.If the package is valid, click on the Submit button to add the package to Packagist.

    Creating a release

    To create a release, go to the releases page of your package. Click on the Create a new release button to open the new release page.

    Enter v0.0.1 as the release title and Initial release as the description. Click on the Publish release button. Then click the Choose a tag dropdown and type v0.0.1 and click on the Create new tag button.

    Then scroll down and click on the Publish release button.

    You should see something like this:

    Go to Your Package (https://packagist.org/packages/vendor/package-name) on Packagist you should see something like this:

    And that’s it. Congratulations! You have successfully published your first package on packagist.

    Installing the Package

    Now that we have published our package, we can use it in our projects. We will use the package in our projects.

    To install the package, run the following command in your project directory that already has a composer.json file:

    composer require my-name/hello-world

    Using the Package

    Now that we have installed the package, we can use it in our project. Create a file called index.php in the root directory of your project. Add the following code to the file:

    <?php

    require_once __DIR__ . &#039;/vendor/autoload.php';

    use MyNameHelloWorldHelloWorld;

    $helloWorld = new HelloWorld();

    echo $helloWorld->sayHello();

    Run the following command to run the file:

    php index.php

    You should see the following output:

    And that’s it. Congratulations! You have successfully created and published your first package on packagist and used it in your project.

    Conclusion

    In this tutorial, we have learned how to create and publish a package on Packagist. We have also learned how to use the package in our project. I hope you found this tutorial helpful. If you have any questions, feel free to ask them in the comments section below.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: