[{"data":1,"prerenderedAt":988},["ShallowReactive",2],{"navigation":3,"external-navigation":214,"external_core:\u002Fcore\u002Fbackend\u002Ftesting":443},[4,8,201],{"title":5,"path":6,"stem":7},"Documentation","\u002F","README",{"title":9,"path":10,"stem":11,"children":12},"EOS","\u002Feos","eos\u002FREADME",[13,44,121,181],{"title":14,"collapsed":15,"path":16,"stem":17,"children":18,"page":43},"Adrs",true,"\u002Feos\u002Fadrs","eos\u002Fadrs",[19,23,27,31,35,39],{"title":20,"path":21,"stem":22},"Use Turborepo for the EOS monorepo","\u002Feos\u002Fadrs\u002F0001-use-turborepo-for-eos","eos\u002Fadrs\u002F0001-use-turborepo-for-eos",{"title":24,"path":25,"stem":26},"Use Oxc for linting and formatting","\u002Feos\u002Fadrs\u002F0003-use-oxc-for-linting-and-formatting","eos\u002Fadrs\u002F0003-use-oxc-for-linting-and-formatting",{"title":28,"path":29,"stem":30},"Lint Markdown and validate links in CI","\u002Feos\u002Fadrs\u002F0004-lint-markdown-in-ci","eos\u002Fadrs\u002F0004-lint-markdown-in-ci",{"title":32,"path":33,"stem":34},"Use Lefthook for repository-managed Git hooks","\u002Feos\u002Fadrs\u002F0005-use-lefthook-for-repository-git-hooks","eos\u002Fadrs\u002F0005-use-lefthook-for-repository-git-hooks",{"title":36,"path":37,"stem":38},"Use Commitlint's conventional configuration","\u002Feos\u002Fadrs\u002F0006-use-commitlint-conventional-configuration","eos\u002Fadrs\u002F0006-use-commitlint-conventional-configuration",{"title":40,"path":41,"stem":42},"Use Nx for EOS task execution","\u002Feos\u002Fadrs\u002F0007-use-nx-for-eos","eos\u002Fadrs\u002F0007-use-nx-for-eos",false,{"title":45,"path":46,"stem":47,"children":48},"Harbourmaster","\u002Feos\u002Fharbourmaster","eos\u002Fharbourmaster\u002FREADME",[49,58,71,96],{"title":50,"path":51,"stem":52,"children":53,"page":43},"Decisions","\u002Feos\u002Fharbourmaster\u002Fdecisions","eos\u002Fharbourmaster\u002Fdecisions",[54],{"title":55,"path":56,"stem":57},"AI-50: GitHub auth and fork pull request policy","\u002Feos\u002Fharbourmaster\u002Fdecisions\u002Fai-50-auth-and-fork-policy","eos\u002Fharbourmaster\u002Fdecisions\u002Fai-50-auth-and-fork-policy",{"title":59,"path":60,"stem":61,"children":62,"page":43},"Explanation","\u002Feos\u002Fharbourmaster\u002Fexplanation","eos\u002Fharbourmaster\u002Fexplanation",[63,67],{"title":64,"path":65,"stem":66},"The Harbourmaster review pipeline","\u002Feos\u002Fharbourmaster\u002Fexplanation\u002Freview-pipeline","eos\u002Fharbourmaster\u002Fexplanation\u002Freview-pipeline",{"title":68,"path":69,"stem":70},"The Harbourmaster shared-action trust boundary","\u002Feos\u002Fharbourmaster\u002Fexplanation\u002Fshared-action-trust-boundary","eos\u002Fharbourmaster\u002Fexplanation\u002Fshared-action-trust-boundary",{"title":72,"path":73,"stem":74,"children":75,"page":43},"How To","\u002Feos\u002Fharbourmaster\u002Fhow-to","eos\u002Fharbourmaster\u002Fhow-to",[76,80,84,88,92],{"title":77,"path":78,"stem":79},"How to add Harbourmaster reviews to a repository","\u002Feos\u002Fharbourmaster\u002Fhow-to\u002Fadd-harbourmaster-to-a-repository","eos\u002Fharbourmaster\u002Fhow-to\u002Fadd-harbourmaster-to-a-repository",{"title":81,"path":82,"stem":83},"How to build a pilot dashboard","\u002Feos\u002Fharbourmaster\u002Fhow-to\u002Fbuild-pilot-dashboard","eos\u002Fharbourmaster\u002Fhow-to\u002Fbuild-pilot-dashboard",{"title":85,"path":86,"stem":87},"How to configure model routing","\u002Feos\u002Fharbourmaster\u002Fhow-to\u002Fconfigure-model-routing","eos\u002Fharbourmaster\u002Fhow-to\u002Fconfigure-model-routing",{"title":89,"path":90,"stem":91},"How to request a Harbourmaster review","\u002Feos\u002Fharbourmaster\u002Fhow-to\u002Frequest-a-review","eos\u002Fharbourmaster\u002Fhow-to\u002Frequest-a-review",{"title":93,"path":94,"stem":95},"How to diagnose a Harbourmaster review failure","\u002Feos\u002Fharbourmaster\u002Fhow-to\u002Ftroubleshoot-a-review","eos\u002Fharbourmaster\u002Fhow-to\u002Ftroubleshoot-a-review",{"title":97,"path":98,"stem":99,"children":100,"page":43},"Reference","\u002Feos\u002Fharbourmaster\u002Freference","eos\u002Fharbourmaster\u002Freference",[101,105,109,113,117],{"title":102,"path":103,"stem":104},"Harbourmaster command-line interface","\u002Feos\u002Fharbourmaster\u002Freference\u002Fcli","eos\u002Fharbourmaster\u002Freference\u002Fcli",{"title":106,"path":107,"stem":108},"Harbourmaster configuration","\u002Feos\u002Fharbourmaster\u002Freference\u002Fconfiguration","eos\u002Fharbourmaster\u002Freference\u002Fconfiguration",{"title":110,"path":111,"stem":112},"Harbourmaster GitHub Action","\u002Feos\u002Fharbourmaster\u002Freference\u002Fgithub-action","eos\u002Fharbourmaster\u002Freference\u002Fgithub-action",{"title":114,"path":115,"stem":116},"Harbourmaster review behavior","\u002Feos\u002Fharbourmaster\u002Freference\u002Freview-behavior","eos\u002Fharbourmaster\u002Freference\u002Freview-behavior",{"title":118,"path":119,"stem":120},"Harbourmaster telemetry","\u002Feos\u002Fharbourmaster\u002Freference\u002Ftelemetry","eos\u002Fharbourmaster\u002Freference\u002Ftelemetry",{"title":72,"path":122,"stem":123,"children":124,"page":43},"\u002Feos\u002Fhow-to","eos\u002Fhow-to",[125,129,133,137,141,145,149,153,157,161,165,169,173,177],{"title":126,"path":127,"stem":128},"How to add an external documentation source","\u002Feos\u002Fhow-to\u002Fadd-external-documentation-source","eos\u002Fhow-to\u002Fadd-external-documentation-source",{"title":130,"path":131,"stem":132},"How to connect SDLC integrations","\u002Feos\u002Fhow-to\u002Fconnect-sdlc-integrations","eos\u002Fhow-to\u002Fconnect-sdlc-integrations",{"title":134,"path":135,"stem":136},"How to consume the EOS plugin marketplace","\u002Feos\u002Fhow-to\u002Fconsume-eos-marketplace","eos\u002Fhow-to\u002Fconsume-eos-marketplace",{"title":138,"path":139,"stem":140},"How to create a spike ticket","\u002Feos\u002Fhow-to\u002Fcreate-spike-ticket","eos\u002Fhow-to\u002Fcreate-spike-ticket",{"title":142,"path":143,"stem":144},"How to finalise a repository change","\u002Feos\u002Fhow-to\u002Ffinalise-change","eos\u002Fhow-to\u002Ffinalise-change",{"title":146,"path":147,"stem":148},"How to install the EOS Docs MCP server","\u002Feos\u002Fhow-to\u002Finstall-eos-docs-mcp","eos\u002Fhow-to\u002Finstall-eos-docs-mcp",{"title":150,"path":151,"stem":152},"How to resolve a spike ticket","\u002Feos\u002Fhow-to\u002Fresolve-spike-ticket","eos\u002Fhow-to\u002Fresolve-spike-ticket",{"title":154,"path":155,"stem":156},"How to run Git hook checks","\u002Feos\u002Fhow-to\u002Frun-git-hook-checks","eos\u002Fhow-to\u002Frun-git-hook-checks",{"title":158,"path":159,"stem":160},"How to use the AI-enabled SDLC","\u002Feos\u002Fhow-to\u002Fuse-ai-enabled-sdlc","eos\u002Fhow-to\u002Fuse-ai-enabled-sdlc",{"title":162,"path":163,"stem":164},"Use the Aikido plugin","\u002Feos\u002Fhow-to\u002Fuse-aikido-plugin","eos\u002Fhow-to\u002Fuse-aikido-plugin",{"title":166,"path":167,"stem":168},"How to use Compass","\u002Feos\u002Fhow-to\u002Fuse-compass","eos\u002Fhow-to\u002Fuse-compass",{"title":170,"path":171,"stem":172},"How to use Fallow","\u002Feos\u002Fhow-to\u002Fuse-fallow","eos\u002Fhow-to\u002Fuse-fallow",{"title":174,"path":175,"stem":176},"How to use incremental mutation tests","\u002Feos\u002Fhow-to\u002Fuse-incremental-mutation-tests","eos\u002Fhow-to\u002Fuse-incremental-mutation-tests",{"title":178,"path":179,"stem":180},"How to use Scout","\u002Feos\u002Fhow-to\u002Fuse-scout","eos\u002Fhow-to\u002Fuse-scout",{"title":97,"path":182,"stem":183,"children":184,"page":43},"\u002Feos\u002Freference","eos\u002Freference",[185,189,193,197],{"title":186,"path":187,"stem":188},"Compass CLI","\u002Feos\u002Freference\u002Fcompass-cli","eos\u002Freference\u002Fcompass-cli",{"title":190,"path":191,"stem":192},"EOS marketplace skills","\u002Feos\u002Freference\u002Fmarketplace-skills","eos\u002Freference\u002Fmarketplace-skills",{"title":194,"path":195,"stem":196},"Nx task execution and cache","\u002Feos\u002Freference\u002Fnx-task-execution","eos\u002Freference\u002Fnx-task-execution",{"title":198,"path":199,"stem":200},"Scout CLI","\u002Feos\u002Freference\u002Fscout-cli","eos\u002Freference\u002Fscout-cli",{"title":202,"path":203,"stem":204,"children":205},"Product engineering","\u002Fproduct-engineering","product-engineering\u002FREADME",[206],{"title":14,"collapsed":15,"path":207,"stem":208,"children":209,"page":43},"\u002Fproduct-engineering\u002Fadrs","product-engineering\u002Fadrs",[210],{"title":211,"path":212,"stem":213},"Codify Engineering Standards as Skills","\u002Fproduct-engineering\u002Fadrs\u002F0002-codify-engineering-standards-as-skills","product-engineering\u002Fadrs\u002F0002-codify-engineering-standards-as-skills",[215,268],{"nav":216,"source":265},[217],{"title":218,"path":219,"stem":220,"children":221},"Ember","\u002Fember","ember",[222,225,257,261],{"title":223,"path":219,"stem":224},"StoreFront Documentation","ember\u002Findex",{"title":14,"path":226,"stem":227,"children":228,"page":43},"\u002Fember\u002Fadrs","ember\u002Fadrs",[229,233,237,241,245,249,253],{"title":230,"path":231,"stem":232},"Add Infection as a non-blocking mutation testing tool for Ember","\u002Fember\u002Fadrs\u002F0001-infection-mutation-testing","ember\u002Fadrs\u002F0001-infection-mutation-testing",{"title":234,"path":235,"stem":236},"Process DELETE scheduled changes before CREATE_UPDATE at the same instant","\u002Fember\u002Fadrs\u002F0002-scheduled-change-processing-order","ember\u002Fadrs\u002F0002-scheduled-change-processing-order",{"title":238,"path":239,"stem":240},"Standard rate scheduling is permanent until overridden","\u002Fember\u002Fadrs\u002F0003-standard-rate-scheduling-is-permanent","ember\u002Fadrs\u002F0003-standard-rate-scheduling-is-permanent",{"title":242,"path":243,"stem":244},"Promotions require an existing standard rate","\u002Fember\u002Fadrs\u002F0004-promotions-require-existing-standard-rate","ember\u002Fadrs\u002F0004-promotions-require-existing-standard-rate",{"title":246,"path":247,"stem":248},"UTC datetime contract between Storefront and Hub","\u002Fember\u002Fadrs\u002F0005-utc-datetime-contract-between-storefront-and-hub","ember\u002Fadrs\u002F0005-utc-datetime-contract-between-storefront-and-hub",{"title":250,"path":251,"stem":252},"Elapsed unprocessed scheduled changes do not block rate changes","\u002Fember\u002Fadrs\u002F0006-stale-scheduler-rows-do-not-block-rate-changes","ember\u002Fadrs\u002F0006-stale-scheduler-rows-do-not-block-rate-changes",{"title":254,"path":255,"stem":256},"ADR-0007: Delete ConfigureTenantAuth routing command once OIDC setup migrates to Hub","\u002Fember\u002Fadrs\u002F0007-delete-configure-tenant-auth-routing-command","ember\u002Fadrs\u002F0007-delete-configure-tenant-auth-routing-command",{"title":258,"path":259,"stem":260},"Domain Overview","\u002Fember\u002Fdomain_overview","ember\u002Fdomain_overview",{"title":262,"path":263,"stem":264},"Main Flows","\u002Fember\u002Fkey_flows","ember\u002Fkey_flows",{"collectionName":266,"label":267,"prefix":220},"external_ember","StoreFront (Ember)",{"nav":269,"source":441},[270],{"title":271,"path":272,"stem":273,"children":274},"Core","\u002Fcore","core",[275,278,306,310,332,336,346,363,420,424],{"title":276,"path":272,"stem":277},"Documentation Index","core\u002Findex",{"title":14,"path":279,"stem":280,"children":281,"page":43},"\u002Fcore\u002Fadrs","core\u002Fadrs",[282,286,290,294,298,302],{"title":283,"path":284,"stem":285},"ADR-XXXX: Short, descriptive title","\u002Fcore\u002Fadrs\u002F0000-template","core\u002Fadrs\u002F0000-template",{"title":287,"path":288,"stem":289},"ADR-1: Promotions endpoint","\u002Fcore\u002Fadrs\u002F0001-add-promotions-endpoint","core\u002Fadrs\u002F0001-add-promotions-endpoint",{"title":291,"path":292,"stem":293},"ADR-2: EOM partner report job dispatch","\u002Fcore\u002Fadrs\u002F0002-eom-report-job-dispatch","core\u002Fadrs\u002F0002-eom-report-job-dispatch",{"title":295,"path":296,"stem":297},"ADR-3: Replace PB fee waive flag with a Zero scheme override","\u002Fcore\u002Fadrs\u002F0003-replace-pb-fee-waive-with-zero-scheme","core\u002Fadrs\u002F0003-replace-pb-fee-waive-with-zero-scheme",{"title":299,"path":300,"stem":301},"ADR-4: No processor Digital Top-up capability gate in Reloadable Commercials","\u002Fcore\u002Fadrs\u002F0004-no-processor-top-up-capability-gate","core\u002Fadrs\u002F0004-no-processor-top-up-capability-gate",{"title":303,"path":304,"stem":305},"ADR-5: Sparse polymorphic sale fee override reason stamp","\u002Fcore\u002Fadrs\u002F0005-sale-fee-override-reason-stamp","core\u002Fadrs\u002F0005-sale-fee-override-reason-stamp",{"title":307,"path":308,"stem":309},"Agent Workflow","\u002Fcore\u002Fai-workflow","core\u002Fai-workflow",{"title":311,"path":312,"stem":313,"children":314},"Backend Guide","\u002Fcore\u002Fbackend","core\u002Fbackend\u002Findex",[315,316,320,324,328],{"title":311,"path":312,"stem":313},{"title":317,"path":318,"stem":319},"Backend AI Tooling","\u002Fcore\u002Fbackend\u002Fai-tooling","core\u002Fbackend\u002Fai-tooling",{"title":321,"path":322,"stem":323},"Backend Linting and Formatting","\u002Fcore\u002Fbackend\u002Flinting-and-formatting","core\u002Fbackend\u002Flinting-and-formatting",{"title":325,"path":326,"stem":327},"Backend Setup","\u002Fcore\u002Fbackend\u002Fsetup","core\u002Fbackend\u002Fsetup",{"title":329,"path":330,"stem":331},"Backend Testing","\u002Fcore\u002Fbackend\u002Ftesting","core\u002Fbackend\u002Ftesting",{"title":333,"path":334,"stem":335},"Documentation Health","\u002Fcore\u002Fdoc-health","core\u002Fdoc-health",{"title":337,"path":338,"stem":339,"children":340},"Frontend Guide","\u002Fcore\u002Ffrontend","core\u002Ffrontend\u002Findex",[341,342],{"title":337,"path":338,"stem":339},{"title":343,"path":344,"stem":345},"Frontend Development","\u002Fcore\u002Ffrontend\u002Fdevelopment","core\u002Ffrontend\u002Fdevelopment",{"title":347,"path":348,"stem":349,"children":350,"page":43},"Modules","\u002Fcore\u002Fmodules","core\u002Fmodules",[351,355],{"title":352,"path":353,"stem":354},"Connect API","\u002Fcore\u002Fmodules\u002Fconnect-api","core\u002Fmodules\u002Fconnect-api",{"title":356,"path":357,"stem":358,"children":359,"page":43},"Secure Links","\u002Fcore\u002Fmodules\u002Fsecure-links","core\u002Fmodules\u002Fsecure-links",[360],{"title":356,"path":361,"stem":362},"\u002Fcore\u002Fmodules\u002Fsecure-links\u002Fsecure-links","core\u002Fmodules\u002Fsecure-links\u002Fsecure-links",{"title":364,"path":365,"stem":366,"children":367,"page":43},"Processors","\u002Fcore\u002Fprocessors","core\u002Fprocessors",[368,372,376,380,384,388,392,396,400,404,408,412,416],{"title":369,"path":370,"stem":371},"Amazon Processor and API","\u002Fcore\u002Fprocessors\u002Famazon","core\u002Fprocessors\u002Famazon",{"title":373,"path":374,"stem":375},"Amilon Processor and API","\u002Fcore\u002Fprocessors\u002Familon","core\u002Fprocessors\u002Familon",{"title":377,"path":378,"stem":379},"Cadooz Processor and API","\u002Fcore\u002Fprocessors\u002Fcadooz","core\u002Fprocessors\u002Fcadooz",{"title":381,"path":382,"stem":383},"Choice Digital Processor and API","\u002Fcore\u002Fprocessors\u002Fchoice-digital","core\u002Fprocessors\u002Fchoice-digital",{"title":385,"path":386,"stem":387},"Diggecard processor reference","\u002Fcore\u002Fprocessors\u002Fdiggecard","core\u002Fprocessors\u002Fdiggecard",{"title":389,"path":390,"stem":391},"ePay Processor and API","\u002Fcore\u002Fprocessors\u002Fepay","core\u002Fprocessors\u002Fepay",{"title":393,"path":394,"stem":395},"GoGift v2 Processor and API","\u002Fcore\u002Fprocessors\u002Fgo-gift-v2","core\u002Fprocessors\u002Fgo-gift-v2",{"title":397,"path":398,"stem":399},"InComm Processor and API","\u002Fcore\u002Fprocessors\u002Fincomm","core\u002Fprocessors\u002Fincomm",{"title":401,"path":402,"stem":403},"Ogloba Processor and API","\u002Fcore\u002Fprocessors\u002Fogloba","core\u002Fprocessors\u002Fogloba",{"title":405,"path":406,"stem":407},"SVSECard Processor and API","\u002Fcore\u002Fprocessors\u002Fsvs-ecard","core\u002Fprocessors\u002Fsvs-ecard",{"title":409,"path":410,"stem":411},"Vananam Processor and API","\u002Fcore\u002Fprocessors\u002Fvananam","core\u002Fprocessors\u002Fvananam",{"title":413,"path":414,"stem":415},"Vouchers Depot processor reference","\u002Fcore\u002Fprocessors\u002Fvouchers-depot","core\u002Fprocessors\u002Fvouchers-depot",{"title":417,"path":418,"stem":419},"You Got a Gift Processor and API","\u002Fcore\u002Fprocessors\u002Fyou-got-a-gift","core\u002Fprocessors\u002Fyou-got-a-gift",{"title":421,"path":422,"stem":423},"Development Quickstart","\u002Fcore\u002Fsetup","core\u002Fsetup",{"title":425,"path":426,"stem":427,"children":428,"page":43},"Workflows","\u002Fcore\u002Fworkflows","core\u002Fworkflows",[429,433,437],{"title":430,"path":431,"stem":432},"Hub Endpoint Workflow","\u002Fcore\u002Fworkflows\u002Fhub-endpoints","core\u002Fworkflows\u002Fhub-endpoints",{"title":434,"path":435,"stem":436},"OpenAPI Tooling","\u002Fcore\u002Fworkflows\u002Fopenapi-tooling","core\u002Fworkflows\u002Fopenapi-tooling",{"title":438,"path":439,"stem":440},"Processor Integration Workflow","\u002Fcore\u002Fworkflows\u002Fprocessors","core\u002Fworkflows\u002Fprocessors",{"collectionName":442,"label":271,"prefix":273},"external_core",{"page":444,"surround":985},{"id":445,"title":329,"body":446,"config":980,"description":452,"extension":981,"meta":982,"navigation":15,"path":330,"seo":983,"stem":331,"__hash__":984},"external_core\u002Fcore\u002Fbackend\u002Ftesting.md",{"type":447,"value":448,"toc":963},"minimark",[449,453,458,475,478,531,534,547,552,589,593,604,634,638,649,669,675,683,686,689,844,848,855,858,875,879,888,892,895,899,911,915,927,931,943,947,959],[450,451,452],"p",{},"This guide covers running backend test suites in PhpStorm and on the command line.",[454,455,457],"h2",{"id":456},"prerequisites","Prerequisites",[459,460,461,469,472],"ul",{},[462,463,464,465],"li",{},"Complete backend environment setup: ",[466,467,468],"a",{"href":326},"docs\u002Fbackend\u002Fsetup.md",[462,470,471],{},"Ensure Docker services are running.",[462,473,474],{},"Ensure VPN access is active.",[450,476,477],{},"Before running backend tests, seed the testing database:",[479,480,485],"pre",{"className":481,"code":482,"language":483,"meta":484,"style":484},"language-shell shiki shiki-themes github-dark github-light","echo 'y' | AWS_PROFILE=internal-services docker-compose exec -T core.web .\u002Fseed-testing-local.sh\n","shell","",[486,487,488],"code",{"__ignoreMap":484},[489,490,493,497,501,505,509,512,515,519,522,525,528],"span",{"class":491,"line":492},"line",1,[489,494,496],{"class":495},"s0DvM","echo",[489,498,500],{"class":499},"sg6BJ"," 'y'",[489,502,504],{"class":503},"scx8i"," |",[489,506,508],{"class":507},"sQ3_J"," AWS_PROFILE",[489,510,511],{"class":503},"=",[489,513,514],{"class":499},"internal-services",[489,516,518],{"class":517},"s-Z4r"," docker-compose",[489,520,521],{"class":499}," exec",[489,523,524],{"class":495}," -T",[489,526,527],{"class":499}," core.web",[489,529,530],{"class":499}," .\u002Fseed-testing-local.sh\n",[450,532,533],{},"For an interactive local run, use:",[479,535,537],{"className":481,"code":536,"language":483,"meta":484,"style":484},"just seed-testing-local\n",[486,538,539],{"__ignoreMap":484},[489,540,541,544],{"class":491,"line":492},[489,542,543],{"class":517},"just",[489,545,546],{"class":499}," seed-testing-local\n",[548,549,551],"h3",{"id":550},"procedure","Procedure",[553,554,555,570,582],"ol",{},[462,556,557,558,561,562,565,566,569],{},"Add the migration name (without ",[486,559,560],{},".php",") to the ",[486,563,564],{},"INSERT INTO \\","migrations` VALUES ...",[486,567,568],{},"block in","database\u002Fschema\u002Fcore-test-schema.sql`.",[462,571,572,573,577,578,581],{},"Use batch number ",[574,575,576],"strong",{},"last batch + 1"," (insert above the ",[486,579,580],{},"add_your_migration_above_this_line"," placeholder).",[462,583,584,585,588],{},"If the migration creates tables, add the matching ",[486,586,587],{},"CREATE TABLE"," DDL to the dump. If it alters or drops tables\u002Fcolumns, amend or remove the existing DDL in the dump so it matches the new structure.",[454,590,592],{"id":591},"authoring-phpunit-tests","Authoring PHPUnit tests",[450,594,595,596,599,600,603],{},"Prefer ",[574,597,598],{},"PHPUnit 10+ PHP attributes"," over legacy docblock annotations or relying only on the ",[486,601,602],{},"test*"," method prefix:",[459,605,606,617],{},[462,607,608,609,612,613,616],{},"Mark test methods with ",[486,610,611],{},"#[Test]"," from ",[486,614,615],{},"PHPUnit\\Framework\\Attributes\\Test",".",[462,618,619,620,623,624,623,627,630,631,616],{},"Use attribute-based ",[486,621,622],{},"#[DataProvider]",", ",[486,625,626],{},"#[TestWith]",[486,628,629],{},"#[CoversClass]",", etc. where applicable, matching existing tests under ",[486,632,633],{},"tests\u002FUnit\u002F",[548,635,637],{"id":636},"test-structure-arrangeactassert-or-givenwhenthen","Test structure (Arrange–Act–Assert or Given–When–Then)",[450,639,640,641,644,645,648],{},"Every ",[574,642,643],{},"new"," test method must label its phases with ",[486,646,647],{},"\u002F\u002F"," comments.",[450,650,651,654,655,658,659,623,662,623,665,668],{},[574,652,653],{},"Default:"," ",[574,656,657],{},"Arrange \u002F Act \u002F Assert"," (",[486,660,661],{},"\u002F\u002F Arrange",[486,663,664],{},"\u002F\u002F Act",[486,666,667],{},"\u002F\u002F Assert",").",[450,670,671,674],{},[574,672,673],{},"Given \u002F When \u002F Then"," only when:",[459,676,677,680],{},[462,678,679],{},"explicitly requested (e.g. acceptance-style scenarios), or",[462,681,682],{},"updating a file that already uses Given\u002FWhen\u002FThen — stay consistent within that file.",[450,684,685],{},"Do not leave multi-step tests unlabelled.",[450,687,688],{},"Example (default style):",[479,690,694],{"className":691,"code":692,"language":693,"meta":484,"style":484},"language-php shiki shiki-themes github-dark github-light","#[Test]\npublic function it_returns_forbidden_for_unauthorized_users(): void\n{\n    \u002F\u002F Arrange\n    $user = $this->vanillaLoginRoleUser(Role::PARTNER);\n\n    \u002F\u002F Act\n    $response = $this->getAsHubUser($user, RouteAliases::SOME_ROUTE);\n\n    \u002F\u002F Assert\n    $response->assertForbidden();\n}\n","php",[486,695,696,707,728,734,741,773,779,785,813,818,824,838],{"__ignoreMap":484},[489,697,698,701,704],{"class":491,"line":492},[489,699,700],{"class":507},"#[",[489,702,703],{"class":495},"Test",[489,705,706],{"class":507},"]\n",[489,708,710,713,716,719,722,725],{"class":491,"line":709},2,[489,711,712],{"class":503},"public",[489,714,715],{"class":503}," function",[489,717,718],{"class":517}," it_returns_forbidden_for_unauthorized_users",[489,720,721],{"class":507},"()",[489,723,724],{"class":503},":",[489,726,727],{"class":503}," void\n",[489,729,731],{"class":491,"line":730},3,[489,732,733],{"class":507},"{\n",[489,735,737],{"class":491,"line":736},4,[489,738,740],{"class":739},"sryI4","    \u002F\u002F Arrange\n",[489,742,744,747,749,752,755,758,761,764,767,770],{"class":491,"line":743},5,[489,745,746],{"class":507},"    $user ",[489,748,511],{"class":503},[489,750,751],{"class":495}," $this",[489,753,754],{"class":503},"->",[489,756,757],{"class":517},"vanillaLoginRoleUser",[489,759,760],{"class":507},"(",[489,762,763],{"class":495},"Role",[489,765,766],{"class":503},"::",[489,768,769],{"class":495},"PARTNER",[489,771,772],{"class":507},");\n",[489,774,776],{"class":491,"line":775},6,[489,777,778],{"emptyLinePlaceholder":15},"\n",[489,780,782],{"class":491,"line":781},7,[489,783,784],{"class":739},"    \u002F\u002F Act\n",[489,786,788,791,793,795,797,800,803,806,808,811],{"class":491,"line":787},8,[489,789,790],{"class":507},"    $response ",[489,792,511],{"class":503},[489,794,751],{"class":495},[489,796,754],{"class":503},[489,798,799],{"class":517},"getAsHubUser",[489,801,802],{"class":507},"($user, ",[489,804,805],{"class":495},"RouteAliases",[489,807,766],{"class":503},[489,809,810],{"class":495},"SOME_ROUTE",[489,812,772],{"class":507},[489,814,816],{"class":491,"line":815},9,[489,817,778],{"emptyLinePlaceholder":15},[489,819,821],{"class":491,"line":820},10,[489,822,823],{"class":739},"    \u002F\u002F Assert\n",[489,825,827,830,832,835],{"class":491,"line":826},11,[489,828,829],{"class":507},"    $response",[489,831,754],{"class":503},[489,833,834],{"class":517},"assertForbidden",[489,836,837],{"class":507},"();\n",[489,839,841],{"class":491,"line":840},12,[489,842,843],{"class":507},"}\n",[454,845,847],{"id":846},"testing-in-phpstorm","Testing in PhpStorm",[450,849,850,851,854],{},"The Unit, Feature, Integration, and Characterisation test suites should be present in your IDE automatically and kept up to date via git (see the ",[486,852,853],{},".run"," folder).",[450,856,857],{},"To run a suite individually, select the suite and click the run button.",[859,860,861],"blockquote",{},[450,862,863,866,867,870,871,874],{},[489,864,865],{},"!WARNING","\nCheck your xdebug mode configuration in ",[486,868,869],{},"local\u002Fxdebug\u002Fxdebug.ini",". If it is not ",[486,872,873],{},"off",", test execution will be slower.",[548,876,878],{"id":877},"executing-tests-with-xdebug-enabled","Executing tests with xdebug enabled",[450,880,881,882,885,886,616],{},"If you run tests with the debug button, ",[486,883,884],{},"-dxdebug.mode=debug"," is added to the interpreter and overrides the value set in ",[486,887,869],{},[454,889,891],{"id":890},"command-line-testing","Command line testing",[450,893,894],{},"If you prefer to run tests from the command line, use the commands below.",[548,896,898],{"id":897},"characterisation","Characterisation",[479,900,902],{"className":481,"code":901,"language":483,"meta":484,"style":484},"just test-characterisation\n",[486,903,904],{"__ignoreMap":484},[489,905,906,908],{"class":491,"line":492},[489,907,543],{"class":517},[489,909,910],{"class":499}," test-characterisation\n",[548,912,914],{"id":913},"feature","Feature",[479,916,918],{"className":481,"code":917,"language":483,"meta":484,"style":484},"just test-feature\n",[486,919,920],{"__ignoreMap":484},[489,921,922,924],{"class":491,"line":492},[489,923,543],{"class":517},[489,925,926],{"class":499}," test-feature\n",[548,928,930],{"id":929},"integration","Integration",[479,932,934],{"className":481,"code":933,"language":483,"meta":484,"style":484},"just test-integration\n",[486,935,936],{"__ignoreMap":484},[489,937,938,940],{"class":491,"line":492},[489,939,543],{"class":517},[489,941,942],{"class":499}," test-integration\n",[548,944,946],{"id":945},"unit","Unit",[479,948,950],{"className":481,"code":949,"language":483,"meta":484,"style":484},"just test-unit\n",[486,951,952],{"__ignoreMap":484},[489,953,954,956],{"class":491,"line":492},[489,955,543],{"class":517},[489,957,958],{"class":499}," test-unit\n",[960,961,962],"style",{},"html pre.shiki code .s0DvM, html code.shiki .s0DvM{--shiki-dark:#79B8FF;--shiki-default:#005CC5}html pre.shiki code .sg6BJ, html code.shiki .sg6BJ{--shiki-dark:#9ECBFF;--shiki-default:#032F62}html pre.shiki code .scx8i, html code.shiki .scx8i{--shiki-dark:#F97583;--shiki-default:#D73A49}html pre.shiki code .sQ3_J, html code.shiki .sQ3_J{--shiki-dark:#E1E4E8;--shiki-default:#24292E}html pre.shiki code .s-Z4r, html code.shiki .s-Z4r{--shiki-dark:#B392F0;--shiki-default:#6F42C1}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sryI4, html code.shiki .sryI4{--shiki-dark:#6A737D;--shiki-default:#6A737D}",{"title":484,"searchDepth":730,"depth":730,"links":964},[965,968,971,974],{"id":456,"depth":709,"text":457,"children":966},[967],{"id":550,"depth":730,"text":551},{"id":591,"depth":709,"text":592,"children":969},[970],{"id":636,"depth":730,"text":637},{"id":846,"depth":709,"text":847,"children":972},[973],{"id":877,"depth":730,"text":878},{"id":890,"depth":709,"text":891,"children":975},[976,977,978,979],{"id":897,"depth":730,"text":898},{"id":913,"depth":730,"text":914},{"id":929,"depth":730,"text":930},{"id":945,"depth":730,"text":946},{},"md",{},{"title":329,"description":452},"zhwhZ4yjcshgR12QjxIEr1JvkzUYP_zBtjzZGlqybEY",[986,987],{"title":325,"path":326,"stem":327,"children":-1},{"title":333,"path":334,"stem":335,"children":-1},1789777548729]