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/phpunitwp-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: falseKjø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-reportEn 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=integrationVed å 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.jsonKonfigurasjon 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øy | Utførelsestid | Databasekrav | Hovedformål |
|---|---|---|---|---|
| Enhetstester | PHPUnit 10/11, Brain Monkey, Mockery | Millisekunder | Ingen (Kun minne) | Ren forretningslogikk, beregninger, filtre |
| Integrasjonstester | WP_UnitTestCase, wp-env | Sekunder | MySQL testdatabase | WP_Query, databaseskriving, hook-rekkefølge |
| Akseptansetester | wp-browser, Codeception | Sekunder til minutter | Hodeløs nettleser + DB | Innstillinger i wp-admin, rettigheter, utsjekk |
| Blokk-tester (e2e) | Playwright, @wordpress/scripts | Minutter | Docker-miljø | Gutenberg-editor, sidepanel, blokk-markup |
Anbefalte rutiner for testvedlikehold:
- Isolér enhetstester: Unngå direkte databasekall i enhetstestkatalogen.
- Bruk transaksjonsrulling: Behold raske integrasjonstester ved å rulle tilbake endringer.
- Kjør automatisert CI: Test alle endringer på PHP 8.2 og 8.3.
- Verifisér blokkstruktur: Sikre at Gutenberg-oppdateringer ikke ødelegger eksisterende data.
- 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 --profileHvis 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.







