Skip to content

Querying Data Building Test Cases

Adarsh Kumar Maurya edited this page Nov 29, 2018 · 1 revision

Querying Data - Building Test Cases

So now that we’ve got a couple methods that help us get the data out when we’re doing our indexes of the products and then the product descriptions, we want to go back and spend a little bit of time in our test case to make sure that we actually have test coverage to ensure for certain that these methods are working correctly.

Before we do that though, I want to talk a little bit about factories, so with this latest version of Laravel there’s been some great work in the testing space, like I was talking about before. One of the things are these factories. So you can basically go in and say, for each of the models that you’ve created, you can kind of build a skeleton or framework for how dummy data for each of them should be created and then you can use these factories in the test cases to either insert data into the database before performing your test, or creating dummy data that can then be inserted into the test itself, so when you’re testing inserts or testing queries.

So let’s actually go create a couple factories for our two models, we have a product and a description. So these are located inside of the database directory, so inside of our database directory we had migrations. We had seeds. And there’s also this factories. There already is a stock factory in here for users, but like I said we got rid of our user, so we can actually change this, just to be product.

<?php

use Faker\Generator as Faker;

/*
|--------------------------------------------------------------------------
| Model Factories
|--------------------------------------------------------------------------
|
| This directory should contain each of the model factory definitions for
| your application. Factories provide a convenient way to generate new
| model instances for testing / seeding your application's database.
|
*/

// $factory->define(App\User::class, function (Faker $faker) {
//     return [
//         'name' => $faker->name,
//         'email' => $faker->unique()->safeEmail,
//         'email_verified_at' => now(),
//         'password' => '$2y$10$TKh8H1.PfQx37YgCzwiKb.KjNyWgaHb9cbcoQgdIVFlYg7B77UdFm', // secret
//         'remember_token' => str_random(10),
//     ];
// });

$factory->define(App\Product::class, function (Faker $faker) {
    return [
        'name' => $faker->word,
    ];
});

$factory->define(App\Description::class, function (Faker $faker) {
    return [
        'body' => $faker->text,
    ];
});

So we’ll build a factory for products, it’s just going to have a name that’s automatically generated and attached to the method or to the model that’s generated and then we’ll just duplicate this and we’ll create one for description as well, but description, again, we only have the body value and then here we can just do text.

So basically this faker class, this is another third party library that’s been included in the framework, and so it takes care of all the plumbing to make sure that it gets included correctly, but this is basically just a faker class. There’s some documentation on how faker actually works, but we can actually pull down some values off of this faker object that will help us build these models for testing.

So if we go back and look at our test cases now, we had our testProductsList that was simply going, visiting the API products index route, getting a response and making sure that it was okay.

    public function testProductsList()
    {
        $this->get(route('products'))
             ->assertOk();

      $this->get(route('products.index'))
           ->assertOk();
    }
``

We want to embellish that now a little bit more and actually make it valuable. So the first thing that we want to do is we actually want to generate some products. So let’s grab some products, and here we’re going to use this factory method and then we’re going to pass in the product class and then this is going to generate for us one single product in the database. So we finish it off, we say we want the factory, the factory’s going to be of the type App product but basically it’s just going to create one record for us. Because this method is meant to return a collection, we might need more than one to make sure that it’s working correctly, so in order to do that, this factory method accepts a second param, which is a number. So we can say here now what we want to create is we want to create three products in the database then perform our operation, then make sure that we can see the correct results there. 
```php
  public function testProductsFactoryList()
    {
      $products = factory(\App\Product::class,3)->create();

      $this->get(route('products.index'))
           ->assertOk();

      array_map(function($product){
        $this -> assertJson($product);
      }, $products->all());
    }

So before we actually run this test, I want to talk a little bit about the testing environment and the database that’s attached to that. There is a testing database that’s already been included in the project, and so this is a database that kind of lives in memory. So you can change it, you can actually have like a MySQL database or you can have a SQLite or what have you, but there’s one that’s just built in to memory.

And so this is automatically available. We’ve already run our migrations, and so basically inside of these test cases you can pick from a couple different options of how you want to ensure that each test case when it runs is going to have a fresh copy of the database, otherwise it would just keep inserting new records or you’d always have kind of constantly growing test data, which might get you some weird results and maybe even slow down your test case. So there’s two options that they give for you, and this example test already includes the using statements that can bring them in for you.

One of them is to use migrations, which will actually perform a migrate reset and then a migrate in between each test run. That can be very expensive.

The other one is database transactions. So a database transaction is kind of a group of mini-queries that can be applied to the database but they can also be rolled back. So this one is quite a bit more performate, so you basically say I’m going to insert these three and then perform this one select and then after we’re done, undo the insert three, so we kind of get back to where we’re going to be.

So here we’re just going to make sure that our test case includes this database transactions trait and so now basically this entire test we just set up to use these transactions to perform the tests.

So if we run this, if we actually run the test now. Oh, string is not a real thing. So we run our test and it failed, because I put string in our faker class instead of the actual entity which we need which is word. So let’s go back to our model factory and let’s update that. So instead of string it’s actually just word. So now we should be back to fixing getting our tests to run correctly. And so here now we have our assertions, but right now our assertion is only saying status okay, so we know that the plumbing is still working, but we still haven’t seen what’s in the response to make sure that it’s actually giving us the correct data back. So in order to make that work, in our test case, we’ve performed the request, we’ve asserted that it came back with a response okay, what we want to do now is use some of those helper methods that we were talking about before like See to look into the response and actually verify that there is correct responses in there.

So in order to do that, we have three products that were created and then we performed our tests, so what I want to do is I want to loop over that list and make sure that the response that we get back actually includes the value that is for them. So I’m going to use an array map method. And then we’ll use a callback, which we’ll add all of the fun values to, and then I’m going to pass in the array of the collection of products. So this is going to iterate over the entire collection of products, and it’s going to investigate each product itself, and so in order to make this actually check and see, what we want to do is we want to use our, one of our assertJson methods. So we’ve created our products. We’ve performed our request. We’re going to loop over each of the products that were intended to be created with those random name values, and then we’re just going to make sure that their randomized value is actually in the response that we got back. So if we go and run that test again. I got the wrong method name. I misspelled it. It’s cool. So we just tried to run the test.