Database testing: model factory generation and use


brief introduction

Laravel provides several useful tools to make testing database driven applications easier. First, you can use auxiliary functions assertDatabaseHas To assert whether the data in the database matches the given data set. For example, if you want to pass email Value is xueyuanjun@laravelacademy.org Condition to data table users To query whether the record exists, we can do the following:

 public function testDatabase() { // Make call to application... $this->assertDatabaseHas('users', [ 'email' => ' xueyuanjun@laravelacademy.org ' ]); }

You can also use assertDatabaseMissing The helper function asserts that the data does not exist in the database.

of course, assertDatabaseHas Methods and other similar auxiliary methods are packaged for convenience. You can also use other PHPUnit built-in assertion methods to test.

Generate model factory

Model factories can be used to quickly populate data tables. To create a model factory, you can use the Artisan Command make:factory

 php artisan make:factory PostFactory

The newly created factory class is located in database/factories Directory.

--model Option can be used to indicate the model class corresponding to the model factory. This option pre fills the generated factory class with the given model name:

 php artisan make:factory PostFactory --model=Post

Generated PostFactory The contents are as follows:

 <? php use Faker\Generator as Faker; $factory->define(App\Post::class, function (Faker $faker) { return [ // ]; });

Reset database after each test

It is often useful to reset the database after each test, so that the data from the last test will not affect the next test. RefreshDatabase The trait is based on whether you use an in memory database or a relational database to migrate the test database in the best way. When you use this trait on the test class, you don't need to worry about anything. The system will automatically help you reset the database after each test:

 <? php namespace Tests\Feature; use Tests\TestCase; use Illuminate\Foundation\Testing\RefreshDatabase; use Illuminate\Foundation\Testing\WithoutMiddleware; class ExampleTest extends TestCase { use RefreshDatabase; /** * A basic functional test example. * * @return void */ public function testBasicExample() { $response = $this->get('/'); // ... } }

Preparation factory

When testing, you usually need to insert new data into the database before executing the test. When creating test data, Larravel allows you to use the model factory to Eloquent model Define a default set of attribute values instead of manually specifying values for each column. To begin, let's take a look database/factories/ModelFactory.php File, which contains a factory definition:

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

In the closure, as the factory definition, we return the default test values of all properties on the model. This closure receives the PHP library Faker Instance, which allows you to easily generate multiple types of random data for tests.

You can also create additional factory files for each model in order to better organize and manage, for example, you can database/factories Create under directory UserFactory.php and CommentFactory.php File. factories All files in the directory will be automatically loaded by Larave.

Factory status

Status allows you to define discrete modifications that can be used in the model factory in any combination, for example, User The model may have a delinquent Status is used to modify a default attribute value. You can use state Method to define state transitions. For simple states, you can pass an attribute to modify the array:

 $factory->state(App\User::class, 'delinquent', [ 'account_status' => 'delinquent', ]);

If your status needs to be calculated or a $faker Instance, you can use a closed package to calculate the property modification of the state:

 $factory->state(App\User::class, 'address', function ($faker) { return [ 'address' => $faker->address, ]; });

Use factory

Create model

After the factory is defined, you can use the global factory Methods use them to generate model instances, so let's look at some examples of creating models. First, we use make Method, which creates the model without saving it to the database:

 public function testDatabase(){ $user = factory(App\User::class)->make(); //User Model Test }

You can also create multiple model collections or create models of a given type:

 //Create 3 App  User instances $users = factory(App\User::class, 3)->make();

Application status

You can also apply any state to the model. If you want to apply multiple state transformations to the model, you need to specify the name of each state you want to apply:

 $users = factory(App\User::class, 5)->states('deliquent')->make(); $users = factory(App\User::class, 5)->states('premium', 'deliquent')->make();

Override Attributes

If you want to override some default values in the model, you can pass array values to make Method, only the specified values will be replaced, and the remaining values will remain the default values specified by the factory:

 $user = factory(App\User::class)->make([ 'name' => 'Abigail', ]);

Persistence model

create The method can not only create model instances, but also use Eloquent's save Method to save them to the database:

 public function testDatabase() { //Create a single App  User instance $user = factory(App\User::class)->create(); //Create 3 App  User instances $users = factory(App\User::class, 3)->create(); //Using models in tests }

You can pass an array to create The method overrides the attributes on the model:

 $user = factory(App\User::class)->create([ 'name' => 'Abigail', ]);

Association

In this example, we add an association to the created model, and use create Method to create multiple models, an Eloquent will be returned Collection Instance , allowing you to use all the methods provided by the collection, such as each

 $users = factory(App\User::class, 3) ->create() ->each(function($u) { $u->posts()->save(factory(App\Post::class)->make()); });

Association&attribute closure

You can also use the closure attribute in the factory to add associations to the model. For example, if you want to create Post Create a new User For instance, you can do the following:

 $factory->define(App\Post::class, function ($faker) { return [ 'title' => $faker->title, 'content' => $faker->paragraph, 'user_id' => function () { return factory(App\User::class)->create()->id; } ]; });

These closures also receive an array of factory properties containing them:

 $factory->define(App\Post::class, function ($faker) { return [ 'title' => $faker->title, 'content' => $faker->paragraph, 'user_id' => function () { return factory(App\User::class)->create()->id; }, 'user_type' => function (array $post) { return App\User::find($post['user_id'])->type; } ]; });

Valid Assertion Methods

Larravel is PHPUnit The test provides multiple database assertion methods:
method describe
$this->assertDatabaseHas($table, array $data); Assertion data table contains given data
$this->assertDatabaseMissing($table, array $data); Assertion data table does not contain the given data
$this->assertSoftDeleted($table, array $data); Asserting that the given record has been soft deleted

give the thumbs-up Cancel Like Collection Cancel Collection

<<Previous: Browser test: use Laravel Dusk for browser test

>>Next: Simulation: Simplify testing by forging events, mails, notifications, queues, file storage and other services