b166er's avatar

On Testing API-Response not equal with Database results

Hi,

I'm trying to test my API, so I created a factory for my Model class and a simple test for it. i had considered running a db-seed first and all feature-tests should then work with this data, so the api response and the database query should then have the same fake results. after the tests it would be great to truncate database, or before each start. I suspect the seeder will be executed for the api query before the test, but existing test data from migration will be output for the database query. When I empty the test database (and deactive DatabaseMigrations-trait), the result of database is empty, but the api still delivers fake data. how can i synchronize the data for both connections, api- and db-query?

my /tests/Feature/PersonTest.php

<?php

namespace Tests\Feature;

use Illuminate\Support\Facades\App;
use Tests\TestCase;
use Illuminate\Http\Request;
use App\Http\Controllers\API\PersonController;
use App\Models\API\Person;
use App\Http\Resources\PersonResource;
use App\Http\Resources\PersonResourceCollection;
use Database\Seeders\ApiSeeder;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Foundation\Testing\DatabaseMigrations;

class PersonTest extends TestCase {
    /**
     * The RefreshDatabase trait leverages database transactions which will not
     * be applicable or available across HTTP requests. Instead, use the
     * DatabaseMigrations trait, which re-migrates the database for each test
     * https://laravel.com/docs/8.x/dusk#migrations
     */
    //use RefreshDatabase;
    //use DatabaseTransactions;
    //use DatabaseMigrations;

    /**
     * Indicates whether the default seeder should run before each test.
     *
     * @var bool
     */
    //protected $seed = true;

    /**
     * Run a specific seeder before each test.
     *
     * @var string
     */
    //protected $seeder = ApiSeeder::class;

    /**
     * A basic test example.
     *
     * @return void
     */
    public function testIndexSuccess() {
        //$this->seed();
        // Run a specific seeder...
//        $this->seed(ApiSeeder::class);

		// call API
        $test_response = $this->withHeaders([
                    'Accept' => 'application/json'
                ])->getJson('/persons');

		// get response from API
        $result = $test_response->dump();

		// check if api-response has result key
        $this->assertArrayHasKey('result', $result);
		// save api-result
        $result_persons = $result['result'];
		// get number of result entries
        $count_result_persons = count($result_persons);

        // get all database entries with pagination-wrapper like in Controller
        $db_persons = Person::where('1', '=', '1')->paginate(config('app.pagination_per_page'));
        // form to json-output like in Controller
        $resource_persons = new PersonResourceCollection($db_persons);
        //dd($result_persons, $db_persons); // is not the same, why?

		// assert json structure
        $result->assertOk() // ->assertStatus(200)
                ->assertExactJson([
                    'count' => $count_result_persons,
                    'total' => $count_result_persons,
                    'perPage' => 100,
                    'lastPage' => 1,
                    'currentPage' => 1,
                    'status' => 'success',
                    'message' => 'OK',
                    'result' => $resource_persons->jsonSerialize() // <-- here is the error, result of db != api
        ]);
    }

}

app/Models/API/BaseModel.php

<?php
namespace App\Models\API;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Orbit\Concerns\Orbital;

class BaseModel extends Model {

    use Orbital,
        HasFactory;

    /**
     * The number of models to return for pagination.
     *
     * @var int
     */
    protected $perPage = 100;

    /**
     * Indicates if the model should be timestamped.
     *
     * @var bool
     */
    public $timestamps = false;
}

app/Models/API/Person.php

namespace App\Models\API;
use App\Models\API\BaseModel;
use App\Models\API\Friend;
use Illuminate\Database\Schema\Blueprint;

class Person extends BaseModel {

    /**
     * Indicates if the IDs are auto-incrementing.
     *
     * @var bool
     */
    public $incrementing = false;

    /**
     * The primary key associated with the table.
     *
     * @var string
     */
    protected $primaryKey = 'name';

    /**
     * The data type of the auto-incrementing ID.
     *
     * @var string
     */
    protected $keyType = 'string';

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = ['name', 'is_active'];

    /**
     * The model's default values for attributes.
     *
     * @var array
     */
    protected $attributes = [
        'is_active' => 0,
    ];

    /**
     * The attributes that should be cast.
     *
     * @var array
     * */
    protected $casts = [
        'is_active' => 'int',
    ];

      /**
     *
     * @param Blueprint $table
     */
    public static function schema(Blueprint $table) {
        $table->string('name');
        $table->integer('is_active');
    }

    /**
     * Get the friends for the person.
     */
    public function friends() {
        return $this->hasMany(Friend::class, 'person_name', 'name');
    }

    /* */
}

query result from api (every time, even if i truncate database):

{
	"count": 3,
	"currentPage": 1,
	"result": {
		"Eriberto": {
			"isActive": 1,
			"name": "Eriberto"
		},
		"Gerald": {
			"isActive": 0,
			"name": "Gerald"
		},
		"Josh": {
			"isActive": 1,
			"name": "Josh"
		}
	},
	"lastPage": 1,
	"message": "OK",
	"perPage": 100,
	"status": "success",
	"total": 16
}

query from database (my default entries from migration file):

{
	"count": 3,
	"currentPage": 1,
	"result": {
		"Ben": {
			"isActive": 0,
			"name": "Ben"
		},
		"Carroll": {
			"isActive": 1,
			"name": "Carroll"
		},
		"Darron": {
			"isActive": 1,
			"name": "Darron"
		}
	},
	"lastPage": 1,
	"message": "OK",
	"perPage": 100,
	"status": "success",
	"total": 3
}

query from database, after truncating (with disabled DatabaseMigrations-trait):

{
	"count": 3,
	"currentPage": 1,
	"result": {},
	"lastPage": 1,
	"message": "OK",
	"perPage": 100,
	"status": "success",
	"total": 3
}

result from console after executing test

$ php artisan test

   PASS  Tests\Unit\ExampleTest
  ✓ example

   FAIL  Tests\Feature\PersonTest
  ⨯ index success

   PASS  Tests\Feature\RootTest
  ✓ root

  ---

  • Tests\Feature\PersonTest > index success
  Failed asserting that two strings are equal.

  at tests/Feature/PersonTest.php:87
     83▕                     'lastPage' => 1,
     84▕                     'currentPage' => 1,
     85▕                     'status' => 'success',
     86▕                     'message' => 'OK',
  ➜  87▕                     'result' => $resource_persons->jsonSerialize()
     88▕         ]);
     89▕
  --- Expected
  +++ Actual
  @@ @@
-'{"count":0,"currentPage":1,"result":[],"lastPage":1,"message":"OK","perPage":100,"status":"success","total":0}'
+'{"count":3,...
0 likes
1 reply
b166er's avatar
b166er
OP
Best Answer
Level 1

ok, i think i found the issue, seems i running into this bug, source:

<?php

namespace Tests;

use Illuminate\Contracts\Console\Kernel;
use Illuminate\Foundation\Application;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\App;

trait CreatesApplication {

    /**
     * Creates the application.
     *
     * @return \Illuminate\Foundation\Application
     */
    public function createApplication(): Application {

        $app = require __DIR__ . '/../bootstrap/app.php';
        $app->make(Kernel::class)->bootstrap();
        $this->clearConfigCache();
        if (false === App::environment('testing')) {
            $this->fail('Not in testing environment according to APP_ENV. Aborting');
            die(1);
        }

        return $app;
    }

    private function clearConfigCache(): void {
        // re-generate new config cache with testing-env
        Artisan::call('config:cache');
        // clear config cache for further tests
        Artisan::call('config:clear');
    }

}

update: and my second mistake was to using custom variablenames in .env for database connection (because i use multiple databases), so i changed back to the default var-name: DB_CONNECTION seems phpunit require/using DB_CONNECTION somewhere.

Please or to participate in this conversation.