PHPUnit Testing در وردپرس چطور راه‌اندازی می‌شود؟ این پرسشی است که در مسیر حرفه‌ای‌سازی پروژه‌های وردپرسی، دیر یا زود با آن روبه‌رو می‌شوید. PHPUnit یک فریم‌ورک تست برای PHP است که به‌عنوان استاندارد صنعت شناخته می‌شود و وردپرس نیز از آن برای تست هسته و افزونه‌ها استفاده می‌کند. راه‌اندازی PHPUnit در وردپرس دو مسیر اصلی دارد: WordPress Test Suite که وردپرس را به‌طور کامل بارگذاری می‌کند و مناسب تست‌های integration است، و Brain Monkey که بدون وردپرس، تست‌های unit را اجرا می‌کند. انتخاب بین این دو، به نوع تست و معماری پروژه بستگی دارد. در این نوشتار، از نصب PHPUnit و Composer تا پیکربندی phpunit.xml، ساخت bootstrap، نوشتن اولین تست، و ادغام در CI/CD را به‌صورت گام‌به‌گام بررسی می‌کنیم. هدف این است که یک راه‌اندازی حرفه‌ای و پایدار برای تست در پروژه‌های وردپرسی داشته باشید که در محیط محلی و در خط لوله انتشار به‌طور یکسان کار کند. برخلاف بسیاری از آموزش‌های سطحی، در اینجا بر تصمیم‌های معماری و انتخاب‌های عملی تمرکز می‌کنیم: چه چیزی را تست کنیم، چه چیزی را نه، و چطور تست‌ها را از پیچیدگی غیرضروری دور نگه داریم.

نخستین‌باری که PHPUnit را در یک پروژه وردپرسی راه‌اندازی کردیم، چند ساعت با خطاهای مبهم و پیکربندی‌های ناقص درگیر بودیم. بعد از چند پروژه، الگویی روشن شکل گرفت که در این نوشتار آن را به‌صورت گام‌به‌گام بررسی می‌کنیم. هدف این است که شما در چند دقیقه، نه چند ساعت، به یک راه‌اندازی پایدار برسید.

PHPUnit چیست و چرا برای وردپرس مهم است؟

PHPUnit یک فریم‌ورک تست برای PHP است که توسط Sebastian Bergmann توسعه یافته و به‌عنوان استاندارد صنعت شناخته می‌شود. این فریم‌ورک امکان نوشتن تست‌های خودکار را فراهم می‌کند که به‌صورت مستقل از مرورگر اجرا می‌شوند و رفتار کد را در شرایط مختلف بررسی می‌کنند. PHPUnit در اکوسیستم وردپرس نیز جایگاه ویژه‌ای دارد: هسته وردپرس از PHPUnit برای تست استفاده می‌کند، و بسیاری از افزونه‌های حرفه‌ای نیز همین مسیر را دنبال می‌کنند.

مزایای PHPUnit در پروژه‌های وردپرسی:

  • کشف زودهنگام خطا: تغییرات جدید، قبل از انتشار، بررسی می‌شوند.
  • مستندسازی رفتار: تست‌ها، رفتار مورد انتظار کد را مستند می‌کنند.
  • امکان بازآرایی مطمئن: با تست، می‌توان بدون ترس از شکستن کد، آن را بازآرایی کرد.
  • پایداری بلندمدت: پروژه‌های دارای تست، در برابر regression مقاوم‌تر هستند.
  • اعتماد مشتری: پروژه‌های دارای تست، برای تیم‌های حرفه‌ای جذاب‌تر هستند.

برای درک عمیق‌تر جایگاه تست در توسعه وردپرس، تست و دیباگ پروژه‌های توسعه وردپرس را ببینید.

PHPUnit یک «شبکه ایمنی» است: اگر کد شما در حین توسعه بشکند، تست‌ها بلافاصله به شما هشدار می‌دهند. بدون این شبکه، هر تغییر یک ریسک پنهان است.

دو مسیر راه‌اندازی: WordPress Test Suite و Brain Monkey

در اکوسیستم وردپرس، دو رویکرد اصلی برای تست وجود دارد:

مسیر اول: WordPress Test Suite

این رویکرد، وردپرس را به‌طور کامل بارگذاری می‌کند و به یک پایگاه داده MySQL آزمایشی نیاز دارد. مناسب برای تست‌های integration که رفتار واقعی وردپرس را بررسی می‌کنند. برای مثال، تست ذخیره‌سازی در wp_options، تست هوک‌های واقعی، و تست REST API.

مسیر دوم: Brain Monkey

این رویکرد، توابع و کلاس‌های وردپرس را شبیه‌سازی می‌کند و بدون وردپرس و MySQL اجرا می‌شود. مناسب برای تست‌های unit که منطق خالص کد را بررسی می‌کنند. سرعت اجرا در این رویکرد چند برابر بیشتر است. برای درک عمیق‌تر، Brain Monkey برای وردپرس را ببینید.

معیارWordPress Test SuiteBrain Monkey
نوع تستIntegrationUnit
نیاز به MySQLبلهخیر
سرعتثانیهمیلی‌ثانیه
پیچیدگی راه‌اندازیزیادکم
مناسب برایرفتار واقعی وردپرسمنطق سفارشی

توصیه عملی: هر دو را در پروژه داشته باشید. ۷۰ درصد تست‌ها را با Brain Monkey بنویسید و ۳۰ درصد را با WordPress Test Suite. برای درک عمیق‌تر، ساختاردهی پروژه توسعه وردپرس را ببینید.

پیش‌نیازها و نصب Composer

قبل از نصب PHPUnit، چند پیش‌نیاز ضروری است:

  • PHP نسخه ۷.۴ یا بالاتر: برای PHPUnit 9 یا ۱۰.
  • Composer: مدیریت وابستگی‌های PHP.
  • MySQL نسخه ۵.۷ یا بالاتر: فقط برای WordPress Test Suite.
  • Subversion (SVN): برای دریافت WordPress Test Suite.

نصب Composer روی Linux/macOS:

curl -sS https://getcomposer.org/installer | php
mv composer.phar /usr/local/bin/composer

روی Windows، از نصب‌کننده رسمی Composer استفاده کنید.

نصب SVN روی Ubuntu/Debian:

sudo apt install subversion

برای درک عمیق‌تر محیط توسعه، توسعه وردپرس با محیط لوکال را ببینید.

نصب PHPUnit

در پوشه پروژه، فایل composer.json بسازید و PHPUnit را به‌عنوان وابستگی توسعه اضافه کنید:

{
  "name": "my/plugin",
  "require-dev": {
    "phpunit/phpunit": "^9.6",
    "brain/monkey": "^2.6",
    "yoast/phpunit-polyfills": "^2.0"
  },
  "autoload": {
    "psr-4": {
      "MyPlugin\\": "src/"
    }
  },
  "autoload-dev": {
    "psr-4": {
      "MyPlugin\\Tests\\": "tests/"
    }
  }
}

سپس اجرا کنید:

composer install

نکته مهم: yoast/phpunit-polyfills یک کتابخانه ضروری است که سازگاری بین نسخه‌های مختلف PHPUnit و PHP را فراهم می‌کند. بدون آن، ممکن است در محیط‌های مختلف با خطا روبه‌رو شوید. برای درک عمیق‌تر، PHP در وردپرس را ببینید.

راه‌اندازی WordPress Test Suite

اگر می‌خواهید تست‌های integration بنویسید، WordPress Test Suite لازم است. این مجموعه، یک نسخه از وردپرس را در یک پایگاه داده آزمایشی نصب می‌کند.

گام ۱: دریافت WordPress Test Suite

svn co https://develop.svn.wordpress.org/trunk/tests/phpunit/ /tmp/wordpress-tests-lib

یا برای نسخه خاص:

svn co https://develop.svn.wordpress.org/tags/6.4/tests/phpunit/ /tmp/wordpress-tests-lib

گام ۲: ساخت پایگاه داده آزمایشی

mysql -u root -p -e "CREATE DATABASE wordpress_test;"

گام ۳: پیکربندی فایل wp-tests-config.php

فایل نمونه را کپی کنید:

cp /tmp/wordpress-tests-lib/wp-tests-config-sample.php /tmp/wordpress-tests-lib/wp-tests-config.php

و مقادیر زیر را تنظیم کنید:

define( 'DB_NAME', 'wordpress_test' );
define( 'DB_USER', 'root' );
define( 'DB_PASSWORD', 'your_password' );
define( 'DB_HOST', 'localhost' );
define( 'ABSPATH', '/tmp/wordpress/' );

برای درک عمیق‌تر خطاهای مرتبط با پایگاه داده، خطای اتصال به دیتابیس وردپرس را ببینید.

گام ۴: تعریف متغیرهای محیطی

در فایل bootstrap.php، مسیر WordPress Test Suite را تعریف کنید:

$_tests_dir = getenv( 'WP_TESTS_DIR' );
if ( ! $_tests_dir ) {
    $_tests_dir = '/tmp/wordpress-tests-lib';
}
require_once $_tests_dir . '/includes/functions.php';

function _manually_load_plugin() {
    require dirname( __DIR__ ) . '/my-plugin.php';
}
tests_add_filter( 'muplugins_loaded', '_manually_load_plugin' );

require $_tests_dir . '/includes/bootstrap.php';

این bootstrap، وردپرس را بارگذاری می‌کند، افزونه شما را فعال می‌کند و سپس تست‌ها را اجرا می‌کند.

پیکربندی فایل phpunit.xml

فایل phpunit.xml پیکربندی اصلی PHPUnit است. یک نمونه جامع:

<?xml version="1.0" encoding="UTF-8"?>
<phpunit
    bootstrap="tests/bootstrap.php"
    colors="true"
    beStrictAboutTestsThatDoNotTestAnything="true"
    beStrictAboutOutputDuringTests="true"
    failOnRisky="true"
    failOnWarning="true">
    <testsuites>
        <testsuite name="unit">
            <directory>tests/unit</directory>
        </testsuite>
        <testsuite name="integration">
            <directory>tests/integration</directory>
        </testsuite>
    </testsuites>
    <coverage>
        <include>
            <directory suffix=".php">src</directory>
        </include>
    </coverage>
    <php>
        <env name="WP_TESTS_DIR" value="/tmp/wordpress-tests-lib"/>
    </php>
</phpunit>

نکته مهم: failOnWarning و failOnRisky را فعال کنید. این تنظیمات، هشدارها را جدی می‌گیرند و از انباشت تست‌های ناقص جلوگیری می‌کنند. برای درک عمیق‌تر خطاهای PHP، خطای Fatal error در PHP را ببینید.

ساخت فایل bootstrap

فایل bootstrap، نقطه شروع تست‌ها است و سه وظیفه اصلی دارد: بارگذاری Composer autoload، راه‌اندازی Brain Monkey، و بارگذاری WordPress Test Suite (در صورت نیاز).

<?php
// tests/bootstrap.php

require_once dirname( __DIR__ ) . '/vendor/autoload.php';

// راه‌اندازی Brain Monkey برای تست‌های unit
use Brain\Monkey;

Monkey\setUp();

// بارگذاری WordPress Test Suite برای تست‌های integration
$_tests_dir = getenv( 'WP_TESTS_DIR' );
if ( $_tests_dir ) {
    require_once $_tests_dir . '/includes/functions.php';

    function _manually_load_plugin() {
        require dirname( __DIR__ ) . '/my-plugin.php';
    }
    tests_add_filter( 'muplugins_loaded', '_manually_load_plugin' );

    require $_tests_dir . '/includes/bootstrap.php';
}

این bootstrap، هر دو مسیر تست را پشتیبانی می‌کند: اگر WP_TESTS_DIR تعریف شده باشد، WordPress Test Suite بارگذاری می‌شود؛ در غیر این صورت، فقط Brain Monkey فعال می‌شود. برای درک عمیق‌تر ساختار افزونه، ساختار استاندارد افزونه وردپرس را ببینید.

نوشتن اولین تست

اولین تست را در پوشه tests/unit بسازید:

<?php
// tests/unit/MyPluginTest.php

namespace MyPlugin\Tests\Unit;

use PHPUnit\Framework\TestCase;
use Brain\Monkey;
use function Brain\Monkey\Functions\when;

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

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

    public function test_get_setting_returns_default_when_empty() {
        when( 'get_option' )->justReturn( false );

        $result = myplugin_get_setting( 'threshold', 50 );

        $this->assertEquals( 50, $result );
    }

    public function test_get_setting_returns_stored_value() {
        when( 'get_option' )->justReturn( ['threshold' => 100] );

        $result = myplugin_get_setting( 'threshold', 50 );

        $this->assertEquals( 100, $result );
    }
}

اجرا:

vendor/bin/phpunit --testsuite unit

اگر همه چیز درست باشد، تست‌ها در چند میلی‌ثانیه اجرا می‌شوند. برای درک عمیق‌تر خطاهای رایج، اشتباهات رایج در کدنویسی وردپرس را ببینید.

تست هوک‌های وردپرس

هوک‌های وردپرس، قلب تپنده توسعه هستند و تست آن‌ها بخش مهمی از تست‌نویسی است. با WordPress Test Suite، هوک‌ها واقعاً اجرا می‌شوند؛ با Brain Monkey، شبیه‌سازی می‌شوند.

تست اکشن‌ها با WordPress Test Suite

public function test_my_action_is_registered() {
    $this->assertNotFalse( has_action( 'init', 'myplugin_init' ) );
}

public function test_my_action_runs() {
    do_action( 'myplugin_custom_action', 'data' );
    $this->assertTrue( myplugin_action_was_called() );
}

تست فیلترها

public function test_price_filter() {
    add_filter( 'myplugin_price', function( $price ) {
        return $price * 1.1;
    } );

    $result = apply_filters( 'myplugin_price', 100 );

    $this->assertEquals( 110, $result );
}

برای درک عمیق‌تر هوک‌ها، هوک‌های وردپرس: قلب تپنده توسعه را ببینید.

مدیریت پایگاه داده در تست‌ها

در WordPress Test Suite، هر تست در یک تراکنش اجرا می‌شود و در پایان، تغییرات برگشت داده می‌شوند. این یعنی هر تست از یک وضعیت تمیز شروع می‌شود. برای استفاده از این قابلیت، کلاس پایه را از WP_UnitTestCase ارث‌بری کنید:

<?php
// tests/integration/MyPluginIntegrationTest.php

class MyPluginIntegrationTest extends WP_UnitTestCase {
    public function test_save_setting() {
        update_option( 'myplugin_setting', 'value' );

        $this->assertEquals( 'value', get_option( 'myplugin_setting' ) );
    }

    public function test_setting_is_clean_in_next_test() {
        $this->assertFalse( get_option( 'myplugin_setting' ) );
    }
}

در این مثال، تست دوم تأیید می‌کند که تغییرات تست اول برگشت داده شده است. برای درک عمیق‌تر، خطای اتصال به دیتابیس وردپرس را ببینید.

ساخت Factory برای داده‌های تست

WordPress Test Suite یک factory قوی برای ساخت داده‌های تست فراهم می‌کند:

$post_id = $this->factory()->post->create( [
    'post_title' => 'Test Post',
    'post_status' => 'publish',
] );

$user_id = $this->factory()->user->create( [
    'role' => 'editor',
] );

این factory، امکان ساخت داده‌های واقعی در پایگاه داده را فراهم می‌کند.

ادغام در CI/CD با GitHub Actions

پس از راه‌اندازی تست‌ها در محیط محلی، گام بعدی ادغام در CI/CD است. یک نمونه GitHub Actions:

name: Tests

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

jobs:
  test:
    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

    steps:
      - uses: actions/checkout@v3

      - name: Setup PHP
        uses: shivammathur/setup-php@v2
        with:
          php-version: '8.2'
          extensions: mysqli
          coverage: xdebug

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

      - name: Install WordPress Test Suite
        run: bash bin/install-wp-tests.sh wordpress_test root root 127.0.0.1 latest

      - name: Run unit tests
        run: vendor/bin/phpunit --testsuite unit

      - name: Run integration tests
        run: vendor/bin/phpunit --testsuite integration
        env:
          WP_TESTS_DIR: /tmp/wordpress-tests-lib

این workflow، تست‌ها را در چند ثانیه اجرا می‌کند و نتیجه را در pull request نمایش می‌دهد. برای درک عمیق‌تر، CI/CD برای پروژه‌های وردپرسی را ببینید.

بهترین شیوه‌های نوشتن تست

  • هر تست، یک رفتار: هر تست باید فقط یک جنبه از رفتار را بررسی کند.
  • نام توصیفی: نام تست باید رفتار مورد انتظار را توصیف کند.
  • الگوی AAA: Arrange (تنظیم)، Act (اجرا)، Assert (بررسی).
  • استقلال تست‌ها: هر تست باید مستقل اجرا شود.
  • سرعت بالا: تست‌های unit باید در میلی‌ثانیه اجرا شوند.
  • پوشش منطق، نه خط: هدف، پوشش منطق است، نه ۱۰۰ درصد خطوط.
  • تست حالت‌های خطا: فقط مسیر موفق را تست نکنید.
  • استفاده از factory: داده‌های تست را با factory بسازید، نه دستی.
  • پرهیز از تست‌های شکننده: تست‌هایی که به جزئیات پیاده‌سازی وابسته‌اند، شکننده هستند.
  • اجرای منظم: تست‌ها را در هر commit اجرا کنید.

برای درک عمیق‌تر ساختار کد، افزونه وردپرس چطور نوشته می‌شود؟ را ببینید.

اشتباهات رایج در راه‌اندازی PHPUnit

  • فراموش کردن Monkey\tearDown(): شبیه‌سازی‌ها بین تست‌ها نشت می‌کنند.
  • عدم استفاده از WP_UnitTestCase: تست‌های integration بدون این کلاس، داده‌های قبلی را می‌بینند.
  • پیکربندی نادرست phpunit.xml: مسیر bootstrap اشتباه، خطاهای مبهم ایجاد می‌کند.
  • عدم تعریف متغیرهای محیطی: WP_TESTS_DIR باید در CI تعریف شود.
  • اجرای تست‌ها با کاربر root MySQL: در محیط تولید خطرناک است.
  • نادیده گرفتن نسخه PHP: PHPUnit ۱۰ نیازمند PHP 8.1 یا بالاتر است.
  • تست بیش از حد: نوشتن تست برای هر خط، زمان‌بر و بی‌فایده است.
  • عدم اجرای تست‌ها در CI: تست‌هایی که اجرا نمی‌شوند، ارزشی ندارند.
  • وابستگی تست‌ها به یکدیگر: ترتیب اجرا نباید روی نتیجه اثر بگذارد.
  • نادیده گرفتن coverage: coverage پایین، نشانه تست‌های ناکافی است.

برای مرور خطاهای مشابه، دیباگ کردن کدهای سفارشی وردپرس را ببینید.

پرسش‌های پرتکرار درباره PHPUnit در وردپرس

PHPUnit را چطور نصب کنم؟ با Composer: composer require --dev phpunit/phpunit.

تفاوت WordPress Test Suite و Brain Monkey چیست؟ اولی وردپرس را بارگذاری می‌کند و برای integration است؛ دومی توابع را شبیه‌سازی می‌کند و برای unit. برای درک عمیق‌تر، Brain Monkey برای وردپرس را ببینید.

چرا تست‌های من کند اجرا می‌شوند؟ احتمالاً از WordPress Test Suite استفاده می‌کنید. برای تست‌های سریع، Brain Monkey را انتخاب کنید.

چطور coverage را اندازه بگیرم؟ با افزودن --coverage-html coverage به دستور PHPUnit و نصب Xdebug.

آیا PHPUnit در CI/CD کار می‌کند؟ بله، و یکی از رایج‌ترین کاربردهای آن است. برای مرور گزینه‌ها، CI/CD برای پروژه‌های وردپرسی را ببینید.

چه چیزی را نباید تست کنم؟ توابع هسته وردپرس و کتابخانه‌های شخص‌ثالث را تست نکنید؛ آن‌ها تست خودشان را دارند.

چطور تست‌ها را در GitHub Actions اجرا کنم؟ با یک workflow ساده که MySQL را به‌عنوان service راه‌اندازی می‌کند و PHPUnit را اجرا می‌کند. برای مرور، GitHub Actions راهنمای خودکارسازی را ببینید.

برای مطالعه بیشتر درباره PHPUnit، صفحه PHPUnit در ویکی‌پدیا مفید است.

خط پایان

PHPUnit Testing در وردپرس، دیگر یک کار لوکس یا اختیاری نیست؛ بخشی از توسعه حرفه‌ای است. راه‌اندازی آن در چند گام ساده انجام می‌شود، اما انتخاب معماری درست — ترکیب WordPress Test Suite و Brain Monkey — تفاوت بین تست‌هایی که هرگز اجرا نمی‌شوند و تست‌هایی که در هر commit اجرا می‌شوند را می‌سازد. اگر پروژه وردپرسی دارید که تست ندارد، همین امروز با یک تست ساده شروع کنید. اگر تست دارید اما کند است، Brain Monkey را بررسی کنید. و اگر تست‌ها را در CI اجرا نمی‌کنید، همین امروز این کار را اضافه کنید.

اگر در پروژه‌ای PHPUnit را راه‌اندازی کرده‌اید یا با چالش‌هایی روبه‌رو شده‌اید، تجربه خود را در دیدگاه‌ها بنویسید؛ به‌خصوص اگر معماری خاصی برای تست دارید یا با نسخه‌های خاص PHPUnit و PHP مشکل‌هایی حل کرده‌اید، این اطلاعات برای خواننده بعدی بسیار ارزشمند است.