---
title: "Testovanie — Workflows for Laravel | Roundly"
description: "Workflows::fake() zaznamenáva bez spúšťania — všetky dvojice asercií, zaznamenané operácie, passthrough, falošné kroky, InteractsWithWorkflows a Pest."
url: https://roundly-consulting.com/sk/open-source/navody/workflows-for-laravel/testovanie
language: sk
---

[Všetky balíky](https://roundly-consulting.com/sk/open-source.md)

[Workflows for Laravel](https://roundly-consulting.com/sk/open-source/navody/workflows-for-laravel.md)

# Testovanie

Workflows::fake() vymení manažéra za Testing\\WorkflowsFake za fasádou aj v kontajneri a vráti ho:

```php
use RoundlyConsulting\Workflows\Facades\Workflows;

$fake = Workflows::fake();   // swaps the facade root AND the container binding, so injected managers are faked

$this->post('/purchase-requests', [...]);

$fake->assertStarted('purchase-approval');
$fake->assertStarted('purchase-approval', fn (StartData $data) => $data->subject?->is($request), times: 1);
$fake->assertEmitted('purchase_request.submitted', fn (EmitData $data) => $data->payload['amount'] === '1200');
$fake->assertEdited('purchase-approval', fn (array $graph) => count($graph['nodes']) === 5);
$fake->assertFlagResolved('purchase-approval', node: 'notify', path: '/recipients');
$fake->assertTouched($request);
$fake->assertWoken(times: 2);
$fake->assertNothingRecorded();

$fake->recorded(WorkflowOperation::Signal);   // list<RecordedOperation{operation, context}>
```

- Len zaznamenáva: každé volanie, ktoré niečo mení — start, emit, signal, webhook, create, edit, discard, publish, retire, activate, restore, vyriešenie príznaku, obnova webhooku, import, sync, retry, cancel, touch, wake, prune — sa zaznamená a nespustí. Zaznamenajú sa aj volania cez trait HasWorkflows (touch po aktualizácii, zrušenie po zmazaní).
- Čítanie prechádza do skutočného úložiska: definície, katalóg, subjekty, validácia, porovnania, prehľady behov, exporty, skúšobné behy a zostavovanie grafu (Workflows::graph()).
- Rozoslanie emitu sa nespustí: overujte emit, nie spustenia, ktoré by vyvolal. Kľúč udalosti sa však najprv overí rovnakou kontrolou ako pri skutočnom emite: interný kľúč s @ alebo chybný kľúč (Order Placed) vyhodí InvalidEventKeyException a nič sa nezaznamená.
- Náhrady, nikdy null: pod fake metóda, ktorá niečo mení, vráti neuloženú náhradu (exists === false) vytvorenú cez nastavené triedy modelov — definíciu s kľúčom, názvom a rozsahom vlastníka z volania, koncept verzie s grafom, ImportResult s neuloženou definíciou a verziou. retry a cancel vrátia inštanciu, ktorú dostali. Spustenie vráti StartResult{instance: null, created: false, skipped: 'faked'}; emity, wake, prune a sync vrátia prázdne reporty; touch() a signal() vrátia 0. Overujte cez fake, nie cez databázu.

\->passthrough() zaznamenáva a zároveň spúšťa:

```php
$fake = Workflows::fake()->passthrough();   // record AND run
$fake->passthrough(false);                   // back to record-only
```

## Asercie

Každá zaznamenaná operácia má pozitívnu aj negatívnu aserciu (signály a skúšobné behy, ktoré sú interné alebo sa nezaznamenávajú, ich nemajú). Každá asercia je uvedená na fasáde a chybové správy uvádzajú kľúč alebo ULID inštancie:

| Operácia | Prejde, keď | Negatívna |
| --- | --- | --- |
| start | `assertStarted(string $key, ?Closure $where = null, ?int $times = null)` | `assertNotStarted($key), assertNothingStarted()` |
| emit | `assertEmitted(string $event, ?Closure $where = null)` | `assertNotEmitted($event), assertNothingEmitted()` |
| create | `assertCreated(string $key)` | `assertNotCreated($key)` |
| edit | `assertEdited(string $key, ?Closure $where = null)` | `assertNotEdited($key)` |
| publish | `assertPublished(string $key)` | `assertNotPublished($key)` |
| retire | `assertRetired(string $key)` | `assertNotRetired($key)` |
| activate | `assertActivated(string $key)` | `assertNotActivated($key)` |
| restore | `assertRestored(string $key)` | `assertNotRestored($key)` |
| discard draft | `assertDraftDiscarded(string $key)` | `assertDraftNotDiscarded($key)` |
| resolve flag | `assertFlagResolved(string $key, ?string $node = null, ?string $path = null)` | `assertNoFlagResolved($key)` |
| rotate webhook | `assertWebhookRotated(string $key)` | `assertWebhookNotRotated($key)` |
| import | `assertImported(?string $key = null)` | `assertNothingImported()` |
| sync | `assertSynced(?string $path = null, ?bool $publish = null)` | `assertNotSynced()` |
| inbound webhook | `assertWebhookReceived(string $hookId)` | `assertWebhookNotReceived($hookId)` |
| retry | `assertRetried(WorkflowInstance $instance)` | `assertNotRetried($instance)` |
| cancel | `assertCancelled(WorkflowInstance $instance)` | `assertNotCancelled($instance)` |
| touch | `assertTouched(?Model $subject = null)` | `assertNotTouched(?Model $subject = null)` |
| wake | `assertWoken(?int $times = null)` | `assertNotWoken()` |
| prune | `assertPruned()` | `assertNotPruned()` |
| anything | — | `assertNothingRecorded()` |

## Zaznamenané operácie

$fake->recorded(?WorkflowOperation $operation = null) vráti list<RecordedOperation> (operation, context) od najstaršej, pre vlastné asercie:

| WorkflowOperation | Zaznamená ho | Kľúče context |
| --- | --- | --- |
| `start` | start()->dispatch(), startWith(), for()->start(), definition()->start() | data (StartData), key |
| `emit` | emit(), emitWith(), for()->emit() | data (EmitData), event |
| `signal` | prepojenia (interné) | key, subject |
| `webhook` | triggers()->webhook(), prichádzajúca route | hook, payload |
| `create_definition` | definitions()->create() / createWith() | data, key |
| `restore_definition` | definitions()->restore() | key, owner |
| `sync` | definitions()->sync() | path, owner, publish, dry\_run |
| `edit` | definition()->edit() | definition, key, graph |
| `discard_draft` | definition()->discardDraft() | definition, key |
| `publish` | definition()->publish() | definition, key, by |
| `retire / activate / rotate_webhook` | definition()->retire() / activate() / rotateWebhook() | definition, key |
| `resolve_flag` | definition()->resolveFlag() | definition, key, node, path |
| `import` | versions()->import() | owner, key, payload |
| `retry` | instances()->retry() | instance |
| `cancel` | instances()->cancel(), for()->cancelAll(), hook HasWorkflows pri mazaní | instance, reason |
| `touch` | for()->touch(), hook HasWorkflows pri aktualizácii | subject |
| `wake` | wake() | now |
| `prune` | prune() | — |
| `dry_run` | nikdy sa nezaznamená | — |

## Spúšťanie workflowov v teste

```php
use RoundlyConsulting\Workflows\Testing\InteractsWithWorkflows;

uses(InteractsWithWorkflows::class);

it('escalates after two business days', function () {
    $this->travelTo(CarbonImmutable::parse('2026-11-06 15:00', 'Europe/Bratislava'));   // Friday
    $instance = Workflows::start('purchase-approval', $request, $team)->dispatch()->instance;

    $this->travelAndWake('P4D');    // move the clock, run workflows:wake, drain the queue

    $this->assertInstanceAt($instance, 'approve');
    $this->assertInstanceStatus($instance, InstanceStatus::Waiting);
});
```

| Pomocník | Čo robí |
| --- | --- |
| `runWorkflows(?string $connection)` | Spracúva front, kým nie je prázdny (≤ 1000 jobov); na sync nerobí nič. |
| `travelAndWake($by)` | 'P2D', 'PT1H', interval alebo absolútny okamih: posunie testovacie hodiny, prebudí a vyprázdni front. |
| `fakeStep(string $type, StepResult\|string\|Closure $result, ?array $outcomes = null)` | Určí, čo vráti každý uzol daného typu kroku. |
| `assertStepRan(string $type, ?int $times = null, ?Closure $where = null)` | Skriptovaný typ bežal (toľkokrát, vyhovujúc $where). |
| `assertStepNotRan(string $type)` | Skriptovaný typ nikdy nebežal. |
| `restoreSteps()` | Vráti skutočné typy (spustí sa samo na konci testu). |
| `assertInstanceAt($instance, $node)` | Aktuálny uzol. |
| `assertInstanceStatus($instance, InstanceStatus)` | Stav. |
| `assertPath($instance, list<string>)` | Cesta dokončených uzlov. |

S workflows.queue.enabled = off beží všetko priamo a runWorkflows() nemá čo robiť.

## Falošné kroky

Workflow otestujete od začiatku do konca bez jeho skutočných vedľajších efektov: určíte výsledok typu kroku a skutočný engine beží ďalej — zámky, čakania, eskalácie, záznam krokov aj autorizačný hook (zamietnutie vo vašich policy krok stále ukončí, takže test ho nikdy neobíde):

```php
uses(InteractsWithWorkflows::class);

$this->fakeStep('approval', 'rejected', outcomes: ['approved', 'rejected', 'expired']);  // approvals not installed: name its outcomes
$this->fakeStep('acme.create_task', fn (StepContext $c) => StepResult::complete(output: ['task_id' => 7]));

Workflows::start('purchase-approval', $request, $team)->dispatch();
$this->runWorkflows();

$this->assertStepRan('acme.create_task', times: 1, where: fn (StepContext $c) => $c->config()['title'] === 'Order');
```

- Reťazec je výsledok; StepResult sa vráti tak, ako je; closure ho vytvorí pri každom behu.
- Registrovaný typ si ponechá kľúč, názov, schému konfigurácie, výsledky, vedľajšie efekty, oprávnenie aj podporu eskalácií, takže validácia pri publikovaní aj autorizácia sa správajú ako v produkcii.
- Typ, ktorý nie je registrovaný (prepojený typ, ktorého balík chýba), potrebuje $outcomes; prijme akúkoľvek konfiguráciu (aj eskaláciu reassign, ktorá nič nerobí) a nemá vedľajšie efekty.
- Funguje so zapnutým aj vypnutým frontom. Skutočný typ sa vráti na konci testu.

## Pest expectations

Registrujú sa automaticky cez Pest plugin balíka; každá najprv obnoví inštanciu z databázy:

```php
expect($instance)->toBeWaitingAt('approve');
expect($instance)->toHaveCompletedVia(['start', 'approve', 'done']);
expect($instance)->toHaveFailedWith('no_approvers');
```

## Skúšobné behy v testoch

Skúšobný beh je lacný spôsob, ako overiť smerovanie bez vedľajších efektov:

```php
expect(Workflows::definition('purchase-approval', $team)->dryRun()->sample(['amount' => '60000'])->run()->nodes())
    ->toBe(['start', 'big', 'legal_cfo', 'notify_approved', 'done']);
```

Každý model má factory (Database\\Factories\\\*Factory), takže definície, verzie a inštancie pripravíte aj priamo.

## Prejavte 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 kryptomien](https://roundly-consulting.com/sk/podporte-nas.md)

Odoslaním daru súhlasíte s našimi [podmienkami prijímania darov](https://roundly-consulting.com/sk/podmienky-darov.md).

[Podporte našu open-source prácu (otvorí sa v novej karte)](https://donate.stripe.com/dRmeVe8FX5PF1Qd9pXcEw00) [Pridajte sa k nám na Patreone (otvorí sa v novej karte)](https://www.patreon.com/cw/roundly)

## 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.

[Ponuka do 48 hodín](https://roundly-consulting.com/sk/kontakt.md) [Prezrieť všetky balíky](https://roundly-consulting.com/sk/open-source/navody/workflows-for-laravel.md)
