Writing your first tests in Laravel using PHPUnit

laravel new blogger
<server name="DB_CONNECTION" value="sqlite"/>
<server name="DB_DATABASE" value=":memory:"/>
php artisan make:model Article -mcf
// database/migrations/2022_03_18_202455_create_articles_table.phpSchema::create('articles', function (Blueprint $table) {
$table->id();
$table->string('title')->unique();
$table->string('slug')->unique();
$table->text('content');
$table->timestamps();
});
// app/Models/Article.php<?phpnamespace App\Models;use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Article extends Model {
use HasFactory;
protected $fillable = [
'title',
'content',
'slug',
'description',
];
}
// database/factories/ArticleFactory.php<?phpnamespace Database\Factories;use Illuminate\Support\Str;
use Illuminate\Database\Eloquent\Factories\Factory;
class ArticleFactory extends Factory {
public function definition()
{
$title = $this->faker->sentence();
return [
'title' => $title,
'slug' => Str::slug($title),
'content' => $this->faker->paragraph(),
];
}
}
php artisan migrate
php artisan make:test Http/Controllers/ArticleControllerTest
// tests/Feature/Http/Controllers/ArticleControllerTest.php  <?php  namespace Tests\Feature\Http\Controllers;use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithFaker;
use Tests\TestCase;
class ArticleController extends TestCase {
use RefreshDatabase;
/** @test */
public function an_article_can_be_created() {
//
}
}
// tests/Feature/Http/Controllers/ArticleControllerTest.php

/** @test */
public function an_article_can_be_created()
{
// given we have some data to save
$data = [
'title' => 'The title of the article',
'slug' => 'the-title-of-the-article',
'content' => 'The content of the article',
];

// when we send the data to the /articles endpoint
$response = $this->post('/articles', $data);

// then we should get a 201 response
$response->assertStatus(201);

// we expect Article::all() to return an array with one item
$this->assertCount(1, Article::all());

// we expect the database to have one record
$this->assertDatabaseHas('articles', $data);
}
php artisan test --filter=ArticleControllerTest  FAIL  Tests\Feature\Http\Controllers\ArticleControllerTest   
⨯ an article can be created
--- • Tests\Feature\Http\Controllers\ArticleControllerTest > an article can be created
Expected response status code [201] but received 404.
Failed asserting that 201 is identical to 404.
// routes/web.php<?phpuse App\Http\Controllers\ArticleController;
use Illuminate\Support\Facades\Route;
Route::get('/', function () {
return view('welcome');
});
Route::post('/articles', [ArticleController::class, 'store'])->name('articles.store');
BadMethodCallException: Method App\Http\Controllers\ArticleController::store does not exist.
// app/Http/Controllers/ArticleController.php<?phpnamespace App\Http\Controllers;use Illuminate\Http\Request;class ArticleController extends Controller
{
public function store()
{
//
}
}
Expected response status code [201] but received 200.
// app/Http/Controllers/ArticleController.phppublic function store()
{
return response()->json([
'message' => 'Article created successfully',
], 201);
}
FAIL  Tests\Feature\Http\Controllers\ArticleControllerTest
⨯ an article can be created
--- • Tests\Feature\Http\Controllers\ArticleControllerTest > an article can be created
Failed asserting that actual size 0 matches expected size 1.
// app/Http/Controllers/ArticleController.phppublic function store(Request $request)
{
Article::create($request->all());
return response()->json([
'message' => 'Article created successfully',
], 201);
}
PASS  Tests\Feature\Http\Controllers\ArticleControllerTest
✓ an article can be created
Tests: 1 passed
Time: 0.09s
// tests/Feature/Http/Controllers/ArticleControllerTest.php/** @test */
public function an_article_is_validated_before_being_saved()
{
// given we have some incorrect data to save (missing title)
$data = [
'slug' => '',
'content' => '',
];
// when we send the data to the /articles endpoint
$response = $this->post('/articles', $data);
// we expect the response to have errors
$response->assertSessionHasErrors('title');
// we expect Article::all() to return an array with zero items
$this->assertCount(0, Article::all());
// or // we expect the database to have zero records
$this->assertDatabaseMissing('articles', $data);
}
FAIL  Tests\Feature\Http\Controllers\ArticleControllerTest
⨯ an article is validated before being saved
--- • Tests\Feature\Http\Controllers\ArticleControllerTest > an article is validated before being saved
Session is missing expected key [errors].
Failed asserting that false is true.
// app/Http/Controllers/ArticleController.phppublic function store(Request $request)
{
$attributes = $request->validate([
'title' => ['required', 'string'],
'slug' => ['required'],
'content' => ['required', 'string'],
]);
Article::create($attributes); return response()->json([
'message' => 'Article created successfully',
], 201);
}
PASS  Tests\Feature\Http\Controllers\ArticleControllerTest
✓ an article can be created
✓ an article is validated before being saved
Tests: 2 passed
Time: 0.11s
// tests/Feature/Http/Controllers/ArticleControllerTest.php/** @test */
public function an_article_can_be_updated()
{
// given we have an article to update
// this will generate a new article and store it in our test database
$article = Article::factory()->create();
// when we send the data to the /articles/{id} endpoint
$response = $this->put('/articles/' . $article->id, [
'title' => 'The title of the article',
'slug' => 'the-title-of-the-article',
'content' => 'The content of the article',
]);
// we expect the database to have the updated record
$this->assertDatabaseHas('articles', [
'id' => $article->id,
'title' => 'The title of the article',
'slug' => 'the-title-of-the-article',
'content' => 'The content of the article',
]);
}
// routes/web.php<?phpuse App\Http\Controllers\ArticleController;
use Illuminate\Support\Facades\Route;
Route::get('/', function () {
return view('welcome');
});
Route::post('/articles', [ArticleController::class, 'store'])->name('articles.store');
Route::put('/articles/{article}', [ArticleController::class, 'update'])->name('articles.update');
// app/Http/Controllers/ArticleController.php<?phpnamespace App\Http\Controllers;use App\Models\Article;
use Illuminate\Http\Request;
class ArticleController extends Controller
{
public function store(Request $request)
{
$attributes = $request->validate([
'title' => ['required', 'string'],
'slug' => ['required'],
'content' => ['required', 'string'],
]);
Article::create($attributes); return response()->json([
'message' => 'Article created successfully',
], 201);
}
public function update(Article $article, Request $request)
{
$attributes = $request->validate([
'title' => ['sometimes', 'string'],
'slug' => ['sometimes'],
'content' => ['sometimes', 'string'],
]);
$article->update($attributes); return response()->json([
'message' => 'Article updated successfully',
], 200);
}
}
PASS  Tests\Feature\Http\Controllers\ArticleControllerTest
✓ an article can be created
✓ an article is validated before being saved
✓ an article can be updated
Tests: 3 passed
Time: 0.13s

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store