First let’s create the database using phpMyAdmin
database: simple_api_rest
collation: utf8mb4_unicode_ci
Create a laravel project using one of this alternatives.
composer create-project laravel/laravel simple_api_rest
composer global require laravel/installer
laravel new simple_api_rest
Don’t select a starter kit.
We will use PHPUnit for testing.
Select mysql as a database.
Now run the command
php artisan install:api
This command create a api.php file in routes folter.
Each route defined in that file have the /api prefix by default.
Remove the route that was generated.
Creating the model Post with their migration.
php artisan make:model Post -m
This command will create the migration in database/migration
And the model in app/Models
public function up(): void
{
Schema::create('posts', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->text('content');
$table->timestamps();
});
}
Creating the PostFactory.
php artisan make:factory PostFactory
This command will create the factory in database/factories
public function definition(): array
{
return [
"title" => $this->faker->title(),
"content" => $this->faker->paragraph(),
];
}
Creating the PostSeeder
php artisan make:seeder PostSeeder
This command will create the seeder in database/seeders
use App\Models\Post;
class PostSeeder extends Seeder
{
/**
* Run the database seeds.
*/
public function run(): void
{
Post::factory(10)->create();
}
}
Open the file seeders/DatabaseSeeder
public function run(): void
{
$this->call([
PostSeeder::class,
]);
}
Run the migration and the seeder
php artisan migrate:fresh # fresh will remove all the data saved
php artisan db:seed # run the seeders
or
php artisan migrate:fresh --seed
You can see data created in the posts table in phpMyAdmin
Creating a test to view if data was created
php artisan make:test PostTest
This command will create the test in tests/Feature
use PHPUnit\Framework\Attributes\Test;
class PostTest extends TestCase
{
#[Test]
public function posts_must_be_created(): void
{
$this->assertDatabaseCount('posts', 10);
}
}
Run the tests
php artisan test # run all the tests
or
php artisan test --filter posts_must_be_created # run all test that matches
Using a database in memory to run the tests
Open phpunit.xml and uncomment two lines
<php>
<env name="APP_ENV" value="testing"/>
<env name="APP_MAINTENANCE_DRIVER" value="file"/>
<env name="BCRYPT_ROUNDS" value="4"/>
<env name="CACHE_STORE" value="array"/>
<env name="DB_CONNECTION" value="sqlite"/>
<env name="DB_DATABASE" value=":memory:"/>
<env name="MAIL_MAILER" value="array"/>
<env name="PULSE_ENABLED" value="false"/>
<env name="QUEUE_CONNECTION" value="sync"/>
<env name="SESSION_DRIVER" value="array"/>
<env name="TELESCOPE_ENABLED" value="false"/>
</php>
If we run the tests again will throw an error because the seeder wasn’t executed.
class PostTest extends TestCase
{
use RefreshDatabase; // Create the database and run the migrations in each test
protected function setUp(): void
{
parent::setUp();
$this->seed(PostSeeder::class); // Run the seeder PostSeeder
}
#[Test]
public function posts_must_be_created(): void
{
$this->assertDatabaseCount('posts', 10);
}
}
Creating controller
php artisan make:controller PostController --api --model=Post
This command will create the controller in app/Http/Controllers
use App\Models\Post;
use Illuminate\Http\Request;
class PostController extends Controller
{
public function index()
{
$posts = Post::all();
return response()->json($posts);
}
public function store(Request $request)
{
$data = $request->validate([
'title' => 'required|string|max:255',
'content' => 'required|string',
]);
$post = Post::create($data);
return response()->json($post, 201);
}
public function show(Post $post)
{
return response()->json($post);
}
public function update(Request $request, Post $post)
{
$data = $request->validate([
'title' => 'sometimes|required|string|max:255',
'content' => 'sometimes|required|string',
]);
$post->update($data);
return response()->json($post);
}
public function destroy(Post $post)
{
$post->delete();
return response()->json(null, 204);
}
}
Defining routes
use App\Http\Controllers\PostController;
use Illuminate\Support\Facades\Route;
Route::apiResource('posts', PostController::class);
Command to see the routes defined
php artisan route:list
or
php artisan r:l
CRUD tests
class PostTest extends TestCase
{
use RefreshDatabase; // Create the database and run the migrations in each test
protected function setUp(): void
{
parent::setUp();
$this->seed(PostSeeder::class); // Run the seeder PostSeeder
}
#[Test]
public function posts_must_be_created(): void
{
$this->assertDatabaseCount('posts', 10);
}
#[Test]
public function must_show_post_list(): void
{
$response = $this->getJson(route('posts.index'));
$response->assertStatus(200);
$response->assertJsonCount(10);
$response->assertJsonStructure([
'*' => [
'id',
'title',
'content',
'created_at',
'updated_at',
]
]);
}
#[Test]
public function must_show_post(): void
{
$response = $this->getJson(route('posts.show', 1));
$response->assertStatus(200);
$response->assertJsonStructure([
'id',
'title',
'content',
'created_at',
'updated_at',
]);
}
#[Test]
public function must_create_post(): void
{
$response = $this->postJson(route('posts.store'), [
'title' => 'Test Post',
'content' => 'This is a test post',
]);
$response->assertStatus(201);
$response->assertJsonStructure([
'id',
'title',
'content',
'created_at',
'updated_at',
]);
$this->assertDatabaseHas('posts', [
'title' => 'Test Post',
'content' => 'This is a test post',
]);
}
#[Test]
public function must_update_post(): void
{
$response = $this->putJson(route('posts.update', 1), [
'title' => 'Updated Test Post',
'content' => 'This is an updated test post',
]);
$response->assertStatus(200);
$response->assertJsonStructure([
'id',
'title',
'content',
'created_at',
'updated_at',
]);
$this->assertDatabaseHas('posts', [
'id' => 1,
'title' => 'Updated Test Post',
'content' => 'This is an updated test post',
]);
}
#[Test]
public function must_delete_post(): void
{
$response = $this->deleteJson(route('posts.destroy', 1));
$response->assertStatus(204);
$this->assertDatabaseMissing('posts', [
'id' => 1,
]);
}
}
Running tests
Update and create tests will throw an error
We’re using mass assignment but still not defined the attributes that can received.
Open Post in app/Models
class Post extends Model
{
use HasFactory;
protected $fillable = [
'title',
'content',
];
}
Runing the tests again
php artisan test
or
php artisan test --filter PostTest
Top comments (0)