Composable tests with laravel
Draft Disclaimer: Please note that this article is currently in draft form and may undergo revisions before final publication. The content, including information, opinions, and recommendations, is subject to change and may not represent the final version. We appreciate your understanding and patience as we work to refine and improve the quality of this article. Your feedback is valuable in shaping the final release.
Composable tests with laravel
tags: laravel
I don't think we can emphasize enough how useful php trait has been since introduced. Today in an attempt to put php trait in the spotlight I would like to share with you a quick usage over my casual coding over the weekend.
TLDR; Trait allowed me to write test cases once and use it n times in other places. No repetitions.
Scenario
- Needed to log all activities on an app
- activity log package from spatie
- of course match my expectation
- example of how it works
- installation composer require spatie/activity-log
- usage
<?php
use Illuminate\Database\Eloquent\Model;
use Spatie\Activitylog\Traits\LogsActivity;
class Region extends Model {
use LogsActivity;
}
- that's it
- now we get to the part I wanted to share
- HasActivityLog
<?php
namespace Tests\Feature\Models\Concerns;
use Spatie\Activitylog\Models\Activity;
/**
* Trait HasActivityLog
* @package Tests\Feature\Models\Concerns
*
* @property string $model
*/
trait HasActivityLog {
public function testLogActivities () {
$count = Activity::count();
$model = $this->model::factory()->create();
$this->assertEquals($count + 1, Activity::count());
$model->update($this->model::factory()->make()->toArray());
$this->assertEquals($count + 2, Activity::count());
$model->delete();
$this->assertEquals($count + 3, Activity::count());
}
}
- Test case
<?php
namespace Tests\Feature\Models;
use Tests\TestCase;
use App\Models\Region;
use Tests\Feature\Models\Concerns\HasActivityLog;
use Illuminate\Foundation\Testing\RefreshDatabase;
class RegionTest extends TestCase
{
use HasActivityLog;
use RefreshDatabase;
protected string $model = Region::class;
}
Test event listeners
Trait
<?php
namespace Tests\Feature\Listeners\Concerns;
use Illuminate\Support\Facades\Event;
/**
* @property string listener
* @property string event
*/
trait HasListener
{
public function testBinding()
{
Event::fake();
Event::assertListening(
$this->event,
$this->listener
);
}
protected function eventFactory()
{
return new $this->event();
}
}
Example with airtime listener
Event:
<?php
namespace App\Events\Device;
use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Broadcasting\InteractsWithSockets;
class AirtimeRechargedEvent implements ShouldQueue
{
use Dispatchable;
use InteractsWithSockets;
use SerializesModels;
use Queueable;
public function __construct(public int $deviceId, public int $amount, public string $date, public string $duration)
{
}
public function broadcastOn()
{
return new PrivateChannel('channel-name');
}
}
Listener
<?php
namespace App\Listeners;
use App\Models\Device;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
use App\Events\Device\AirtimeRechargedEvent;
class CreateAirtimeTransaction implements ShouldQueue
{
use InteractsWithQueue;
public function handle(AirtimeRechargedEvent $event)
{
$device = Device::where('id', $event->deviceId)->firstOrFail();
$device->transactions()->create([
'label' => __('Airtime'),
'amount' => $event->amount * -1,
'created_at' => $event->date,
]);
}
}
Test
<?php
namespace Tests\Feature\Listeners\Device;
use Tests\TestCase;
use App\Models\Device;
use App\Models\Transaction;
use App\Listeners\CreateAirtimeTransaction;
use App\Events\Device\AirtimeRechargedEvent;
use Tests\Feature\Listeners\Concerns\HasListener;
class CreateAirtimeTransactionTest extends TestCase
{
use HasListener;
protected $listener = CreateAirtimeTransaction::class;
protected $event = AirtimeRechargedEvent::class;
public function testCreatesTransaction()
{
$device = Device::factory()->create();
$event = new AirtimeRechargedEvent($device->id, mt_rand(3000, 30000), now(), 30);
$listener = new CreateAirtimeTransaction();
$listener->handle($event);
$this->assertDatabaseHas(Transaction::class, [
'label' => 'Airtime',
'created_at' => $event->date,
'entity_id' => $device->id,
'entity_type' => $device->getMorphClass(),
'amount' => $event->amount * -1,
]);
}
}