Enhets-testing for WordPress: Guide for utviklere i 2026

Enhets-testing for WordPress: Guide for utviklere i 2026

Sist verifisert: 12. oktober 2026
15 min lesetid
Veiledning
Full-stack-utvikler

Automatisert testing i WordPress-utvikling har beveget seg fra å være et valgfritt tiltak til å bli et ufravikelig kvalitetskrav for produksjonssatte systemer. I mange år stolte utviklingsteam primært på manuell testing: oppdatere nettleservinduet, klikke seg gjennom skjemaer i wp-admin og inspisere databasen etter kodejusteringer. Denne tilnærmingen bryter sammen så snart et prosjekt vokser og integrerer egendefinerte innleggstyper, REST-API-endepunkter, taksonomier og sammensatt forretningslogikk. En liten regresjon i en lite brukt filter-hook kan forbli uoppdaget helt til ordreflyten feiler i det virkelige driftsmiljøet.

En moderne teststrategi organiserer kvalitetssikringen i adskilte lag. Nederst finner vi enhetstester som kjører på millisekunder uten å laste inn WordPress-kjernen eller koble til databasen. Over disse ligger integrasjonstester som validerer samspillet med MySQL-skjemaet og kjernefunksjonene. Øverst opererer akseptanse- og nettlesertester. Å bygge en stabil og vedlikeholdsvennlig testsuite i 2026 forutsetter et bevisst skille mellom disse nivåene og riktig bruk av verktøy som PHPUnit 10 og 11, Brain Monkey, Mockery, wp-browser og wp-env.

#Grunnleggende prinsipper for enhetstesting og full isolasjon

En ren enhetstest evaluerer en enkeltstående funksjon eller klassemetode i fullstendig isolasjon fra eksterne avhengigheter. I WordPress representerer denne isolasjonen en arkitektonisk utfordring, ettersom tradisjonell kodebasekonvensjon er tett sammenvevd med globale kjernefunksjoner som get_post(), get_option() og apply_filters(). Hvis slike funksjoner kalles direkte i doménelogikken, fører kjøring i et standard PHP-miljø uten WordPress-kjernefiler umiddelbart til fatale feilmeldinger om udefinerte funksjoner.

Å kjøre enhetstester ved å laste inn hele WordPress-miljøet og databasen for hvert enkelt testtilfelle ødelegger utviklernes tilbakemeldingssløyfe. Dersom testsuiten bruker ti minutter på å fullføre, slutter utviklerne å kjøre den før endringer sendes til versjonskontroll. Ekte enhetstester må fullføres i løpet av få sekunder. For å oppnå denne hastigheten må forretningslogikken frikobles fra databasen og globale variabler ved hjelp av test-doubles og funksjons-mocks.

Testbar kode i WordPress begynner med programvarearkitekturen. Klasser må skille doménelogikk fra CMS-spesifikke API-er. I stedet for å kalle update_post_meta() midt i en rabattberegning, bør verdiene sendes som parametere til en ren metode som returnerer det beregnede resultatet. Interaksjoner med WordPress-kjernefunksjoner kapsles inn i adaptere eller tjenesteklasser, slik at testrammeverk enkelt kan simulere oppførselen.

#Brain Monkey for hook- og filter-verifisering

Brain Monkey er standardverktøyet for isolert enhetstesting av WordPress-kode. Verktøyet er utviklet spesifikt for å etterligne WordPress-funksjoner, handlinger og filtre uten å laste inn WordPress-kjernen, noe som muliggjør lynraske testkjøringer.

Brain Monkey avskjærer kall til add_action(), add_filter(), do_action() og apply_filters(). Dette gjør at tester kan verifisere presist om en hook er registrert med forventet prioritet og argumentantall, eller om et egendefinert filter transformerer datasettet korrekt.

<?php
declare(strict_types=1);

namespace WPPoland\Tests\Unit;

use Brain\Monkey;
use Brain\Monkey\Actions;
use Brain\Monkey\Filters;
use Brain\Monkey\Functions;
use PHPUnit\Framework\TestCase;
use WPPoland\Billing\InvoiceManager;

final class InvoiceManagerTest extends TestCase {
    protected function setUp(): void {
        parent::setUp();
        Monkey\setUp();
    }

    protected function tearDown(): void {
        Monkey\tearDown();
        parent::tearDown();
    }

    public function test_invoice_creation_triggers_expected_action(): void {
        Actions\expectDone('wppoland_invoice_generated')
            ->once()
            ->with(1042, 599.00);

        Functions\expect('get_option')
            ->with('currency_code', 'EUR')
            ->andReturn('EUR');

        $manager = new InvoiceManager();
        $invoiceId = $manager->createInvoice(1042, 599.00);

        $this->assertSame(1042, $invoiceId);
    }

    public function test_filter_modifies_invoice_total(): void {
        Filters\expectApplied('wppoland_invoice_discount')
            ->once()
            ->with(500.00, 1042)
            ->andReturn(450.00);

        $manager = new InvoiceManager();
        $finalAmount = $manager->applyDiscounts(500.00, 1042);

        $this->assertSame(450.00, $finalAmount);
    }
}

I denne testen eksisterer verken WordPress eller en database. Brain Monkey håndterer livssyklusen til hooks fullstendig i minnet. Ved å kalle Monkey\setUp() i setUp() og Monkey\tearDown() i tearDown() tilbakestilles mock-tilstanden mellom testene, noe som hindrer utilsiktet tilstandsoverføring.

#Mockery for komplekse objektavhengigheter

Brain Monkey fungerer sømløst sammen med Mockery for å etterligne objekter og grensesnitt ved siden av funksjons-mocks. I avanserte utvidelser samhandler klasser med databasedepoter, HTTP-klienter og eksterne betalingsporter. Mockery gjør det mulig å bygge test-doubles som etterligner komplekse grensesnitt og verifiserer metodekall.

<?php
declare(strict_types=1);

namespace WPPoland\Tests\Unit;

use Mockery;
use Mockery\Adapter\Phpunit\MockeryPHPUnitIntegration;
use PHPUnit\Framework\TestCase;
use WPPoland\Gateways\PaymentClientInterface;
use WPPoland\Orders\OrderProcessor;

final class OrderProcessorTest extends TestCase {
    use MockeryPHPUnitIntegration;

    public function test_order_processes_payment_successfully(): void {
        $paymentClient = Mockery::mock(PaymentClientInterface::class);
        $paymentClient->shouldReceive('charge')
            ->once()
            ->with(120.50, 'EUR')
            ->andReturn((object) ['status' => 'success', 'transaction_id' => 'tx_9981']);

        $processor = new OrderProcessor($paymentClient);
        $result = $processor->process(101, 120.50, 'EUR');

        $this->assertTrue($result->isSuccessful());
        $this->assertSame('tx_9981', $result->getTransactionId());
    }
}

Ved å ta i bruk MockeryPHPUnitIntegration integreres Mockerys forventninger automatisk i PHPUnits totale assertion-teller, og uoppfylte krav vil umiddelbart markere testen som feilet.

#Avanserte Mockery-mønstre i WordPress-arkitektur

I krevende bedriftsarkitekturer benyttes ofte designmønstre som service-containere og lagringsabstraksjoner. Når man tester en integrasjon som synkroniserer varelager mot et eksternt API, kan ikke testene sende faktiske nettverkskall. Mockery gir fleksible metoder for å modellere nye forsøk og unntakshåndtering.

<?php
declare(strict_types=1);

namespace WPPoland\Tests\Unit;

use Mockery;
use Mockery\Adapter\Phpunit\MockeryPHPUnitIntegration;
use PHPUnit\Framework\TestCase;
use WPPoland\Http\ApiClientInterface;
use WPPoland\Sync\InventorySynchronizer;
use RuntimeException;

final class InventorySynchronizerTest extends TestCase {
    use MockeryPHPUnitIntegration;

    public function test_sync_retries_on_network_timeout(): void {
        $apiClient = Mockery::mock(ApiClientInterface::class);

        $apiClient->shouldReceive('fetchProductStock')
            ->twice()
            ->with('SKU-9901')
            ->andReturnValues([
                new RuntimeException('Connection timed out'),
                ['sku' => 'SKU-9901', 'stock' => 45],
            ]);

        $synchronizer = new InventorySynchronizer($apiClient);
        $stock = $synchronizer->syncProduct('SKU-9901');

        $this->assertSame(45, $stock);
    }

    public function test_sync_throws_exception_after_exhausting_retries(): void {
        $apiClient = Mockery::mock(ApiClientInterface::class);
        $apiClient->shouldReceive('fetchProductStock')
            ->times(3)
            ->with('SKU-9901')
            ->andThrow(new RuntimeException('Fatal gateway outage'));

        $this->expectException(RuntimeException::class);
        $this->expectExceptionMessage('Fatal gateway outage');

        $synchronizer = new InventorySynchronizer($apiClient, 3);
        $synchronizer->syncProduct('SKU-9901');
    }
}

Dette mønsteret sikrer at nettverksfeil og hastighetsbegrensninger håndteres forutsigbart uten avhengighet til eksterne servere under testkjøring.

#Integrasjonstesting med klassen WP_UnitTestCase

Mens enhetstester verifiserer isolert forretningslogikk, skal koden til syvende og sist kjøre i WordPress. Integrasjonstester kontrollerer hvordan databaseforespørsler via WP_Query, egendefinerte innleggstyper, brukertilganger og innstillinger fungerer mot en reell MySQL-database.

WordPress’ offisielle testsuite leverer klassen WP_UnitTestCase, som arver fra PHPUnits basistestklasse. WP_UnitTestCase starter opp hele WordPress-miljøet og kobler til en dedikert testdatabase. Den inneholder også fabrikker (factories) som gjør det enkelt å opprette testdata for innlegg, taksonomier og brukere.

<?php
declare(strict_types=1);

namespace WPPoland\Tests\Integration;

use WP_UnitTestCase;
use WP_Query;

final class CustomQueryTest extends WP_UnitTestCase {
    public function test_query_returns_published_enterprise_projects(): void {
        $postId = $this->factory->post->create([
            'post_type'   => 'portfolio',
            'post_status' => 'publish',
            'post_title'  => 'Enterprise Migration',
        ]);

        $draftId = $this->factory->post->create([
            'post_type'   => 'portfolio',
            'post_status' => 'draft',
            'post_title'  => 'Draft Migration',
        ]);

        $query = new WP_Query([
            'post_type'      => 'portfolio',
            'post_status'    => 'publish',
            'posts_per_page' => 10,
        ]);

        $postIds = wp_list_pluck($query->posts, 'ID');

        $this->assertContains($postId, $postIds);
        $this->assertNotContains($draftId, $postIds);
    }
}

Bruk av $this->factory forenkler genereringen av konsistente testdata betraktelig og sørger for at relasjonelle koblinger opprettes korrekt.

#Tilbakerulling av databasetransaksjoner i integrasjonstester

Integrasjonstester med mange skriveoperasjoner kan bli svært trege dersom databasetabellene må tømmes og opprettes på nytt mellom hver testmetode. I tidlige testoppsett kjørte suiten gjentatte DROP TABLE-kommandoer, noe som førte til kjøretider på mange minutter.

Klassen WP_UnitTestCase løser dette gjennom transaksjonsstyring i MySQL. Før en testmetode utføres, starter testkjørereren en MySQL-transaksjon. Testen utfører sine operasjoner, lagrer innlegg og endrer alternativer. I opprydningsfasen (tearDown) utfører kjørereren en ROLLBACK-kommando i stedet for å slette radene manuelt.

Denne tilbakerullingen gir klare gevinster:

  • Hver testmetode starter med en ren og forutsigbar databasetilstand.
  • Kjørehastigheten forblir høy ettersom endringer forkastes direkte i minnet på MySQL-serveren.
  • Auto-increment-verdier for primærnøkler fortsetter å øke som i et ordinært produksjonsmiljø.

På moderne serveroppsett med MySQL 8.0 eller MariaDB 10.11 og InnoDB-tabeller gir transaksjonsrullinger et pålitelig og effektivt testfundament.

#Standardiserte lokale testmiljøer med wp-env

Tidligere krevde oppsett av et lokalt testmiljø omfattende bash-skript, lokale databasetjenester og manuelle uthentinger fra Subversion. Det offisielle verktøyet @wordpress/env (wp-env) standardiserer hele prosessen ved hjelp av Docker-containere.

Konfigurasjonsfilen .wp-env.json i rotmappen definerer miljøet:

{
  "core": "WordPress/WordPress#master",
  "plugins": [ "." ],
  "config": {
    "WP_DEBUG": true,
    "SCRIPT_DEBUG": true
  },
  "env": {
    "tests": {
      "config": {
        "WP_DEBUG": true
      }
    }
  }
}

Kommandoen wp-env start starter containerne for webserver og MySQL. For å kjøre integrasjonstestene i den dedikerte testcontaineren kjøres:

npx wp-env run tests-cli vendor/bin/phpunit

wp-env oppretter en separat testdatabase, noe som sikrer at lokale utviklingsdata aldri påvirkes under testkjøringer.

#Testing av REST-API-endepunkter og skjemavalidering

Egendefinerte REST-API-endepunkter er ryggraden i moderne hodeløse løsninger, mobilapper og dynamiske blokkgrensesnitt. Integrasjonstester for WP_REST_Controller sikrer at ruter, tilgangsrettigheter, JSON-skjemavalidering og HTTP-statuskoder fungerer som forutsatt.

WP_UnitTestCase samhandler direkte med kjernens interne WP_REST_Server. Forespørsler behandles internt i prosessen uten nettverksforsinkelser.

<?php
declare(strict_types=1);

namespace WPPoland\Tests\Integration;

use WP_UnitTestCase;
use WP_REST_Request;
use WP_REST_Server;

final class OrdersEndpointTest extends WP_UnitTestCase {
    private WP_REST_Server $server;

    protected function setUp(): void {
        parent::setUp();
        global $wp_rest_server;
        $this->server = $wp_rest_server = new WP_REST_Server();
        do_action('rest_api_init');
    }

    public function test_unauthenticated_request_is_rejected(): void {
        $request = new WP_REST_Request('POST', '/wppoland/v1/orders');
        $request->set_header('content-type', 'application/json');
        $request->set_body(wp_json_encode(['item_id' => 42]));

        $response = $this->server->dispatch($request);

        $this->assertSame(401, $response->get_status());
        $data = $response->get_data();
        $this->assertSame('rest_cannot_create', $data['code']);
    }

    public function test_authorized_user_creates_order_successfully(): void {
        $adminId = $this->factory->user->create(['role' => 'administrator']);
        wp_set_current_user($adminId);

        $request = new WP_REST_Request('POST', '/wppoland/v1/orders');
        $request->set_header('content-type', 'application/json');
        $request->set_body(wp_json_encode([
            'item_id'  => 42,
            'quantity' => 2,
            'amount'   => 199.50,
        ]));

        $response = $this->server->dispatch($request);

        $this->assertSame(201, $response->get_status());
        $data = $response->get_data();
        $this->assertArrayHasKey('order_id', $data);
        $this->assertSame(199.50, $data['amount']);
    }
}

Ved å teste REST-kontrollere grundig unngår man at ugyldige data når lagringslaget.

#Testing av databaseskjema og tabellmigreringer

Mange avanserte utvidelser benytter egne databasetabeller til revisjonslogger eller asynkrone oppgavekøer. Å sikre at tabellopprettelse, kolonneoppdateringer og indekseringer skjer uten datatap forutsetter dedikerte migreringstester.

I slike tester bekrefter man at dbDelta() fullfører uten feil, og at skriptene håndterer skjemaendringer atomisk.

<?php
declare(strict_types=1);

namespace WPPoland\Tests\Integration;

use WP_UnitTestCase;
use WPPoland\Database\MigrationManager;

final class MigrationManagerTest extends WP_UnitTestCase {
    public function test_migration_creates_custom_audit_table(): void {
        global $wpdb;
        $tableName = $wpdb->prefix . 'wppoland_audit_log';

        $wpdb->query("DROP TABLE IF EXISTS {$tableName}");

        $manager = new MigrationManager();
        $manager->runMigrations();

        $tableExists = $wpdb->get_var(
            $wpdb->prepare("SHOW TABLES LIKE %s", $tableName)
        );

        $this->assertSame($tableName, $tableExists);

        $indices = $wpdb->get_results("SHOW INDEX FROM {$tableName}", ARRAY_A);
        $indexNames = wp_list_pluck($indices, 'Key_name');

        $this->assertContains('idx_created_at', $indexNames);
    }
}

Dette forhindrer kostbare feil under oppgraderinger i driftsmiljøet.

#Akseptansetesting og nettleserautomatisering med wp-browser

Enhets- og integrasjonstester kjører i kommandolinjen, men enkelte egenskaper krever visuell testing i et reelt nettlesermiljø. Å verifisere at innstillinger i kontrollpanelet lagres riktig, eller at klientskjemaer validerer data, krever akseptansetester.

wp-browser kobler Codeception-rammeverket sammen med WordPress og tilbyr spesialiserte moduler:

  • WPDb: Muliggjør direkte inspeksjon og manipulering av databasen uten PHPUnit-oppstart.
  • WPLoader: Laster inn WordPress-kjernen i funksjonelle tester.
  • WPWebDriver: Styrer nettlesere som Chrome eller Firefox via Selenium eller Chromedriver.
<?php
// Akseptansetest med wp-browser
$I = new AcceptanceTester($scenario);
$I->wantTo('verify admin settings save correctly');
$I->loginAsAdmin();
$I->amOnAdminPage('admin.php?page=wppoland-settings');
$I->fillField('#api_key', 'live_secret_key_12345');
$I->click('Save Changes');
$I->see('Settings saved successfully', '.notice-success');
$I->seeInDatabase('wp_options', [
    'option_name'  => 'wppoland_api_key',
    'option_value' => 'live_secret_key_12345',
]);

Akseptansetester sikrer at alle deler av teknologistakken fungerer sammen før koden rulles ut.

#Testing av Gutenberg-blokker med Jest og Playwright

Moderne WordPress-prosjekter kombinerer et PHP-backend med et React-basert redigeringsgrensesnitt. Derfor må teststrategien omfatte verktøy fra JavaScript-verdenen.

Pakken @wordpress/scripts tilbyr et enhetlig testoppsett. Rent JavaScript-logikk og attributt-transformasjoner testes med Jest, mens brukerinteraksjoner i blokkeditoren automatiseres ved hjelp av Playwright.

#Testing av blokkattributter og migreringer med Jest

Jest kjører raske enhetstester for React-komponenter og hjelpefunksjoner. Rensing av attributter og støtte for eldre blokkversjoner verifiseres i Jest.

import { cleanInvoiceAttributes } from '../src/utils';

describe('Invoice block attribute sanitization', () => {
    it('sets default tax rate when invalid rate is provided', () => {
        const input = { taxRate: -5, clientName: 'WPPoland' };
        const result = cleanInvoiceAttributes(input);
        expect(result.taxRate).toBe(23);
        expect(result.clientName).toBe('WPPoland');
    });

    it('preserves valid vat rates', () => {
        const input = { taxRate: 8, clientName: 'WPPoland' };
        const result = cleanInvoiceAttributes(input);
        expect(result.taxRate).toBe(8);
    });
});

#End-to-end-testing i editoren med Playwright

Playwright styrer en hodeløs nettleser, åpner Gutenberg-editoren, setter inn egendefinerte blokker og bekrefter tilstanden i DOM-treet og sidepanelet.

import { test, expect } from '@wordpress/e2e-test-utils-playwright';

test.describe('Invoice summary block', () => {
    test.beforeEach(async ({ admin }) => {
        await admin.createNewPost();
    });

    test('inserts block and updates total in sidebar', async ({ editor, page }) => {
        await editor.insertBlock({ name: 'wppoland/invoice-summary' });
        const block = page.locator('[data-type="wppoland/invoice-summary"]');
        await expect(block).toBeVisible();

        await page.fill('input[aria-label="Invoice total"]', '1250');
        await expect(block.locator('.invoice-formatted-amount')).toHaveText('1,250.00 EUR');
    });
});

Playwright oppdager visuelle feil og grensesnittavvik som backend-tester ikke fanger opp.

#Statisk analyse med PHPStan i samspill med PHPUnit

Statisk kodeanalyse komplementerer dynamiske enhetstester. Mens enhetstester undersøker spesifikke inndata og utdata, analyserer PHPStan hele kodebasen for typefeil, død kode og ugyldige funksjonssignaturer før testene i det hele tatt startes.

Ved hjelp av szepeviktor/phpstan-wordpress får man tilgang til typedokumentasjon for hele WordPress-kjernen, noe som sikrer at testfilene ikke inneholder ugyldige referanser.

Konfigurasjon i phpstan.neon.dist:

includes:
    - vendor/szepeviktor/phpstan-wordpress/extension.neon

parameters:
    level: 8
    paths:
        - src
        - tests
    excludePaths:
        - tests/Fixtures/*
    bootstrapFiles:
        - tests/bootstrap-unit.php
    checkMissingIterableValueType: false

Kjøring av vendor/bin/phpstan analyse i CI-pipelinen fjerner typiske kjøretidsfeil tidlig i utviklingsløpet.

#Kodedekning og krav til testkvalitet

Måling av testdekning (code coverage) gir oversikt over utestede kodeveier. Likevel er det lite hensiktsmessig å kreve en kunstig 100-prosents dekning dersom det leder til testing av trivielle hjelpemetoder i stedet for forretningsregler.

Moderne team etablerer et pragmatisk dekningsmål på 80 til 85 prosent for doménelogikken, kontrollert i GitHub Actions via PCOV eller Xdebug 3.

php -d pcov.enabled=1 vendor/bin/phpunit --testsuite=unit --coverage-text --coverage-html=coverage-report

En gjennomtenkt konfigurasjon i PHPUnits XML-fil sørger for at eksterne biblioteker og presentasjonsfiler utelates fra beregningen.

#Kontinuerlig integrasjon i GitHub Actions på PHP 8.2 og 8.3

Automatiserte tester gir reell trygghet når de kjøres konsekvent ved hver innsending og pull-forespørsel. Dette sikrer at utilstrekkelig testet kode aldri flettes inn i hovedgrenen.

Et moderne oppsett i GitHub Actions verifiserer koden på PHP 8.2 og 8.3 med MySQL 8.0:

name: Test Suite

on:
  push:
    branches: [ master, main ]
  pull_request:
    branches: [ master, main ]

jobs:
  unit-tests:
    name: PHPUnit on PHP ${{ matrix.php }}
    runs-on: ubuntu-latest
    strategy:
      matrix:
        php: ['8.2', '8.3']

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Setup PHP
        uses: shivammathur/setup-php@v2
        with:
          php-version: ${{ matrix.php }}
          coverage: none
          tools: composer:v2

      - name: Install Composer dependencies
        run: composer install --prefer-dist --no-progress

      - name: Run unit test suite
        run: vendor/bin/phpunit --testsuite=unit

  integration-tests:
    name: Integration Tests on PHP ${{ matrix.php }}
    runs-on: ubuntu-latest
    services:
      mysql:
        image: mysql:8.0
        env:
          MYSQL_ROOT_PASSWORD: root
          MYSQL_DATABASE: wordpress_test
        ports:
          - 3306:3306
        options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3

    strategy:
      matrix:
        php: ['8.2', '8.3']

    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Setup PHP
        uses: shivammathur/setup-php@v2
        with:
          php-version: ${{ matrix.php }}
          extensions: mysqli, pdo_mysql
          coverage: none
          tools: composer:v2

      - name: Install dependencies
        run: composer install --prefer-dist --no-progress

      - name: Run integration tests
        env:
          WP_DB_HOST: 127.0.0.1
          WP_DB_NAME: wordpress_test
          WP_DB_USER: root
          WP_DB_PASS: root
        run: vendor/bin/phpunit --testsuite=integration

Ved å separere enhetstester fra integrasjonstester får utviklere tilbakemelding innen tretti sekunder etter kodeinnsending.

#Katalogstruktur for testsuiter i utvidelsesprosjekter

En ryddig mappestruktur skiller enhets-, integrasjons- og akseptansetester tydelig fra hverandre.

my-enterprise-plugin/
├── .github/
│   └── workflows/
│       └── tests.yml
├── .wp-env.json
├── composer.json
├── phpunit.xml.dist
├── src/
│   ├── Billing/
│   ├── Gateways/
│   └── Plugin.php
└── tests/
    ├── bootstrap-unit.php
    ├── bootstrap-integration.php
    ├── Unit/
    │   ├── Billing/
    │   └── Gateways/
    ├── Integration/
    │   ├── DatabaseQueryTest.php
    │   └── HookRegistrationTest.php
    └── Fixtures/
        └── sample-invoice.json

#Konfigurasjon av phpunit.xml.dist

Konfigurasjonsfilen styrer testinndelingen:

<?xml version="1.0" encoding="UTF-8"?>
<phpunit
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.5/phpunit.xsd"
    bootstrap="tests/bootstrap-unit.php"
    colors="true"
    cacheDirectory=".phpunit.cache"
    executionOrder="depends,defects"
    requireCoverageMetadata="false"
    beStrictAboutOutputDuringTests="true"
    failOnRisky="true"
    failOnWarning="true">
    <testsuites>
        <testsuite name="unit">
            <directory suffix="Test.php">tests/Unit</directory>
        </testsuite>
        <testsuite name="integration">
            <directory suffix="Test.php">tests/Integration</directory>
        </testsuite>
    </testsuites>
    <source>
        <include>
            <directory suffix=".php">src</directory>
        </include>
    </source>
</phpunit>

Dette gjør det enkelt å kjøre raske enhetstester lokalt og den komplette suiten før leveranse.

#Testmatrise og sjekkliste for vedlikehold

Systematisk testing krever gode arbeidsrutiner og løpende oppfølging.

TestnivåVerktøyUtførelsestidDatabasekravHovedformål
EnhetstesterPHPUnit 10/11, Brain Monkey, MockeryMillisekunderIngen (Kun minne)Ren forretningslogikk, beregninger, filtre
IntegrasjonstesterWP_UnitTestCase, wp-envSekunderMySQL testdatabaseWP_Query, databaseskriving, hook-rekkefølge
Akseptansetesterwp-browser, CodeceptionSekunder til minutterHodeløs nettleser + DBInnstillinger i wp-admin, rettigheter, utsjekk
Blokk-tester (e2e)Playwright, @wordpress/scriptsMinutterDocker-miljøGutenberg-editor, sidepanel, blokk-markup

Anbefalte rutiner for testvedlikehold:

  1. Isolér enhetstester: Unngå direkte databasekall i enhetstestkatalogen.
  2. Bruk transaksjonsrulling: Behold raske integrasjonstester ved å rulle tilbake endringer.
  3. Kjør automatisert CI: Test alle endringer på PHP 8.2 og 8.3.
  4. Verifisér blokkstruktur: Sikre at Gutenberg-oppdateringer ikke ødelegger eksisterende data.
  5. Refaktorer for testbarhet: Skill forretningslogikk fra malfiler og visningslag.

Les mer om våre tekniske leveranser og profesjonell WordPress-utvikling hos WPPoland.

#Strategier for flerspråklighet og eksterne utvidelser

I internasjonale installasjoner samhandler moduler ofte med flerspråklighetsutvidelser som Polylang eller WPML, i tillegg til e-handelsløsninger bygget på WooCommerce. Manglende tester for oversettelsesfiltre kan føre til at tekster i handlekurven eller e-postvarsler vises på feil språk.

I enhetstester for oversettelsesmoduler bør man etterligne oversettelsesfunksjonene via Brain Monkey, og kontrollere at formatering og kontekst beholdes:

<?php
declare(strict_types=1);

namespace WPPoland\Tests\Unit;

use Brain\Monkey\Functions;
use PHPUnit\Framework\TestCase;
use WPPoland\I18n\Translator;

final class TranslatorTest extends TestCase {
    public function test_translates_message_with_placeholder(): void {
        Functions\expect('__')
            ->once()
            ->with('Order #%d completed', 'wppoland')
            ->andReturn('Ordre #%d fullført');

        $translator = new Translator();
        $message = $translator->formatOrderMessage(105);

        $this->assertSame('Ordre #105 fullført', $message);
    }
}

I integrasjonstestene verifiseres i tillegg locale-filtrene for å sikre at dynamiske språkbytter underveis i en brukersesjon ikke etterlater feilaktig bufret innhold.

#Feilsøking og ytelsesprofilering av trege tester

Når testsuiten vokser til hundrevis av testmetoder, blir den samlede kjøretiden avgjørende for utvikleropplevelsen. PHPUnit inneholder innebygde profileringsfunksjoner for å avdekke trege metoder.

Ved å benytte --profile identifiserer man raskt hvilke tester som bruker unødvendig lang tid:

vendor/bin/phpunit --testsuite=integration --profile

Hvis en integrasjonstest bruker over 200 millisekunder, skyldes det ofte manglende indekser i midlertidige MySQL-tabeller, dype kallkjeder i kjerne-hooks eller ubufret XML-parsing.

#Håndtering av caching og transienter under testing

WordPress bruker transienter og objekt-mellomlagring i stor utstrekning for å optimalisere ytelse. I en testkontekst kan gjenstridig hurtigbuffer skape falske positiver eller skjule underliggende datafeil.

I enhetstester med Brain Monkey simulerer man funksjonene get_transient() og set_transient() eksplisitt for å bekrefte at utløpstider og nøkkelnavn settes etter hensikten. I integrasjonstester med WP_UnitTestCase sørger man for å kalle wp_cache_flush() før tester som manipulerer globale innstillinger, slik at hvert testkall opererer mot ferske verdier.

<?php
declare(strict_types=1);

namespace WPPoland\Tests\Integration;

use WP_UnitTestCase;

final class TransientCacheTest extends WP_UnitTestCase {
    public function test_transient_expires_and_regenerates_cleanly(): void {
        wp_cache_flush();

        set_transient('wppoland_cached_rate', 1.25, 3600);
        $cachedValue = get_transient('wppoland_cached_rate');

        $this->assertSame(1.25, (float) $cachedValue);

        delete_transient('wppoland_cached_rate');
        $this->assertFalse(get_transient('wppoland_cached_rate'));
    }
}

Systematisk tømming av transienter sikrer at testresultatene forblir deterministiske på tvers av ulike testkjøringer.

#Konklusjon og praktiske råd for utviklingsteam

Automatisert testing er selve grunnmuren for stabil drift og smidige oppgraderinger i profesjonelle WordPress-prosjekter. Ved å kombinere raske enhetstester med grundige integrasjonstester oppnår utviklingsteam høy trygghet og færre driftsfeil.

Start gradvis ved å isolere kjerneberegninger, etabler en standardisert testbase med Docker og integrer testkjøringen direkte i arbeidsflyten på GitHub Actions.

Ved å følge disse prinsippene skaper du en kodebase som er robust, skalerbar og enkel å videreutvikle for hele teamet.

Neste steg

Gjør artikkelen om til faktisk implementering

Denne blokken styrker intern lenking og sender leseren videre til de mest relevante tjenestene og innholdet.

Vil du få dette implementert på nettstedet ditt?

Hvis du vil gjøre kunnskapen i artikkelen om til konkrete forbedringer, redesign eller en tydelig leveranseplan, kan jeg ta det videre.

Relevant klynge

Utforsk andre WordPress-tjenester og kunnskapsbase

Styrk virksomheten din med profesjonell teknisk støtte innen kjerneområdene i WordPress-økosystemet.

Artikkel-FAQ

Ofte stilte spørsmål

Praktiske svar for å bruke temaet i faktisk arbeid.

SEO-readyGEO-readyAEO-ready4 Q&A
Hvorfor skal jeg teste hvis jeg bare kan laste inn siden på nytt?#
Manuell testing er tregt og man kan lett overse feil. Enhets-tester kjører på millisekunder og verifiserer tusenvis av logiske veier umiddelbart.
Trenger jeg en database for enhets-tester?#
Ekte enhets-tester bør være uavhengige av databasen ved hjelp av 'mocks'. Integrasjonstester bruker derimot en midlertidig database (ofte i Docker).
Er testing verdt den ekstra tiden?#
Ja. På sikt sparer det hundrevis av timer med feilsøking og hindrer kritiske feil på produksjonsservere.
Kan jeg teste Gutenberg-blokker?#
Absolutt. I 2026 bruker vi Jest sammen med den offisielle WordPress-pakken for å teste blokk-rendring og attributter.

Trenger du FAQ tilpasset bransje og marked? Vi lager en versjon som støtter dine forretningsmål.

Ta kontakt

Relaterte artikler