Test Driven Development

6th May 19
coding blur

I am currently learning about test driven development.  So you write the tests first and then you write the code that passes the test.  

Project Requirements

In this project the project requirements are: 

view monthly budget

view all transactions

create / update / delete transactions

create / update / delete categories

sign up and sign in

 

Display All Transactions test

The first test we want to create will be a feature test and test whether all transactions are displayed.  After creating a new Laravel project I run in the console: 

php artisan make:test ViewTransactionsTest

The first test:

   /**
     * @test
     */
    public function it_can_display_all_transactions()
    {
        $transaction = factory('App\Transaction')->create();
        $this->get('/transactions')
        ->assertSee($transaction->description);
    }

Firstly I need to have a record in the database and will be using model factories to accomplish that.  The first line will create a new instance of the class 'App\Transaction' and persist it to the database with the data we provided to the model factory.

Then we want to visit the page '/transaction' and then assert that I can see this transaction.  

 

test for it can display all transactions

The test unsurprisingly failed as we have not created the transaction class yet.  So I created that by running the command:

php artisan make:model Transaction

We need to create a model factory for the Transaction class.  With database/factories I added TransactionFactory.php file with : 


/** @var \Illuminate\Database\Eloquent\Factory $factory */
use App\Transaction;
use Illuminate\Support\Str;
use Faker\Generator as Faker;


$factory->define(Transaction::class, function (Faker $faker) {
    return [
      'description' => $faker->sentence(2)
    ];
});

After I run the test again and I get the error : 

Caused by
PDOException: SQLSTATE[HY000] [1045] Access denied for user 'homestead'@'localhost' (using password: YES)

This is because I haven't set up a database and the right connection details in laravel.  I created a new database called 'test' and edited the laravel .env file with the correct credentials.  And sure enough when I run the test I no longer get the error when I run my test.  My next error is:

Caused by
PDOException: SQLSTATE[42S02]: Base table or view not found: 1146 Table 'test.transactions' doesn't exist

We might have a valid database but we don't have a valid table to hold the data.  To do this we need to run a migration:

php artisan make:migration create_transactions_table --create=transactions

This creates a migration file named it create_transactions_table for a database table named transactions.  Within the migration file I added a description field:

    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('transactions', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->string('description');
            $table->timestamps();
        });
    }

Though if you were to run the test you still would be faced with the same error, this is because we haven't told the test case to execute any migration.  So in my test file I add the DatabaseMigrations trait. 

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

class ViewTransactionsTest extends TestCase
{
    use DatabaseMigrations;
    /**
     * @test
     */

Unfortunately when I run the test I get a whole lot of html with little idea as to where the error is.  So I go the the development website and take myself to /transactions, I get a 404 error - not found.  This is because I do not a have a route set up.  So in laravel, rather than throwing an error when I run the test, its displaying the 404 page.  So to fix this you need to amend the TestCase class to disable exception handling so we can see the exception in the test not the html.  Running the test again: 

Symfony\Component\HttpKernel\Exception\NotFoundHttpException: 

I get the http not found exception.  So indeed it is because the router hasn't been set up.  So to fix that go to web.php in routes.  Create a GET route pointing to '/transactions' and handled by the TransactionsController, the index method:

Route::get('/transactions', 'TransactionsController@index');

 

I have recently completed my apprenticeship.  I am currently building the API to a React JS site using Firebase.