Expert-level Codeception testing skill for PHP applications. Covers acceptance, functional, and unit testing with the Actor pattern, BDD-style syntax, Page Objects, API testing, and database helpers.
npx skills add https://github.com/PramodDutta/qaskills --skill Codeception Testing
You are an expert QA automation engineer specializing in Codeception testing for PHP applications. When the user asks you to write, review, or debug Codeception tests, follow these detailed instructions.
$I actor object (AcceptanceTester, FunctionalTester, UnitTester). Write tests as user stories: $I->amOnPage(), $I->see(), $I->click().tests/_support/Page/._before() hooks for setup and database transactions for clean state.Always organize Codeception projects with this structure:
tests/
Acceptance/
LoginCest.php
DashboardCest.php
CheckoutCest.php
Functional/
UserCest.php
ApiCest.php
Unit/
Services/
PaymentServiceTest.php
Models/
UserTest.php
_support/
AcceptanceTester.php
FunctionalTester.php
UnitTester.php
Page/
Acceptance/
LoginPage.php
DashboardPage.php
Functional/
UserPage.php
Helper/
Acceptance.php
Functional.php
Api.php
Data/
TestDataFactory.php
_data/
dump.sql
fixtures/
_output/
codeception.yml
tests/Acceptance.suite.yml
tests/Functional.suite.yml
tests/Unit.suite.yml
composer require --dev codeception/codeception
composer require --dev codeception/module-webdriver
composer require --dev codeception/module-phpbrowser
composer require --dev codeception/module-asserts
composer require --dev codeception/module-db
composer require --dev codeception/module-rest
# Initialize project structure
php vendor/bin/codecept bootstrap
# Generate suites
php vendor/bin/codecept generate:suite acceptance
php vendor/bin/codecept generate:suite functional
php vendor/bin/codecept generate:suite api
actor: AcceptanceTester
modules:
enabled:
- WebDriver:
url: http://localhost:8000
browser: chrome
window_size: 1920x1080
capabilities:
chromeOptions:
args:
- "--headless"
- "--no-sandbox"
- "--disable-gpu"
- \Tests\Support\Helper\Acceptance
step_decorators:
- \Codeception\Step\Retry
actor: FunctionalTester
modules:
enabled:
- PhpBrowser:
url: http://localhost:8000
- \Tests\Support\Helper\Functional
- Asserts
<?php
namespace Tests\Acceptance;
use Tests\Support\AcceptanceTester;
use Tests\Support\Page\Acceptance\LoginPage;
class LoginCest
{
public function _before(AcceptanceTester $I): void
{
$I->amOnPage('/');
}
public function loginWithValidCredentials(AcceptanceTester $I): void
{
$I->amOnPage('/login');
$I->fillField('#email', '[email protected]');
$I->fillField('#password', 'password123');
$I->click('button[type=submit]');
$I->waitForElement('.dashboard', 10);
$I->see('Welcome', '.welcome-message');
$I->seeCurrentUrlEquals('/dashboard');
}
public function loginShowsErrorForInvalidCredentials(AcceptanceTester $I): void
{
$I->amOnPage('/login');
$I->fillField('#email', '[email protected]');
$I->fillField('#password', 'wrong');
$I->click('button[type=submit]');
$I->waitForElement('.error-message', 5);
$I->see('Invalid credentials', '.error-message');
$I->seeCurrentUrlEquals('/login');
}
public function loginRequiresAllFields(AcceptanceTester $I): void
{
$I->amOnPage('/login');
$I->click('button[type=submit]');
$I->see('Email is required');
$I->see('Password is required');
}
}
// Navigation
$I->amOnPage('/path');
$I->seeCurrentUrlEquals('/expected');
$I->seeCurrentUrlMatches('~^/users/\d+$~');
$I->seeInCurrentUrl('/partial');
// Forms
$I->fillField('#email', 'value');
$I->fillField('Email', 'value'); // by label
$I->selectOption('#role', 'Admin');
$I->checkOption('#agree');
$I->uncheckOption('#newsletter');
$I->attachFile('#avatar', 'photo.jpg');
$I->click('Submit');
$I->click('button[type=submit]');
$I->click(['css' => '.submit-btn']);
// Assertions
$I->see('text');
$I->see('text', '.selector');
$I->dontSee('error');
$I->seeElement('#element');
$I->dontSeeElement('.hidden');
$I->seeInField('#email', '[email protected]');
$I->seeCheckboxIsChecked('#agree');
$I->seeNumberOfElements('.item', 5);
$I->seeLink('Click Here', '/url');
// Waiting (WebDriver only)
$I->waitForElement('.element', 10);
$I->waitForElementVisible('.modal', 5);
$I->waitForElementNotVisible('.spinner', 15);
$I->waitForText('Loaded', 10, '.container');
$I->wait(1); // avoid -- only for debugging
// Grabbing values
$text = $I->grabTextFrom('.element');
$value = $I->grabValueFrom('#input');
$attr = $I->grabAttributeFrom('a', 'href');
$count = $I->grabNumRecords('users', ['status' => 'active']);
// Cookies and sessions
$I->setCookie('name', 'value');
$I->grabCookie('name');
$I->resetCookie('name');
<?php
namespace Tests\Support\Page\Acceptance;
use Tests\Support\AcceptanceTester;
class LoginPage
{
public static string $url = '/login';
public static string $emailField = '#email';
public static string $passwordField = '#password';
public static string $submitButton = 'button[type=submit]';
public static string $errorMessage = '.error-message';
public static string $welcomeMessage = '.welcome-message';
protected AcceptanceTester $I;
public function __construct(AcceptanceTester $I)
{
$this->I = $I;
}
public function open(): self
{
$this->I->amOnPage(self::$url);
return $this;
}
public function loginAs(string $email, string $password): void
{
$this->I->fillField(self::$emailField, $email);
$this->I->fillField(self::$passwordField, $password);
$this->I->click(self::$submitButton);
}
public function seeError(string $message): void
{
$this->I->waitForElement(self::$errorMessage, 5);
$this->I->see($message, self::$errorMessage);
}
public function seeWelcome(): void
{
$this->I->waitForElement(self::$welcomeMessage, 10);
$this->I->see('Welcome', self::$welcomeMessage);
}
}
<?php
namespace Tests\Acceptance;
use Tests\Support\AcceptanceTester;
use Tests\Support\Page\Acceptance\LoginPage;
class LoginWithPageObjectCest
{
private LoginPage $loginPage;
public function _before(AcceptanceTester $I): void
{
$this->loginPage = new LoginPage($I);
}
public function successfulLogin(AcceptanceTester $I): void
{
$this->loginPage->open()
->loginAs('[email protected]', 'password123');
$this->loginPage->seeWelcome();
$I->seeCurrentUrlEquals('/dashboard');
}
public function invalidLogin(AcceptanceTester $I): void
{
$this->loginPage->open()
->loginAs('[email protected]', 'wrong');
$this->loginPage->seeError('Invalid credentials');
}
}
actor: ApiTester
modules:
enabled:
- REST:
url: http://localhost:8000/api
depends: PhpBrowser
part: Json
- Asserts
<?php
namespace Tests\Api;
use Tests\Support\ApiTester;
class UserApiCest
{
private string $authToken;
public function _before(ApiTester $I): void
{
$I->haveHttpHeader('Content-Type', 'application/json');
$I->haveHttpHeader('Accept', 'application/json');
}
public function getUsersList(ApiTester $I): void
{
$I->sendGet('/users');
$I->seeResponseCodeIs(200);
$I->seeResponseIsJson();
$I->seeResponseContainsJson(['status' => 'success']);
$I->seeResponseJsonMatchesJsonPath('$.data[*].id');
}
public function createUser(ApiTester $I): void
{
$I->sendPost('/users', [
'name' => 'Alice',
'email' => '[email protected]',
'role' => 'user',
]);
$I->seeResponseCodeIs(201);
$I->seeResponseContainsJson([
'name' => 'Alice',
'email' => '[email protected]',
]);
}
public function deleteUserRequiresAuth(ApiTester $I): void
{
$I->sendDelete('/users/1');
$I->seeResponseCodeIs(401);
}
}
<?php
namespace Tests\Functional;
use Tests\Support\FunctionalTester;
class DatabaseCest
{
public function userIsCreatedInDatabase(FunctionalTester $I): void
{
$I->haveInDatabase('users', [
'name' => 'Alice',
'email' => '[email protected]',
'created_at' => date('Y-m-d H:i:s'),
]);
$I->seeInDatabase('users', [
'email' => '[email protected]',
]);
$count = $I->grabNumRecords('users', ['status' => 'active']);
$I->assertGreaterThan(0, $count);
}
public function deletedUserIsRemoved(FunctionalTester $I): void
{
$I->haveInDatabase('users', ['email' => '[email protected]', 'name' => 'Temp']);
// perform delete action
$I->dontSeeInDatabase('users', ['email' => '[email protected]']);
}
}
<?php
namespace Tests\Support\Helper;
use Codeception\Module;
class Acceptance extends Module
{
public function loginAsAdmin(): void
{
$I = $this->getModule('WebDriver');
$I->amOnPage('/login');
$I->fillField('#email', '[email protected]');
$I->fillField('#password', 'admin123');
$I->click('button[type=submit]');
$I->waitForElement('.dashboard', 10);
}
public function seeFlashMessage(string $message): void
{
$I = $this->getModule('WebDriver');
$I->waitForElement('.flash-message', 5);
$I->see($message, '.flash-message');
}
public function clearSession(): void
{
$I = $this->getModule('WebDriver');
$I->resetCookie('PHPSESSID');
$I->reloadPage();
}
}
name: Codeception Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
services:
mysql:
image: mysql:8.0
env:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: test_db
ports:
- 3306:3306
steps:
- uses: actions/checkout@v4
- uses: shivammathur/setup-php@v2
with:
php-version: '8.3'
extensions: mbstring, pdo_mysql
- run: composer install --prefer-dist
- run: php artisan serve &
- run: php vendor/bin/codecept run acceptance --html
- uses: actions/upload-artifact@v4
if: always()
with:
name: codeception-report
path: tests/_output/
_before/_after hooks over procedural Cept files.loginShowsErrorForInvalidCredentials.codeception.yml environments (--env ci, --env local) to swap URLs, credentials, and driver settings without code changes.Db module with cleanup: true to wrap each test in a transaction and rollback after.waitForElement or waitForText instead of wait() for dynamic content. Hard waits mask timing issues.@group smoke, @group regression for selective CI execution: codecept run --group smoke._output/ and upload them in CI for debugging.10. Keep acceptance tests focused -- Acceptance tests are slow. Test critical user paths only. Push detailed logic validation to functional and unit layers.
_before hooks, dependency injection, or proper test organization.$I->click('#btn-submit-v2') scattered across tests break when HTML changes. Use Page Objects.testCreateUser followed by testDeleteUser that share state create fragile, unparallelizable suites.$I->see(), $I->seeInDatabase() loses Codeception's reporting and retry capabilities.$I->wait(5) wastes time on fast pages and is insufficient on slow ones. Always wait for specific conditions.@dataProvider or @example annotations.10. Skipping the functional layer -- Jumping from unit tests to acceptance tests leaves a gap. Functional tests catch framework-level bugs without browser overhead.
# Run all suites
php vendor/bin/codecept run
# Run specific suite
php vendor/bin/codecept run acceptance
php vendor/bin/codecept run functional
php vendor/bin/codecept run unit
# Run specific test
php vendor/bin/codecept run acceptance LoginCest
php vendor/bin/codecept run acceptance LoginCest:loginWithValidCredentials
# Run with options
php vendor/bin/codecept run --steps # Show step-by-step output
php vendor/bin/codecept run --html # Generate HTML report
php vendor/bin/codecept run --group smoke # Run tagged group
php vendor/bin/codecept run --env ci # Use CI environment config
php vendor/bin/codecept run -f # Fail fast on first error
Toolkit for interacting with and testing local web applications using Playwright. Supports verifying frontend functionality, debugging UI behavior, capturing browser screenshots, and viewing browser logs.
Use when implementation is complete, all tests pass, and you need to decide how to integrate the work - guides completion of development work by presenting structured options for merge, PR, or cleanup
Use when implementing any feature or bugfix, before writing implementation code
Use when encountering any bug, test failure, or unexpected behavior, before proposing fixes
Use when about to claim work is complete, fixed, or passing, before committing or creating PRs - requires running verification commands and confirming output before making any success claims; evidence before assertions always
Expert guidance for systematic backtesting of trading strategies. Use when developing, testing, stress-testing, or validating quantitative trading strategies. Covers "beating ideas to death" methodology, parameter robustness testing, slippage modeling, bias prevention, and interpreting backtest results. Applicable when user asks about backtesting, strategy validation, robustness testing, avoiding overfitting, or systematic trading development.
Cloud laboratory platform for automated protein testing and validation. Use when designing proteins and needing experimental validation including binding assays, expression testing, thermostability measurements, enzyme activity assays, or protein sequence optimization. Also use for submitting experiments via API, tracking experiment status, downloading results, optimizing protein sequences for better expression using computational tools (NetSolP, SoluProt, SolubleMPNN, ESM), or managing protein design workflows with wet-lab validation.
This skill should be used for time series machine learning tasks including classification, regression, clustering, forecasting, anomaly detection, segmentation, and similarity search. Use when working with temporal data, sequential patterns, or time-indexed observations requiring specialized algorithms beyond standard ML approaches. Particularly suited for univariate and multivariate time series analysis with scikit-learn compatible APIs.
Take pramoddutta/codeception testing from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.