SuperAPI AI Directory

basercms-unittest

Testing & quality

View repository

Updated 10-15 17:24

About this skill

baserCMS Unit Test Execution Guide (Local)

baserCMS unit tests are run on Docker containers. This summarizes the standard practices for execution, filtering, and failure investigation.

Execution Environment

  • Execution is docker compose(<プロジェクトのdocker-compose.yml配置パス>)。
  • PHP container name: basercms, baserCMS location: /var/www/html。
  • Test configuration: phpunit.xml.dist。<testsuites> A testsuite is defined per plugin in (BaserCore / BcBlog / BcCustomContent / BcMail / BcThemeFile …).
  • The DB depends on the local environment (in the past there were failures with bc-db no host, but currently the <db-host> container is used. This kind of connection failure is environmental and can be ignored).
  • Always check that the containers are running before execution. If the PHP container or DB container is stopped (Exited) and docker exec fails.docker ps Check with docker start <container> <db-container>, and if it is down, for the DB wait until mysqld accepts connections (details below in "Pre-check: Container Operation").

Command Formatting (Important: Automatic Approval of Permissions)

  • For compound commands,outerto pipe | or redirect > if you place , automatic approval of permissions does not work and a confirmation prompt appears.
  • docker exec basercms sh -c '...' within the single quotes ofto redirect/tail if you put everything such as , it is treated as a single docker exec command and automatically approved.
  • Safe read-only commands (grep/find/ls/cat/sed -n/head/tail) are used alone.

Execution Commands

All Tests (Full Suite)

Since the output is large, save it to a file inside the container and display only the end. It takes a little over about 10 minutes to complete, so run it in the background as needed.

docker exec basercms sh -c 'cd /var/www/html && vendor/bin/phpunit --no-coverage > /tmp/phpunit_full.log 2>&1; tail -45 /tmp/phpunit_full.log'

Single File / Single Method

docker exec basercms sh -c 'cd /var/www/html && vendor/bin/phpunit --no-coverage plugins/baser-core/tests/TestCase/Model/Table/PagesTableTest.php 2>&1 | tail -20'
docker exec basercms sh -c 'cd /var/www/html && vendor/bin/phpunit --no-coverage --filter testBeforeSave plugins/baser-core/tests/TestCase/Model/Table/PagesTableTest.php 2>&1 | tail -20'

Syntax Check (lint)

Always perform after modification. Deprecation warnings such as implicit nullable are also output together.

docker exec basercms sh -c 'cd /var/www/html && php -l plugins/baser-core/src/Controller/Admin/ThemesController.php'

Failure Investigation Procedure

  1. Determine whether it is fatal or a warning.logs/debug.log の debug: are deprecation warnings (operation continues).logs/error.log and Fatal/Exception are real errors. Note that in debug mode warnings are also displayed on screen and look like "errors".
  2. When there are many failures, aggregate by root cause. Normalize exception messages from the full log and aggregate them, and first identify systemic causes (a small number of causes producing a large number of failures).
    docker exec basercms sh -c 'cd /var/www/html && grep -hoE "[A-Za-z\\\\]+Exception: .{0,80}|[A-Za-z\\\\]+Error: .{0,80}" /tmp/phpunit_full.log | sed -E "s/[0-9]+/N/g" | sort | uniq -c | sort -rn | head -30'
    
    Aggregation by test class:
    docker exec basercms sh -c 'cd /var/www/html && grep -hoE "^[0-9]+\) [A-Za-z0-9_\\\\]+Test::" /tmp/phpunit_full.log | sort | uniq -c | sort -rn | head -40'
    
  3. Determine whether the cause is the app src, or the test/environment/fixture/i18n。
    • git diff HEAD -- <file> … whether the file in question is your own change target.
    • Unmodified file and plugin load dependent (Plugin::isLoaded('X') is false and behavior is not attached =Unknown method etc.) / not registered in the DI container (Alias ... is not being managed by the container) / external plugin class not loaded /English ↔ Japanese message mismatch(locale/translation), then it is highly likely not migration-induced but due to environment/test factors.
    • Difficult to create a clean baselineNote that: after upgrading the framework, vendor has been replaced, so a simple git stash cannot reproduce the pre-migration state.
  4. Fix → lint → --filter verify unit → rerun all teststo confirm the improvement in counts and new regressions.
  5. How to identify cases where "a value becomes NULL/invalid after saving" (involving afterSave chains): even if you log the entity's propertiesCorrect value at all pointsyet the DB is NULL, which can happen (not dirty / caused by another entity's save).Read the DB step by step with raw SQL and bisectdo: around the suspicious processing (save / afterSave / cascade method),before and afterに $d = $this->getConnection()->execute("SELECT col FROM t WHERE id=?", [$id])->fetch("assoc"); \Cake\Log\Log::write("debug", "STEPn=".var_export($d["col"]??"NULL", true)); insert, andtests/TestApp/logs/debug.log を grep to identify after which step it gets corrupted by interleaving (?? 'X' collapses null to 'X', so be careful in interpretation). Once you find the save causing it, log thevalue itselfthat the save writes (e.g., aggregation is 2026.0 abnormal value such as etc. → func()->min(datetime) numeric conversion, etc.). Debug logs and temporary tests must bealways completely removed.

Introducing a test execution environment for plugins alone (standalone)

Separate from the full suite (the above basercms container),test only one plugin independentlymechanism. Effective for proceeding with TDD on plugins during the 4→5 migration (ported tests become verification for Table/Service 5-series conversion).

  • The execution container is on the application side(e.g., <container>, placement destination /var/www/html/v5). The DB host (e.g. <db-host>) can only be resolved within the Docker network, sorun phpunit inside the container(cannot connect by hitting directly from the host).
  • The plugin has its own composer.json / vendor/ , and incorporates baser-core as a dev dependency.

Where to obtain tests/bootstrap.php and TestApp (important)

composer.json / phpunit.xml.dist can be manually recreated from the description in this book, buttests/bootstrap.php と tests/TestApp/ the entire set (app.php / app_local.php / bootstrap.php / paths.php / plugins.php / routes.php / src/Application.php / src/Controller/AppController.php) has boilerplate content and a large volume, and is not something to write from scratch. These are Included in the plugin skeleton generation command of the official baserCMS baserproject/bc-bake plugin。

  • Repository: https://github.com/baserproject/BcBake
  • Procedure (manual preparation required): composer require --dev baserproject/bc-bake install with → log in to the admin panel and enable(or plugins into the table name='BcBake', status=1 Inject directly. bc-bake is a development tool with no proprietary tables or install migrations, so hardcoding is sufficient in practice) → Run the scaffolding generation command:
    bin/cake bake bc_plugin {PluginName}
    
    This is the standard CakePHP bake plugin In addition to the files created by, baserCMS test infrastructure (tests/bootstrap.php + TestApp etc.) is generated. (The MVC set is bin/cake bake bc_all {table_name} -p {PluginName} --prefix Admin。)
  • Via bc-bake is the manual prerequisite of composer installation + activationis required (it is difficult for an agent alone to complete, so ask the user to prepare this). Activation is by logging into the admin panel, or plugins directly into the table INSERT (name,title,version,status,db_init,priority,created,modified) VALUES ('BcBake','BcBake','1.2.1',1,1,100,NOW(),NOW()). When activated, bin/cake bake --help に bc_plugin / bc_all appears.

★Procedure for retrofitting to an existing plugin (verified)

bake bc_plugin {Name} は Existence is checked by "registered (loaded) plugin name"Therefore, with an existing plugin name, it is Plugin: {Name} already exists, no action taken rejected by (The same applies even if you select the vendor side in path selection= the check is based on the resolved plugin path, not the directory). Therefore:

  1. Temporarily rename the existing plugin itself to move it aside (e.g. plugins/Sample → plugins/Sample2). This makes the "Sample" name treated as unregistered.There is no need to set status=0 in the plugins table(simply moving the directory aside makes it treated as unregistered, and even if an active plugin temporarily lacks its dir, bake's bootstrap tolerates it = verified).
  2. bin/cake bake bc_plugin Sample(the path is 1=plugins/, confirmation is y)。with the correct "Sample" nameand the template is generated. The dialogue is (echo 1; yes y) | ... and run through.| head etc. withIf you close the pipe midway, generation is interrupted by SIGPIPE, so redirect the output to a file and let it run to completion.
    • If it hangs (stops waiting for dialogue)pkill -f 'cake bake' stop it with, and the plugin directory in the middle of generation rm -rf and then retry.
  3. The generated plugins/Sample/tests/bootstrap.php と plugins/Sample/tests/TestApp/ is, the moved-aside main body plugins/Sample2/tests/ へmove(only bootstrap.php and TestApp. See below).
  4. TestApp/config/install.php は It is generated with generic values (host=127.0.0.1 / database=basercms,test_basercms)so the project's DB (e.g. host= / database=v5・test_v5 / user=)へbe sure to edit.
  5. Delete the generated side plugins/Sample , and restore the main body (plugins/Sample2 → plugins/Sample)。
  6. vendor/bin/phpunit confirm green with.

What is moved is tests/bootstrap.php と tests/TestApp/ only.bake bc_plugin は composer.json / phpunit.xml.dist is also generated, butcomposer.json は name is a placeholder, the baser-core version is different, there is no phinx pin, etc., so it is regenerated, therefore the main body's own composer.json(with phinx 0.16.10 pin)do not overwrite。phpunit.xml.dist also, if you already have your own, beware of leftover testsuite names from the reuse source (e.g. CuMcp).

Full set of necessary files (directly under the plugin)

  1. composer.json … require-dev as below.robmorgan/phinx is always 0.16.10 pin to(the pitfall described below).
    "require-dev": {
        "phpunit/phpunit": "10.5.31",
        "josegonzalez/dotenv": "^4.0",
        "baserproject/baser-core": "5.2.1",
        "robmorgan/phinx": "0.16.10",
        "vierge-noire/cakephp-fixture-factories": "^3.0",
        "vierge-noire/cakephp-test-suite-light": "^3.0"
    },
    "autoload-dev": {
        "psr-4": {
            "<Plugin>\\Test\\": "tests/",
            "App\\": "tests/TestApp/src/",
            "BaserCore\\Test\\": "vendor/baserproject/baser-core/tests/"
        }
    },
    "minimum-stability": "dev",
    "prefer-stable": true
    
  2. phpunit.xml.dist … bootstrap="tests/bootstrap.php"、<testsuite> に <directory>tests/TestCase/</directory>、<extensions> に Cake\TestSuite\Fixture\Extension\PHPUnitExtension。
  3. tests/bootstrap.php … TestApp の config/paths.php Load → vendor/autoload.php → core bootstrap → Configure::load('app') → Configure::load('install') set Datasources for test → (new Migrations\TestSuite\Migrator())->runMany([['plugin'=>'BaserCore'],['plugin'=>'<Plugin>']]) → build schema Configure::write('BcApp.testAppPluginsToLoad', ['<Plugin>'])。
  4. tests/TestApp/(minimal app skeleton)… config/install.php(Datasources.default と Datasources.test(DB for test, e.g. test_<project>_v5))、app.php/app_local.php/bootstrap.php/paths.php/plugins.php/routes.php、src/Application.php、src/Controller/AppController.php。
  5. config/Migrations/(schema on the plugin side.*_Initial.php + schema-dump-default.lock). The BaserCore portion is automatically loaded from vendor.
  6. Create the DB for test (e.g. test_<project>_v5) in advancein advance.

★Pitfall: migrations break with phinx 0.16.11 (most important)

cakephp/migrations 4.6.6 In the robmorgan/phinx が 0.16.11 environment, when raised tothe test itself is never reached and everything fails:

Class Migrations\Db\Adapter\PhinxAdapter contains 2 abstract methods and must
therefore be declared abstract or implement the remaining methods
(Phinx\Db\Adapter\AdapterInterface::preExecuteActions, ...::postExecuteActions)

phinx 0.16.11 が AdapterInterface was added to PhinxAdapter in migrations 4.6.6 is not implemented.The fix is to pin phinx to 0.16.10(match the version of the application body that has a proven track record):

docker exec -w /var/www/html/v5/plugins/<Plugin> <container> sh -c 'COMPOSER_MEMORY_LIMIT=-1 composer update robmorgan/phinx --with-all-dependencies --no-interaction'

composer.json の require-dev also to "robmorgan/phinx": "0.16.10" to prevent recurrence.

Pre-check 1: Identify the target container (never assume)

Do not hardcode the container name. The basercms= the core monorepo development environment PHP 8.5,<container>= this project PHP 8.1). The names are similar and easy to mistake. Before running,actually verify the container that contains the target app:

docker ps -a --format '{{.Names}}\t{{.Status}}'                      # 候補を列挙
docker exec <候補> sh -c 'ls -d /var/www/html/v5 >/dev/null 2>&1 && echo HIT; php -v | head -1'

The target path (e.g. /var/www/html/v5) exists, and the container that outputs the expected PHP version is correct. Likewise for the DB container, use one whose name matches the connection host name (install.php の host, e.g. <db-host>) (do not confuse it with another DB such asbc-db ).

Pre-check 2: Container operation (always first)

Once identified,check that both the app container and the DB container are Up, and start them if they are down. In a stopped state (Exited) as is, docker exec then Connection refused/ no container, you will waste time being stuck.

docker ps -a --format '{{.Names}}\t{{.Status}}' | grep -E '<container>|<db-container>'
docker start <container> <db-container>

The DB container may not accept connections via mysqld immediately after startup, sowait until it is reachable:

for i in $(seq 1 30); do docker exec <db-container> sh -c 'mysqladmin ping -h127.0.0.1 -u<user> -p<pass> 2>/dev/null | grep -q alive' && break; sleep 2; done

(For connection information, refer to the plugin's tests/TestApp/config/install.php の Datasources.test, and for production v5/config/install.php . In , DB host <db-host> / DB test_<project>_v5 / user <dbuser>。)

Execution command

docker exec -w /var/www/html/v5/plugins/<Plugin> <container> sh -c 'vendor/bin/phpunit --testdox 2>&1 | tail -60'

If it passes bootstrap and the test list appears, migrations → DB construction has succeeded.

Cleanup of old v4 test assets (during migration)

  • Delete aggregate classes: CakeTestSuite that inherit public static function suite() and merely bundle with *AllTest / *AllModelTest do not work with CakePHP5/PHPUnit10 (CakeTestSuite absent),<directory> are picked up by discovery and cause load errors. They have zero value, so delete them.
  • Fixtures to v5: extends CakeTestFixture + public $import / public $records(v4) → Cake\TestSuite\Fixture\TestFixture series.
  • Tests to v5: extends BaserTestCase / ClassRegistry::init('Plugin.Model') / public $fixtures = ['plugin.sample.User'](v4 notation) → BaserCore's TestCase base +fetchTable() + v5 fixtures.
  • Note: Even if the method names are all present, the Table body may still be v4($this->Assoc->find('first',...)・unbindModel・virtualFields・Behaviors->detach('BcCache') such remnants /// TODO baserCMS5移行: markers). In that case it is correct for the test to be red, and proceed in tandem with upgrading the SUT to v5.

Test data: Factory and schema (for migration TDD)

The standard practice for data preparation when converting Table/aggregation/save methods to the 5.x series via TDD during migration.

Factory(CakephpFixtureFactories)

Use the vierge-noire/cakephp-fixture-factories bundled with baser-core. One Factory per table:

namespace Sample\Test\Factory;
use CakephpFixtureFactories\Factory\BaseFactory as CakephpBaseFactory;
use Faker\Generator;
class SampleCommitFactory extends CakephpBaseFactory
{
    protected function getRootTableRegistryName(): string { return 'Sample.SampleCommits'; }
    protected function setDefaultTemplate(): void {
        $this->setDefaultData(fn(Generator $faker) => [
            'date' => '2021-11-30 00:00:00', 'project_id' => 1, 'user_id' => 1,
            'commit_type' => 1, 'commit' => 0, 'notes' => '',
            'created' => '2021-12-01 00:00:00', 'modified' => '2021-12-01 00:00:00',
        ]);
    }
}
  • Put all NOT NULL columns into defaults(SHOW COLUMNS ... WHERE Null='NO' to verify).
  • Usage: SampleCommitFactory::make(['user_id'=>1,'commit'=>100])->persist() saves and returns the entity (with id). In tests, the id can be used as a foreign key for subsequent data.
  • datetime columns should be passed in 'Y-m-d H:i:s' format。'Y-m-d' ; otherwise marshal turns it into NULL and it won't match date-range conditions.
  • save-type callbacks fire on persist(e.g. afterSave in SamplePractices). By leveraging this, the results of afterSave (calc_amount, etc.) can be asserted after persist.
  • For BTM/HABTM test data, link directly to the join table: e.g. users_user_groups は TableRegistry::getTableLocator()->get('UsersUserGroups')->setTable('users_user_groups') で newEntity(['user_id'=>, 'user_group_id'=>])->saveOrFail(). The group itself is get('BaserCore.UserGroups')->save(newEntity(['name'=>'unit_1','title'=>..,'auth_prefix'=>'Admin']))。

Test schema (when the migration is incomplete)

Plugin's config/Migrations may create only some of the tables (mid-migration). Without the tables needed in the test DB, aggregation tests cannot be assembled.

  • Add a migration that faithfully reproduces the production SHOW CREATE TABLE within(BaseMigration::up() , inside $this->execute(<<<'SQL' CREATE TABLE ... SQL)). Garbled Japanese COMMENT clauses may be removed, butcolumn names, types, and charset must match production。down() で DROP TABLE IF EXISTS。
  • [Caution] Do not add columns or tables that don't exist in production just to make tests pass. That hides production incompatibilities. Solve it with the correct 5.x schema (e.g. do not add users.user_group_id , solve with users_user_groups BTM → basercms-plugin-4-to-5-upgrade T-K)。

The migration TDD loop

  1. Write a test that asserts the 4.x expected value (or the expected aggregate value with minimal data) → run it and get RED(the 4.x method fails with Undefined property/$this->data etc. = the expected red).
  2. Convert the SUT to 5.x (seebasercms-plugin-4-to-5-upgrade T-F/T-J).
  3. Re-run and get GREEN. Also confirm there are no regressions in the full suite.

If the expected values don't match even after a faithful migration, do not fudge it to pass; record the cause and ask for a decision (it may be a misreading of the 4.x logic or a bug in 4.x itself).

Controller integration tests (admin screens, proven)

Even in a standalone environment, baser-core-style HTTP integration testswork. Since GET renders all the way to the template, they serve as **a substitute for manual browser checks (automated screen checks)** and can detect templates/forms broken by migration.

  • Format (modeled on baser-core tests/TestCase/Controller/Admin/*ControllerTest.php ):
    class SampleEstimatesControllerTest extends \BaserCore\TestSuite\BcTestCase {
        use \CakephpFixtureFactories\Scenario\ScenarioAwareTrait;
        public function setUp(): void {
            parent::setUp();
            $this->loadFixtureScenario(\BaserCore\Test\Scenario\InitAppScenario::class); // 管理者/サイト/グループ
            $this->loginAdmin($this->getRequest('/baser/admin/sample/sample_estimates/index'));
        }
    }
    
    • GET: $this->get('/baser/admin/sample/sample_estimates/edit/'.$id); $this->assertResponseSuccess();
    • POST: $this->enableCsrfToken(); $this->enableSecurityToken(); $this->post('/baser/admin/sample/sample_estimates/edit/'.$id, $data); $this->assertRedirect([...]);
    • Prepare data with Factory (SampleEstimateFactory::make([...])->persist())。
  • Prerequisites for GET rendering (200):
    • Also include in the standalone vendor the component-dependent packages that screen uses (e.g. for Excel output, phpoffice/phpspreadsheet. If absent, the component returns 500 with class_not_found).
    • Theall tablesreferenced by the screen must exist in the test DB (estimate edit references sample_dep_companies tables from other plugins such as → supplement migrations with the production DDL). Deficiencies can be identified by the 500's Base table or view not found .
    • The true cause of the 500 is checked in tests/TestApp/logs/error.log を tail/grep (because IntegrationTest rounds everything to status 500).
  • Typical migration omissions caught by GET integration tests: 4-series templates FormHelper::create('Model')(string model) → No context provider found .... 5-series is $this->BcAdminForm->create(null, ['valueSources'=>['data','context']])。$this->Form->... → $this->BcAdminForm->...、$this->Form->value('x') → $this->getRequest()->getData('x') etc. (basercms-plugin-4-to-5-upgrade C-I/F-series).302 smoke tests (unauthenticated redirect) alone do not reveal rendering errors, so run all the way through rendering with a logged-in GET integration test.
  • ★[Most important] assertResponseSuccess()(200)は Undefined variable Swallowing PHP warnings such as: even if the template references an unset view variable (e.g., $saleTypes/$billingStatuses = AppController::beforeRender の set(Configure::read(...)) missing), a warning is emitted but rendering still passes with 200,and the test stays green. = Even if the rendering test is green, the screen can ship with "empty selects/broken columns".Countermeasure: after the rendering test, call tests/TestApp/logs/error.log を grep "Undefined variable"(or check Warning/Deprecated) and confirm pristine. Tightening this in CI reliably catches AppController inheritance omissions (basercms-plugin-4-to-5-upgrade the set rows in the C-0 table) and the like.
  • POST flow integration test for ajax actions: invoice confirmation (ajax_fixed_multiple) ajax like (1) enableCsrfToken()/enableSecurityToken() + ajax headers as needed, (2) POST keys areconfirmed by reading the 4-series canonical controller(do not guess), (3) confirm that the confirmation cascades to other tables(e.g., invoice confirmation → one invoice billing_status_cd=3 not only but also SampleSales is created), you can demonstrate the controller → Table → afterSave coupling through the screen. If the response changes format from the 4-series' direct return value to 5-series JSON, record it (JS client-side behavior is confirmed in the browser).
  • ★Assert the existence of DOM ids that bundled JS grabs to catch the "silent death of JS integration": 4-series bundled JS grabs DOM by CamelCase id (#SampleEstimateSubmissionTarget)や data[Model][field] name, but the 5-series control() は kebab id(sampleestimate-submission-target)+Model[field] generates name, so even if the selectors all fail, no PHP error or warning is emittedand only the JS silently dies while rendering stays 200(autofill, calculation, and radio switching do not work.basercms-plugin-4-to-5-upgrade C-F2). This cannot be caught by a plain rendering check in a PHP test, butasserting "does the id referenced by the JS exist in the rendered HTML" catches it as a regression:
    // JS が掴む id を grep で洗い出して列挙 → 描画HTMLに含まれることを assert
    $this->get('/baser/admin/sample/sample_estimates/add');
    $this->assertResponseSuccess();
    foreach (['SampleEstimateSubmissionTarget','SampleEstimateName','SampleEstimateContractMonths' /* form.js が #id で掴む全id */] as $id) {
        $this->assertResponseContains('id="' . $id . '"', "JS連携id {$id} がDOMに無い=セレクタ不一致");
    }
    
    Whether you align ids by explicitly assigning them or fix the JS to use 5-series ids, this assert becomes a guard that detects "the ids drifted again in the next migration or template regeneration".

Integration scenario test for cyclic afterSave (interlinked domains)

For integrations where the afterSave of multiple Tables fire each other (e.g., Quote↔Invoice↔Project↔Payment↔Sales), Table unit tests cannot guarantee that the entire cycle converges without breaking.Integration scenario tests that run through a single series of business flows end to endAdd 1–2 per domain.

  • Format: same as unit tests BcTestCase + Factory. Retrieve multiple Tables with getTableLocator()->get() and call methods in the order of the business scenario. Example (invoice lifecycle):
    1. Prepare a project + quote (pre-order) with Factory
    2. Invoice generation (createNewMultiple) → assert invoice count, quote billing_amount, and automatic payment generation
    3. Invoice confirmation (fixedMultiple) → assert invoice billing_status_cd=3, payment amount update, and revenue recognition
    4. Reflection of aggregation into the quote (sale_amount/billing_status_cd)を assert
  • Demonstration of re-entry guard: "being able to reach the end without hanging" itself proves there is no infinite loop (basercms-plugin-4-to-5-upgrade の inAfterSave guard). Also assertthat counts have not exploded(e.g., 1 invoice for a single payment). Preparing both a single payment (contract_months=1/payment_span=1) and multiple installments (annual contract, monthly = 12 installments) cases also protects the installment logic at the same time.
  • [Most important] Integration tests also adhere to "faithful to 4-series" (reproduce even if 4-series is buggy): The expected values for integration are not "values that look business-correct" but "values that 4-series actually outputs." Example: 4-series SampleSale::afterSave is quote sale_amount をthe recognized amount for that one invoiceso with multiple invoices,only the recognized amount of the last processed invoice remains(last-write-wins, not summed). 5-series correctly reproduces this behavior too, so the test asserts not the sum but 100000(the last one), andleave a line-numbered comment in 4-series explaining why it happens. Even if the expectation is business-wise odd, do not "fix" it on your own (fixing is a separate task). → A concrete example of the principle in "Migration TDD loop."

Cautions when running tests in parallel (test DB contention)

When proceeding with multiple work items in parallel,vendor/bin/phpunitdo not run (the full suite) multiple times simultaneously. Because a shared test DB test_<project>_v5 is used, fixture truncate / insert contend and become unstable (failures or mutual contamination). When proceeding with parallel rewrites, each parallel job should stop at php -l(syntax check), andrun the full suite only once at the endall together. (For the methodology of what order to proceed with the migration as a whole = cross-cutting code check → syntax 5-series conversion → tests & browser, see basercms-plugin-4-to-5-upgradeSee "How to proceed with migration". This skill covers only test execution and investigation.)

Note

  • If you want to display method names in the full test, the environment variable SHOW_TEST_METHOD=true(BcTestCase::setUp is supported).
  • For local-specific circumstances, also see .github/instructions/local.instructions.md(.gitignore (may not exist as a target).

Related: For recipes to fix bugs caused by migration, see cakephp-migration / php-migration to skills.