PHP testing mastery - PHPUnit 11, Pest 3, TDD, mocking, and CI/CD integration
Master PHP testing with PHPUnit 11, Pest 3, TDD, mocking, and CI/CD integration. Use when writing tests, debugging failures, or setting up CI pipelines for PHP projects.
/plugin marketplace add pluginagentmarketplace/custom-plugin-php/plugin install php-assistant@pluginagentmarketplace-phpThis skill inherits all available tools. When active, it can use any tool Claude has access to.
assets/config.yamlassets/schema.jsonreferences/GUIDE.mdreferences/PATTERNS.mdscripts/validate.pyAtomic skill for mastering PHP testing strategies
Comprehensive skill for PHP testing covering PHPUnit 11, Pest 3, TDD methodology, mocking strategies, and CI/CD integration.
interface SkillParams {
topic:
| "phpunit" // PHPUnit framework
| "pest" // Pest framework
| "mocking" // Mockery, Prophecy
| "tdd" // Test-driven development
| "integration" // Database, API testing
| "ci-cd"; // GitHub Actions, GitLab CI
level: "beginner" | "intermediate" | "advanced";
framework?: "laravel" | "symfony" | "none";
coverage_goal?: number;
}
validation:
topic:
required: true
allowed: [phpunit, pest, mocking, tdd, integration, ci-cd]
level:
required: true
framework:
default: "none"
beginner:
- Test case structure
- Basic assertions
- Running tests
intermediate:
- Data providers
- Fixtures (setUp/tearDown)
- Test doubles
advanced:
- Attributes (#[Test], #[DataProvider])
- Code coverage
- Parallel execution
beginner:
- Expectations syntax
- Test organization
- Groups and filtering
intermediate:
- Higher-order tests
- Datasets
- Hooks
advanced:
- Mutation testing (--mutate)
- Architecture testing
- Custom expectations
beginner:
- Mock basics
- Stubs vs mocks
- Simple expectations
intermediate:
- Partial mocks
- Spies
- Argument matching
advanced:
- Mock chains
- Return callbacks
- Exception testing
errors:
TEST_FAILURE:
code: "TEST_001"
recovery: "Compare expected vs actual, check setup"
MOCK_ERROR:
code: "TEST_002"
recovery: "Verify mock expectations and injection"
FLAKY_TEST:
code: "TEST_003"
recovery: "Check isolation, fix race conditions"
retry:
max_attempts: 2
backoff:
type: linear
delay_ms: 100
<?php
declare(strict_types=1);
namespace Tests\Unit;
use App\Services\Calculator;
use PHPUnit\Framework\TestCase;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\Attributes\DataProvider;
final class CalculatorTest extends TestCase
{
private Calculator $calculator;
protected function setUp(): void
{
$this->calculator = new Calculator();
}
#[Test]
public function it_adds_two_numbers(): void
{
$result = $this->calculator->add(2, 3);
$this->assertSame(5, $result);
}
#[Test]
#[DataProvider('divisionProvider')]
public function it_divides_correctly(int $a, int $b, float $expected): void
{
$result = $this->calculator->divide($a, $b);
$this->assertEqualsWithDelta($expected, $result, 0.0001);
}
public static function divisionProvider(): array
{
return [
'whole' => [10, 2, 5.0],
'decimal' => [7, 2, 3.5],
];
}
#[Test]
public function it_throws_on_division_by_zero(): void
{
$this->expectException(\DivisionByZeroError::class);
$this->calculator->divide(10, 0);
}
}
<?php
use App\Models\User;
use function Pest\Laravel\{actingAs, post, assertDatabaseHas};
describe('User Registration', function () {
it('allows new user registration', function () {
post('/register', [
'name' => 'John',
'email' => 'john@example.com',
'password' => 'password',
'password_confirmation' => 'password',
])
->assertRedirect('/dashboard');
assertDatabaseHas('users', ['email' => 'john@example.com']);
});
it('requires valid email', function () {
post('/register', ['email' => 'invalid'])
->assertSessionHasErrors('email');
});
})->group('auth');
<?php
declare(strict_types=1);
namespace Tests\Unit;
use App\Services\UserService;
use App\Repositories\UserRepository;
use Mockery;
use PHPUnit\Framework\TestCase;
final class UserServiceTest extends TestCase
{
public function test_creates_user(): void
{
// Arrange
$repository = Mockery::mock(UserRepository::class);
$repository
->shouldReceive('create')
->once()
->with(['name' => 'John', 'email' => 'john@test.com'])
->andReturn(new User(['id' => 1]));
$service = new UserService($repository);
// Act
$user = $service->createUser([
'name' => 'John',
'email' => 'john@test.com',
]);
// Assert
$this->assertEquals(1, $user->id);
}
protected function tearDown(): void
{
Mockery::close();
}
}
name: Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.3'
coverage: xdebug
- name: Install dependencies
run: composer install --no-progress
- name: Run tests
run: vendor/bin/phpunit --coverage-clover coverage.xml
- name: Upload coverage
uses: codecov/codecov-action@v3
| Problem | Cause | Solution |
|---|---|---|
| Tests pass locally, fail in CI | Environment differences | Check PHP version, database state |
| Mock not called | Not injected | Verify DI, don't instantiate inside class |
| Database pollution | Shared state | Use RefreshDatabase trait |
| Slow tests | Too many DB operations | Use mocks, run parallel |
| Metric | Target |
|---|---|
| Code coverage | ≥80% |
| Test speed | <5 min full suite |
| Flaky rate | 0% |
| Test isolation | 100% |
Skill("php-testing", {topic: "mocking", level: "intermediate"})
This skill should be used when the user asks to "create an agent", "add an agent", "write a subagent", "agent frontmatter", "when to use description", "agent examples", "agent tools", "agent colors", "autonomous agent", or needs guidance on agent structure, system prompts, triggering conditions, or agent development best practices for Claude Code plugins.
This skill should be used when the user asks to "create a slash command", "add a command", "write a custom command", "define command arguments", "use command frontmatter", "organize commands", "create command with file references", "interactive command", "use AskUserQuestion in command", or needs guidance on slash command structure, YAML frontmatter fields, dynamic arguments, bash execution in commands, user interaction patterns, or command development best practices for Claude Code.
This skill should be used when the user asks to "create a hook", "add a PreToolUse/PostToolUse/Stop hook", "validate tool use", "implement prompt-based hooks", "use ${CLAUDE_PLUGIN_ROOT}", "set up event-driven automation", "block dangerous commands", or mentions hook events (PreToolUse, PostToolUse, Stop, SubagentStop, SessionStart, SessionEnd, UserPromptSubmit, PreCompact, Notification). Provides comprehensive guidance for creating and implementing Claude Code plugin hooks with focus on advanced prompt-based hooks API.