Testovanie
Credits::fake() nahradí manažéra záznamníkom, ktorý nezapisuje žiadny záznam do knihy pohybov ani nespúšťa udalosti, a vráti ho. Náhrada platí pre fasádu, každý injektovaný CreditsManager, metódy modelu z HasCredits aj credits:modify:
use RoundlyConsulting\Credits\Facades\Credits;
$fake = Credits::fake();
// ... run the code under test ...
$fake->assertAdded($user, 100, bucket: 'points'); // amount and bucket optional
$fake->assertDeducted($user, 30); // the positive amount, as passed to deduct()
$fake->assertSet($user, 0); // the requested target
$fake->assertNothingAdded();
$fake->assertNothingDeducted();
$fake->assertNothingSet();
$fake->assertNothingModified(); // no add, deduct or set at all
$hold = $user->holdCredits(30);
$hold->capture(10);
$fake->assertHeld($user, 30); // amount and bucket optional
$fake->assertCaptured($user, 10);
$fake->assertNothingReleased();
$fake->assertNothingReleased(); // a partial capture is recorded as captured only
$fake->assertReset($user, 'free', '2026-11'); // after a reset; bucket and period optional
$fake->assertNothingHeld();
$fake->assertNothingCaptured();
$fake->assertNothingReset();- assertAdded($owner, ?$amount, ?$bucket) / assertNothingAdded() — pripísania podľa vlastníka a voliteľne sumy a skupiny.
- assertDeducted($owner, ?$amount, ?$bucket) / assertNothingDeducted() — odpisy podľa kladnej sumy, ako ju dostalo deduct() (odpisu PHP_INT_MIN, ktorého veľkosť sa do int nezmestí, zodpovedá len volanie bez $amount).
- assertSet($owner, ?$amount, ?$bucket) / assertNothingSet() — volania setTo() podľa požadovaného cieľa.
- assertNothingModified() — žiadne pripísanie, odpis ani nastavenie.
- assertHeld($owner, ?$amount, ?$bucket) / assertNothingHeld() — vytvorené rezervácie.
- assertCaptured($owner, ?$amount, ?$bucket) / assertNothingCaptured() — strhnutia podľa strhnutej sumy.
- assertReleased($owner, ?$amount, ?$bucket) / assertNothingReleased() — uvoľnenia podľa sumy rezervácie; čiastočné strhnutie sa zaznamená len ako strhnutie.
- assertReset($owner, ?$bucket, ?$period) / assertNothingReset() — obnovenia podľa skupiny a kľúča obdobia; obnovenie sa zaznamená ako reset, nie ako set.
Aj rezervácie a obnovenia bežia v pamäti. hold() vráti neuloženú CreditHold, uplatní skutočnú ochranu voči dostupnému zostatku fake a held() / available() spoja rezervácie fake so skutočnými riadkami credit_holds. capture() odpíše z knihy pohybov fake a release() označí rezerváciu ako uvoľnenú — obe fungujú aj na rezervácii uloženej pred nasadením fake, ktorej riadok zostane nedotknutý, no fake ju prestane započítavať. expireHolds() expiruje vlastné staré rezervácie fake a vráti ich počet. reset() je v pamäti idempotentné pre obdobie a rešpektuje riadky credit_resets, ktoré už sú v databáze.
Fake si vedie knihu pohybov v pamäti. Zostatky ju pripočítajú k skutočným záznamom vlastníka, takže pripísanie cez fake sa dá cez fake aj minúť. Ochrana pred prečerpaním platí ďalej: odpis, ktorý by skutočný manažér odmietol, vyhodí InsufficientCreditsException — s rovnakou správou aj $minimum — a nezaznamená sa. Rovnako platí 64-bitový limit: zmena, po ktorej by sa zostatok nezmestil, alebo setTo(), ktorého rozdiel sa nezmestí, vyhodí AmountOverflow z money s rovnakou správou ako skutočná kniha pohybov a nezaznamená sa; zostatok alebo súčet nad int64 ho vyhodí tiež. Okamih sa porovnáva ako presný časový bod s presnosťou na sekundy, rovnako ako skutočný created_at. setTo() sa zaznamená, aj keď zostatok už sedí.
Proti skutočnej databáze
Keď test overuje samotnú knihu pohybov, testujte proti skutočnej databáze s RefreshDatabase — publikovaná migrácia prebehne ako každá iná. Zapisujte cez verejné API a cez neho aj overujte:
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Event;
use RoundlyConsulting\Credits\Events\CreditsModified;
use RoundlyConsulting\Credits\Exceptions\InsufficientCreditsException;
uses(RefreshDatabase::class);
it('grants and spends credits', function () {
$user = User::factory()->create();
$user->modifyCredits(100, 'signup bonus');
$user->modifyCredits(-30, 'purchase', ['order_id' => 42]);
expect($user->creditsBalance())->toBe(70)
->and($user->hasCredits(50))->toBeTrue()
->and($user->credits()->count())->toBe(2);
});
it('refuses to overdraw and writes nothing', function () {
$user = User::factory()->create();
$user->modifyCredits(20);
expect(fn () => $user->modifyCredits(-50))
->toThrow(InsufficientCreditsException::class);
expect($user->creditsBalance())->toBe(20)
->and($user->credits()->count())->toBe(1);
});
it('announces every change', function () {
Event::fake([CreditsModified::class]);
$user = User::factory()->create();
$user->modifyCredits(10, 'welcome');
Event::assertDispatched(
CreditsModified::class,
fn (CreditsModified $event) => $event->balance === 10 && $event->creditable->is($user),
);
});Zostatky k dátumu
Historické zostatky určuje created_at, takže vám stačí cestovanie v čase z Laravelu:
it('reconstructs a past balance', function () {
$user = User::factory()->create();
$this->travelTo(now()->subWeek());
$user->modifyCredits(100);
$this->travelBack();
$user->modifyCredits(-40);
expect($user->creditsBalance(now()->subDays(3)))->toBe(100)
->and($user->creditsBalance())->toBe(60);
});Factory
Credit obsahuje factory na priame vytvorenie záznamov. K vlastníkovi ho pripojíte cez vzťah creditable:
use RoundlyConsulting\Credits\Models\Credit;
// The factory picks a random amount (-1000..1000) unless you set one.
Credit::factory()->for($user, 'creditable')->create(['amount' => 250, 'bucket' => 'promotional']);Factory vytvára nakonfigurovaný credits.model, takže factory() podtriedy vráti podtriedu, a záznam bez skupiny skončí v credits.default_bucket. Záznamy z factory obchádzajú ochranu pred prečerpaním a nespúšťajú CreditsModified — ak test overuje práve toto, použite modifyCredits().
Databázové enginy
SQLite potichu prijme uuid v celočíselnom stĺpci. Ak ste zmenili key_type alebo primary_key_type, spúšťajte aspoň časť testov na engine, na ktorý nasadzujete.
Testy samotného balíka
Testy balíka spustíte z jeho repozitára:
composer testPrejavte lásku k open source
Tento balík je zadarmo pod licenciou MIT. Ak vám šetrí čas, jednorazový príspevok alebo členstvo na Patreone nám pomôže ho ďalej udržiavať, testovať a dokumentovať.
Ďalšie spôsoby podpory vrátane kryptomienOdoslaním daru súhlasíte s našimi podmienkami prijímania darov.
Chcete to zabudovať do svojho produktu?
Naše balíky integrujeme do zákazkových Laravel a AI riešení. Napíšte nám, na čom pracujete, a ozveme sa do 48 hodín.