Merhaba,


Bu yazıda Laravel'de Unit Test kullanımından bahsedeceğim. Örnek projede basit bir Rest Api yazıp onun üzerinden gideceğim. Örnek projeyi Github üzerinden paylaştım. İndirip inceleyebilirsiniz.


Örnek proje linki


İlk olarak istediğimiz bir dizinde (ör: masaüstü) komut satırını açıp yeni bir Laravel projesi oluşturmak için gerekli kodları yazıyoruz:

composer create-project --prefer-dist laravel/laravel unit-test-sample


Sonrasında Xampp yardımıyla yerelde bir veritabanı oluşturuyoruz. Proje kök dizinindeki .env dosyasını açarak veritabanı bilgilerini aşağıdaki gibi düzenliyoruz:

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=unit_test_sample
DB_USERNAME=root
DB_PASSWORD=


defaultStringLength hatasıyla karşılaşmamak için app/Providers/AppServiceProvider.php dosyasını açarak boot() fonksiyonunu düzenliyoruz:

public function boot()
{
    Schema::defaultStringLength(191);
}


Şimdi bir blog sitemizin olduğunu ve bu sitede yazılar paylaştığımızı düşünelim. Bu yazıların veritabanında tutulduğu tabloyu oluşturmak adına;

php artisan make:migration create_posts_table

şeklinde migration oluşturup up() kısmını;

public function up()
{
    Schema::create('posts', function (Blueprint $table) {
        $table->bigIncrements('id');
        $table->string('title');
        $table->text('content');
        $table->timestamps();
    });
}

şeklinde düzenliyoruz ve tablomuzu oluşturmak için;

php artisan migrate

komutunu çalıştırıyoruz.


Model dosyamızı oluşturuyoruz;

php artisan make:model Post

Post model dosyamızın içeriği;

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Post extends Model
{
    protected $fillable = [
        'title',
        'content'
    ];
}


api.php dosyamızı açıp içeriğine;

Route::group(['prefix' => 'posts'], function () {
    Route::get('/', 'PostController@index')->name('posts');
    Route::get('/{post}', 'PostController@show')->name('posts.show');
    Route::post('/', 'PostController@store')->name('posts.store');
    Route::put('/{post}', 'PostController@update')->name('posts.update');
    Route::delete('/{post}', 'PostController@delete')->name('posts.delete');
});

şeklinde route tanımlamalarını yapıyoruz.


Controller dosyamızı oluşturuyoruz;

php artisan make:controller PostController

PostController dosyamızın içeriği;

<?php

namespace App\Http\Controllers;

use App\Post;
use Illuminate\Http\Request;

class PostController extends Controller
{
    public function index()
    {
        return Post::all();
    }

    public function show(Post $post)
    {
        return $post;
    }

    public function store(Request $request)
    {
        $post = Post::create($request->all());
        return response()->json($post, 201);
    }

    public function update(Request $request, Post $post)
    {
        $post->update($request->all());
        return response()->json($post);
    }

    public function delete(Post $post)
    {
        $post->delete();
        return response()->json(null, 204);
    }
}


Böylelikle Rest Api hazır hale gelmiş oluyor.


Artık işin Unit Test boyutuna geçebiliriz;


ÖNEMLİ NOT: Unit Test sahte (fake) verilerle yapılır. Herhangi bir şekilde gerçek veritabanımıza müdahale etmemesi gerekir ki proje de karmaşıklığa hatta daha da ötesi veritabanımızdaki tabloların tamamen silinmesi gibi durumlara yol açmasın. Bundan dolayı yapmamız gereken işlemler şu şekilde olacaktır;

  • Factory dosyası oluşturuyoruz;
php artisan make:factory PostFactory

PostFactory dosyamızın içeriği;

<?php

/* @var $factory \Illuminate\Database\Eloquent\Factory */

use App\Post;
use Faker\Generator as Faker;

$factory->define(Post::class, function (Faker $faker) {
    return [
        'title' => $faker->sentence,
        'content' => $faker->paragraph
    ];
});
  • Yerelde test adında bir veritabanı oluşturuyoruz ve .env dosyamızı açıp içeriğine test veritabanımızı da ekliyoruz;
DB_CONNECTION_SECOND=test
DB_HOST_SECOND=127.0.0.1
DB_PORT_SECOND=3306
DB_DATABASE_SECOND=test
DB_USERNAME_SECOND=root
DB_PASSWORD_SECOND=
  • phpunit.xml dosyasını açıp <php> taglarının bulunduğu kısma veritabanı olarak test'i kullanmak istediğimizi belirtiyoruz;
<php>
    <server name="APP_ENV" value="testing"/>
    <server name="BCRYPT_ROUNDS" value="4"/>
    <server name="CACHE_DRIVER" value="array"/>
    <server name="MAIL_DRIVER" value="array"/>
    <server name="QUEUE_CONNECTION" value="sync"/>
    <server name="SESSION_DRIVER" value="array"/>
    <server name="DB_DATABASE" value="test"/>
</php>
  • tests klasörünün içindeki TestCase dosyamızı açıp içeriğini şu şekilde düzenliyoruz;
<?php

namespace Tests;

use Faker\Factory;
use Illuminate\Foundation\Testing\DatabaseMigrations;
use Illuminate\Foundation\Testing\TestCase as BaseTestCase;

abstract class TestCase extends BaseTestCase
{
    use CreatesApplication, DatabaseMigrations;

    protected $faker;

    public function setUp(): void
    {
        parent::setUp();
        $this->faker = Factory::create();
    }
}
  • test dosyamızı oluşturuyoruz;
php artisan make:test PostTest --unit

PostTest dosyamızın içeriğini şu şekilde düzenliyoruz;

<?php

namespace Tests\Unit;

use App\Post;
use Tests\TestCase;

class PostTest extends TestCase
{
    public function test_can_create_post()
    {
        $data = [
            'title' => $this->faker->sentence,
            'content' => $this->faker->paragraph
        ];

        $this->post(route('posts.store'), $data)
            ->assertStatus(201)
            ->assertJson($data);
    }

    public function test_can_update_post()
    {
        $post = factory(Post::class)->create();

        $data = [
            'title' => $this->faker->sentence,
            'content' => $this->faker->paragraph
        ];

        $this->put(route('posts.update', $post->id), $data)
            ->assertStatus(200)
            ->assertJson($data);
    }

    public function test_can_show_post()
    {
        $post = factory(Post::class)->create();

        $this->get(route('posts.show', $post->id))
            ->assertStatus(200);
    }

    public function test_can_delete_post()
    {
        $post = factory(Post::class)->create();

        $this->delete(route('posts.delete', $post->id))
            ->assertStatus(204);
    }

    public function test_can_list_posts()
    {
        $posts = factory(Post::class, 2)->create()->map(function ($post) {
            return $post->only(['id', 'title', 'content']);
        });

        $this->get(route('posts'))
            ->assertStatus(200)
            ->assertJson($posts->toArray())
            ->assertJsonStructure([
                '*' => [ 'id', 'title', 'content' ]
            ]);
    }
}

PostTest dosyamızda yazmış olduğumuz kısımları kısaca özetleyecek olursak;

  • test_can_create_post kısmında;
  • title ve content içeren fake bir veri oluşturduk.
  • Rest Api'de yer alan store fonksiyonuna fake veri ile post isteğinde bulunduk.
  • Cevap olarak gelen durum kodunu ve json verisini kontrol ettik. Durum kodu 201'e ve json verisi de gönderdiğimiz veriye eşitse testten geçmesini söyledik.


  • test_can_update_post kısmında;
  • Post modeline uygun fake bir veri oluşturduk.
  • Rest Api'de yer alan update fonksiyonuna fake veri ve id parametresi ile put isteğinde bulunduk.
  • Cevap olarak gelen durum kodunu ve json verisini kontrol ettik. Durum kodu 200'e ve json verisi de gönderdiğimiz veriye eşitse testten geçmesini söyledik.


  • test_can_show_post kısmında;
  • Post modeline uygun fake bir veri oluşturduk.
  • Rest Api'de yer alan show fonksiyonuna id parametresi ile get isteğinde bulunduk.
  • Cevap olarak gelen durum kodunu kontrol ettik. Durum kodu 200'e eşitse testten geçmesini söyledik.


  • test_can_delete_post kısmında;
  • Post modeline uygun fake bir veri oluşturduk.
  • Rest Api'de yer alan delete fonksiyonuna id parametresi ile delete isteğinde bulunduk.
  • Cevap olarak gelen durum kodunu kontrol ettik. Durum kodu 204'e eşitse testten geçmesini söyledik.


  • test_can_list_posts kısmında;
  • Post modeline uygun fake 2 adet veri oluşturduk.
  • Rest Api'de yer alan index fonksiyonuna get isteğinde bulunduk.
  • Cevap olarak gelen durum kodunu, json verisini ve json verisinin yapısını kontrol ettik. Durum kodu 200'e, json verisi gönderdiğimiz veriye ve json verisinin yapısı belirttiğimiz yapıya eşitse testten geçmesini söyledik.


Son olarak komut satırına şu ifadeyi yazarak testimizi çalıştırıyoruz;

"./vendor/bin/phpunit"

Not: Tırnak işaretlerini (") unutmuyoruz.


Gelen sonuçta kodumuzun tüm testlerden başarılı şekilde geçtiğiniz görebiliriz;


Umarım yararlı olmuştur.


İyi çalışmalar...