---
title: "Testovanie — Kubernetes API for Laravel | Roundly"
description: "Kubernetes::fake() nahradí apiserver verziou v pamäti — naplňte objekty, nasimulujte logy a exec a overte vytvorenie, úpravy, škálovanie či zmazanie."
url: https://roundly-consulting.com/sk/open-source/navody/kubernetes-api-for-laravel/testovanie
language: sk
---

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

[Kubernetes API for Laravel](https://roundly-consulting.com/sk/open-source/navody/kubernetes-api-for-laravel.md)

# Testovanie

Kubernetes::fake() vymení manager — za fasádou aj v kontajneri, takže ho dostanú aj injektované KubernetesManagery — za apiserver v pamäti a vráti objekt KubernetesFake. Nič neodíde do siete. Každý klaster z managera (pomenovaný, predvolený, ad-hoc, obmedzený na namespace) aj každý naň naviazaný zdroj komunikuje s fake, ktorý každú požiadavku zaznamená:

```php
use RoundlyConsulting\KubernetesApi\Facades\Kubernetes;
use RoundlyConsulting\KubernetesApi\Resources\Deployment;

it('scales checkout for the sale', function () {
    $fake = Kubernetes::fake()->seed(Deployment::class, [
        ['metadata' => ['name' => 'checkout', 'namespace' => 'shop'], 'spec' => ['replicas' => 2]],
    ]);

    app(PrepareForSale::class)();   // your code: Kubernetes::namespace('shop')->deployments()->setName('checkout')->scale(10)

    $fake->assertScaled(Deployment::class, 'checkout', 10);
    $fake->assertNothingDeleted();
});
```

Fake sa správa ako malý apiserver: naplnené aj vytvorené objekty môžete vypisovať (selektory podľa labelov a polí, limit a continue, všetky namespace), hľadať, upravovať, patchovať, škálovať a mazať. Chýbajúci objekt vráti skutočnú 404, duplicitné vytvorenie či zastaraná resourceVersion skutočnú 409 a požiadavky s dryRun() sa overia bez uloženia. Rovnako ako na apiserveri dostane create s metadata.generateName a bez mena meno <generateName> s piatimi náhodnými znakmi, update(), updateOrCreate() a patch() zachovajú uložený status (status v tele sa ignoruje) a limit 0 alebo menej znamená bez limitu. Každé meno klastra má vlastné úložisko; ad-hoc klastre zdieľajú úložisko predvoleného klastra.

## Príprava

| Príprava | Účinok |
| --- | --- |
| `seed(string $resource, array $items, ?string $cluster = null)` | Vloží objekty — manifesty alebo objekty zdrojov — na klaster. $resource je trieda alebo zaregistrované meno ('pods'). Položka v namespace bez metadata.namespace skončí v predvolenom namespace cieľového klastra, kam by ju dalo create() cez tento klaster; neznáme meno klastra vyhodí ClusterNotFoundException. |
| `seedLogs(string $pod, string $logs, string $namespace = 'default', ?string $cluster = null)` | Čo vrátia logs() a streamLogs(); tailLines sa rešpektuje. |
| `stubExec(ExecResult\|Closure $result)` | Čo vráti exec() — pevný výsledok alebo closure, ktorá dostane RecordedRequest. Predvolene prázdny výstup a návratový kód 0. |
| `stubVersion(VersionInfo\|string $version)` | Čo nahlási version(); predvolene v1.34.0. |
| `unreachable(bool $unreachable = true)` | Každá požiadavka vyhodí ConnectionException; ping() vráti false. |
| `recorded(?Closure $filter = null)` | Každý RecordedRequest, ktorý fake dostal, voliteľne filtrovaný. |

Logy, exec aj sledovanie zmien fungujú aj proti fake — watch pošle jednu udalosť ADDED za každý vyhovujúci objekt:

```php
use RoundlyConsulting\KubernetesApi\DataTransferObjects\ExecResult;
use RoundlyConsulting\KubernetesApi\DataTransferObjects\PodLogOptions;
use RoundlyConsulting\KubernetesApi\DataTransferObjects\WatchEvent;
use RoundlyConsulting\KubernetesApi\Facades\Kubernetes;

it('serves seeded pods, logs and exec', function (): void {
    $fake = Kubernetes::fake()
        ->seed('pods', [
            ['metadata' => ['name' => 'api', 'namespace' => 'shop', 'labels' => ['app' => 'web']]],
        ])
        ->seedLogs('api', "one\ntwo\nthree\n", namespace: 'shop')
        ->stubExec(new ExecResult("hi\n", '', 0));

    $pod = Kubernetes::namespace('shop')->pods()->setName('api');

    expect($pod->logs(new PodLogOptions(tailLines: 2)))->toBe("two\nthree")
        ->and(iterator_to_array($pod->streamLogs(), false))->toBe(['one', 'two', 'three'])
        ->and($pod->exec(['sh', '-c', 'echo hi'])->stdout)->toBe("hi\n");

    // A watch streams one ADDED event per matching object
    $events = [];
    Kubernetes::namespace('shop')->pods()->whereLabel('app', 'web')
        ->watch(function (WatchEvent $event) use (&$events): void {
            $events[] = $event;
        });

    expect($events)->toHaveCount(1)
        ->and($events[0]->isAdded())->toBeTrue();

    $fake->assertExecuted('api', ['sh', '-c', 'echo hi']);
});
```

## Overenia

| Overenie · variant „nič“ | Príklad | Prejde, keď |
| --- | --- | --- |
| `assertSent(Closure) · assertNothingSent()` | `$fake->assertSent(fn (RecordedRequest $r): bool => $r->cluster === 'production')` | Niektorá požiadavka vyhovuje — alebo sa neodoslala žiadna. |
| `assertCreated($resource, $constraint = null) · assertNothingCreated()` | `$fake->assertCreated('configMaps', 'settings')` | Zdroj sa vytvoril — voliteľne s týmto menom alebo podľa closure. Za vytvorenie sa počíta aj žiadosť o token service accountu. |
| `assertUpdated($resource, $constraint = null) · assertNothingUpdated()` | `$fake->assertUpdated(Deployment::class, 'checkout')` | Celá úprava — update() alebo updateOrCreate() nad existujúcim objektom. |
| `assertPatched($resource, $constraint = null) · assertNothingPatched()` | `$fake->assertPatched('deployments', 'checkout')` | Akýkoľvek PATCH — vrátane patch(), scale() a rolloutRestart(). |
| `assertScaled($resource, $name, ?int $replicas = null) · assertNothingScaled()` | `$fake->assertScaled(Deployment::class, 'checkout', 10)` | Volanie scale() — ak je zadaný počet replík, presne naň. |
| `assertRestarted($resource, $name) · assertNothingRestarted()` | `$fake->assertRestarted(Deployment::class, 'checkout')` | Volanie rolloutRestart(). |
| `assertDeleted($resource, $constraint = null) · assertNothingDeleted()` | `$fake->assertDeleted('pods', 'api')` | Zmazanie. |
| `assertExecuted(string $pod, ?array $command = null) · assertNothingExecuted()` | `$fake->assertExecuted('api', ['sh', '-c', 'echo hi'])` | exec() v danom pode — ak je zadaný príkaz, presne tento. |
| `assertTokenRequested(string $serviceAccount, ?string $namespace = null, ?int $expirationSeconds = null) · assertNoTokenRequested()` | `$fake->assertTokenRequested('deployer', 'ci', 900)` | requestToken() pre daný service account — ak sú zadané, aj v tomto namespace a s touto životnosťou. |

$resource je trieda zdroja alebo zaregistrované meno ('pods'); obmedzením je meno objektu alebo closure, ktorá dostane RecordedRequest. Požiadavky v režime dry-run sa zaznamenajú, no overenia zmien nikdy nesplnia:

```php
use RoundlyConsulting\KubernetesApi\Facades\Kubernetes;
use RoundlyConsulting\KubernetesApi\Resources\Deployment;
use RoundlyConsulting\KubernetesApi\Testing\RecordedRequest;

$fake->assertCreated('configMaps', fn (RecordedRequest $request): bool => $request->namespace === 'shop'
    && $request->input('data.FEATURE_X') === 'on');

// Everything the fake received, optionally filtered
$dryRuns = $fake->recorded(fn (RecordedRequest $request): bool => $request->isDryRun());

// The assertions are also callable on the facade once it is faked
Kubernetes::assertScaled(Deployment::class, 'checkout', 10);
```

RecordedRequest nesie cluster (pri ad-hoc klastroch null), method, path, verb, apiVersion, plural, namespace, name, subresource, query, dekódované body, contentType a pri exec command a container — a k tomu isDryRun(), input('spec.replicas') a isRolloutRestart().

## Tokeny service accountov

Žiadosť o token sa zaznamená ako vytvorenie na subresource token s name nastaveným na service account — assertCreated('serviceAccounts', 'deployer') ju preto zachytí a assertNothingCreated() na nej zlyhá. Fake odpovie 404, ak service account v danom klastri a namespace nebol naplnený; inak deterministickým tokenom fake-token-{namespace}.{name}.{n} (n počíta žiadosti o token v rámci fake od 1), expiráciou o expirationSeconds od aktuálneho času (predvolene 3600 — čas zmrazíte cez Carbon::setTestNow()) a vráti späť požadované audiencie (predvolene https://kubernetes.default.svc.cluster.local) aj prípadnú väzbu:

```php
use Illuminate\Support\Carbon;
use RoundlyConsulting\KubernetesApi\Facades\Kubernetes;

Carbon::setTestNow('2026-10-10T12:00:00Z');

$fake = Kubernetes::fake()->seed('serviceAccounts', [
    ['metadata' => ['name' => 'deployer', 'namespace' => 'ci']],
]);

$token = Kubernetes::namespace('ci')->serviceAccounts()->setName('deployer')->requestToken(900);

$token->token;                              // 'fake-token-ci.deployer.1'
$token->expiresAt->toIso8601ZuluString();   // '2026-10-10T12:15:00Z'

$fake->assertTokenRequested('deployer', 'ci', 900);
Kubernetes::assertTokenRequested('deployer');   // through the facade
$fake->assertCreated('serviceAccounts', 'deployer');   // a token request is a create too
```

## Čo fake nerobí

- Klastre zaregistrované pred fake() zostávajú a ich definície sa naďalej spúšťajú; pod fake však fromKubeConfig(), inCluster() ani zdroje kubeconfig a in-cluster v konfigurácii nikdy nečítajú prihlasovacie údaje.
- Rovnako ako apiserver odpovie fake chybou 422 na server-side apply bez fieldManager alebo na force pri inom type patchu.
- Patche strategic-merge a server-side apply sa aplikujú ako JSON merge patche a logy aj exec odpovedajú pre akýkoľvek pod.
- Mazanie je okamžité. Skutočné delete() je mazanie typu Foreground: apiserver objekt ponechá, označený deletionTimestamp, kým nezmiznú jeho závislé objekty (pod navyše čaká na uplynutie grace period), takže GET naďalej odpovedá a opätovné vytvorenie dostane 409. Fake objekt odstráni hneď — postupnosť zmazať a znova vytvoriť testujte proti skutočnému klastru.
- status sa zachová pri každom type vrátane vlastných zdrojov bez subresource status, kde by skutočný apiserver uložil to, čo pošlete.
- Klastre vytvorené mimo managera — new Cluster, Cluster::make() — fake obídu.

## Kontrola na úrovni HTTP cez Http::fake()

Http::fake() naďalej funguje — zachytí skutočný HTTP transport, keď chcete overiť presné URL a hlavičky. V takých testoch vypnite obmedzenie požiadaviek na strane klienta, aby veľká sada testov nikdy nečakala na rozpočet v pamäti:

```php
use Illuminate\Http\Client\Request;
use Illuminate\Support\Facades\Http;
use RoundlyConsulting\KubernetesApi\Cluster;
use RoundlyConsulting\KubernetesApi\Facades\Kubernetes;

beforeEach(function (): void {
    config(['kubernetes.rate_limits.enabled' => false]);

    Kubernetes::registerCluster('testing', fn (Cluster $cluster): Cluster => $cluster
        ->url('https://k8s.example.com')
        ->withToken('fake-token')
        ->withManagerName('shop-tests'));

    Http::preventStrayRequests();
});

it('lists the deployments in a namespace', function (): void {
    Http::fake([
        '*' => Http::response([
            'items' => [
                ['metadata' => ['name' => 'checkout', 'namespace' => 'shop']],
            ],
        ]),
    ]);

    $deployments = Kubernetes::cluster('testing')->deployments()->setNamespace('shop')->get();

    expect($deployments)->toHaveCount(1)
        ->and($deployments->first()->getName())->toBe('checkout');

    Http::assertSent(fn (Request $request): bool => $request->method() === 'GET'
        && $request->url() === 'https://k8s.example.com/apis/apps/v1/namespaces/shop/deployments?pretty=1'
        && $request->hasHeader('Authorization', 'Bearer fake-token'));
});
```

exec() otvára vlastný WebSocket namiesto HTTP klienta, takže ho Http::fake() nezachytí — nasimulujte ho cez Kubernetes::fake() alebo overte presnú cestu subresource a dotaz, ktoré pre príkaz zostaví getExecPath():

```php
$path = Kubernetes::cluster('testing')->pods()
    ->setNamespace('shop')
    ->withName('checkout-5f7c9d8b6-x2kqj')
    ->getExecPath(['php', 'artisan', 'cache:clear'], container: 'app');

expect($path)
    ->toContain('/api/v1/namespaces/shop/pods/checkout-5f7c9d8b6-x2kqj/exec?')
    ->toContain('container=app')
    ->toContain('command=cache%3Aclear');
```

## Testy samotného balíka

Predvolená sada testov balíka je úplne simulovaná a klaster nepotrebuje. Samostatná, voliteľná sada spúšťa skutočné CRUD, exec a výpisy proti lokálnemu klastru OrbStack; je chránená tak, že sa spustí len pri K8S\_INTEGRATION=1, kontexte presne orbstack a hoste apiservera na loopbacku alebo \*.orb.local, a každý beh používa dočasný namespace, ktorý sa potom zmaže:

```bash
composer test

# Opt-in live suite against a local OrbStack cluster
K8S_INTEGRATION=1 composer test-integration
```

| Premenná | Predvolené | Účel |
| --- | --- | --- |
| `K8S_INTEGRATION` | nenastavená | Hodnota 1 (alebo true) sadu zapne; bez nej sa každý integračný test preskočí. |
| `K8S_INTEGRATION_CONTEXT` | orbstack | Kontext kubeconfigu, z ktorého sada berie prihlasovacie údaje. Musí to byť orbstack. |
| `K8S_INTEGRATION_NAMESPACE` | náhodný k8s-it-… | Prepíše dočasný namespace, ktorý sada vytvorí a po sebe zmaže. |

## 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/kubernetes-api-for-laravel.md)
