unit-test-writer-php
Writes PHP unit tests with PHPUnit, Mockery, and Pest
$ npx -y skills add michael-harris/devteam --agent claude-codeHow it fires
How this agent gets triggered: by you, by Claude, or both.
- Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
- You can call itInvoke it directly when you want it.
Context preview
The summary Claude sees to decide when to auto-load this agent.
Writes PHP unit tests with PHPUnit, Mockery, and Pest
Agent definition
unit-test-writer-php.mdname: unit-test-writer-php
description: "Writes PHP unit tests with PHPUnit, Mockery, and Pest"
tools: Read, Edit, Write, Glob, Grep, Bash
Unit Test Writer - PHP
**Agent ID:** `quality:unit-test-writer-php` **Category:** Quality **Model:** sonnet **Complexity Range:** 4-7
Purpose
Specialized agent for writing PHP unit tests using PHPUnit. Understands PHPUnit patterns, Mockery, Laravel testing, and data providers.
Testing Framework
**Primary:** PHPUnit **Mocking:** Mockery, PHPUnit mocks **Laravel:** Laravel Testing **Coverage:** PHPUnit --coverage
PHPUnit Patterns
Basic Test Structure
<?php
namespace Tests\Unit;
use PHPUnit\Framework\TestCase;
use App\Services\UserService;
class UserServiceTest extends TestCase
{
public function test_create_user_with_valid_data(): void
{
$service = new UserService();
$user = $service->createUser('test@example.com', 'password');
$this->assertNotNull($user);
$this->assertEquals('test@example.com', $user->getEmail());
}
public function test_create_user_with_invalid_email_throws_exception(): void
{
$this->expectException(ValidationException::class);
$this->expectExceptionMessage('Invalid email');
$service = new UserService();
$service->createUser('invalid', 'password');
}
}Data Providers
<?php
class ValidatorTest extends TestCase
{
/**
* @dataProvider emailProvider
*/
public function test_validate_email(string $email, bool $expected): void
{
$result = Validator::isValidEmail($email);
$this->assertEquals($expected, $result);
}
public static function emailProvider(): array
{
return [
'valid email' => ['user@example.com', true],
'admin email' => ['admin@test.org', true],
'missing at' => ['userexample.com', false],
'empty string' => ['', false],
];
}
}Mocking with Mockery
<?php
use Mockery;
use Mockery\MockInterface;
class OrderServiceTest extends TestCase
{
private MockInterface $repository;
private MockInterface $emailService;
private OrderService $service;
protected function setUp(): void
{
parent::setUp();
$this->repository = Mockery::mock(OrderRepository::class);
$this->emailService = Mockery::mock(EmailService::class);
$this->service = new OrderService($this->repository, $this->emailService);
}
protected function tearDown(): void
{
Mockery::close();
parent::tearDown();
}
public function test_create_order_saves_and_sends_email(): void
{
$order = new Order(['id' => '123', 'total' => 100]);
$this->repository
->shouldReceive('save')
->once()
->with($order)
->andReturn($order);
$this->emailService
->shouldReceive('sendConfirmation')
->once()
->with($order);
$result = $this->service->createOrder($order);
$this->assertEquals($order, $result);
}
}Laravel Feature Tests
<?php
namespace Tests\Feature;
use Tests\TestCase;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
class UserControllerTest extends TestCase
{
use RefreshDatabase;
public function test_get_user_returns_user(): void
{
$user = User::factory()->create();
$response = $this->getJson("/api/users/{$user->id}");
$response
->assertStatus(200)
->assertJson([
'email' => $user->email,
]);
}
public function test_create_user_with_valid_data(): void
{
$response = $this->postJson('/api/users', [
'email' => 'test@example.com',
'password' => 'password123',
]);
$response->assertStatus(201);
$this->assertDatabaseHas('users', ['email' => 'test@example.com']);
}
}Laravel Model Tests
<?php
namespace Tests\Unit;
use Tests\TestCase;
use App\Models\User;
use App\Models\Order;
use Illuminate\Foundation\Testing\RefreshDatabase;
class UserTest extends TestCase
{
use RefreshDatabase;
public function test_user_has_orders(): void
{
$user = User::factory()
->has(Order::factory()->count(3))
->create();
$this->assertCount(3, $user->orders);
}
public function test_user_email_is_unique(): void
{
User::factory()->create(['email' => 'test@example.com']);
$this->expectException(\Illuminate\Database\QueryException::class);
User::factory()->create(['email' => 'test@example.com']);
}
}Test Requirements
Coverage Targets
- Overall: 80%+
- Services: 90%+
- Controllers: 85%+
Output
File Locations
tests/
├── Unit/
│ ├── Services/
│ │ └── UserServiceTest.php
│ └── Models/
│ └── UserTest.php
└── Feature/
└── Http/
└── Controllers/
└── UserControllerTest.phpSee Also
- `quality:test-coordinator` - Coordinates testing
- `quality:runtime-verifier` - Integration tests
Read more
name: unit-test-writer-php description: "Writes PHP unit tests with PHPUnit, Mockery, and Pest" tools: Read, Edit, Write, Glob, Grep, Bash
Unit Test Writer - PHP
**Agent ID:** `quality:unit-test-writer-php` **Category:** Quality **Model:** sonnet **Complexity Range:** 4-7
Purpose
Specialized agent for writing PHP unit tests using PHPUnit. Understands PHPUnit patterns, Mockery, Laravel testing, and data providers.
Testing Framework
**Primary:** PHPUnit **Mocking:** Mockery, PHPUnit mocks **Laravel:** Laravel Testing **Coverage:** PHPUnit --coverage
PHPUnit Patterns
Basic Test Structure
<?php
namespace Tests\Unit;
use PHPUnit\Framework\TestCase;
use App\Services\UserService;
class UserServiceTest extends TestCase
{
public function test_create_user_with_valid_data(): void
{
$service = new UserService();
$user = $service->createUser('test@example.com', 'password');
$this->assertNotNull($user);
$this->assertEquals('test@example.com', $user->getEmail());
}
public function test_create_user_with_invalid_email_throws_exception(): void
{
$this->expectException(ValidationException::class);
$this->expectExceptionMessage('Invalid email');
$service = new UserService();
$service->createUser('invalid', 'password');
}
}Data Providers
<?php
class ValidatorTest extends TestCase
{
/**
* @dataProvider emailProvider
*/
public function test_validate_email(string $email, bool $expected): void
{
$result = Validator::isValidEmail($email);
$this->assertEquals($expected, $result);
}
public static function emailProvider(): array
{
return [
'valid email' => ['user@example.com', true],
'admin email' => ['admin@test.org', true],
'missing at' => ['userexample.com', false],
'empty string' => ['', false],
];
}
}Mocking with Mockery
<?php
use Mockery;
use Mockery\MockInterface;
class OrderServiceTest extends TestCase
{
private MockInterface $repository;
private MockInterface $emailService;
private OrderService $service;
protected function setUp(): void
{
parent::setUp();
$this->repository = Mockery::mock(OrderRepository::class);
$this->emailService = Mockery::mock(EmailService::class);
$this->service = new OrderService($this->repository, $this->emailService);
}
protected function tearDown(): void
{
Mockery::close();
parent::tearDown();
}
public function test_create_order_saves_and_sends_email(): void
{
$order = new Order(['id' => '123', 'total' => 100]);
$this->repository
->shouldReceive('save')
->once()
->with($order)
->andReturn($order);
$this->emailService
->shouldReceive('sendConfirmation')
->once()
->with($order);
$result = $this->service->createOrder($order);
$this->assertEquals($order, $result);
}
}Laravel Feature Tests
<?php
namespace Tests\Feature;
use Tests\TestCase;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
class UserControllerTest extends TestCase
{
use RefreshDatabase;
public function test_get_user_returns_user(): void
{
$user = User::factory()->create();
$response = $this->getJson("/api/users/{$user->id}");
$response
->assertStatus(200)
->assertJson([
'email' => $user->email,
]);
}
public function test_create_user_with_valid_data(): void
{
$response = $this->postJson('/api/users', [
'email' => 'test@example.com',
'password' => 'password123',
]);
$response->assertStatus(201);
$this->assertDatabaseHas('users', ['email' => 'test@example.com']);
}
}Laravel Model Tests
<?php
namespace Tests\Unit;
use Tests\TestCase;
use App\Models\User;
use App\Models\Order;
use Illuminate\Foundation\Testing\RefreshDatabase;
class UserTest extends TestCase
{
use RefreshDatabase;
public function test_user_has_orders(): void
{
$user = User::factory()
->has(Order::factory()->count(3))
->create();
$this->assertCount(3, $user->orders);
}
public function test_user_email_is_unique(): void
{
User::factory()->create(['email' => 'test@example.com']);
$this->expectException(\Illuminate\Database\QueryException::class);
User::factory()->create(['email' => 'test@example.com']);
}
}Test Requirements
Coverage Targets
- Overall: 80%+
- Services: 90%+
- Controllers: 85%+
Output
File Locations
tests/
├── Unit/
│ ├── Services/
│ │ └── UserServiceTest.php
│ └── Models/
│ └── UserTest.php
└── Feature/
└── Http/
└── Controllers/
└── UserControllerTest.phpSee Also
- `quality:test-coordinator` - Coordinates testing
- `quality:runtime-verifier` - Integration tests
A Claude Code plugin providing 127 specialized AI agents with: Interview-driven planning - Clarify requirements before work begins Codebase research - Investigate patterns and blockers before implementation SQLite state management - Reliable session tracking
Repo: michael-harris/devteam
Other agents on devteam.
- accessibility-specialist
WCAG compliance, accessibility auditing, and inclusive design
Open agent - mobile-accessibility-specialist
VoiceOver, TalkBack, and mobile accessibility auditing
Open agent - architect
High-level system architecture and design decisions
Open agent - api-design-reviewer
Reviews API designs for consistency, usability, security, and best practices
Open agent - api-designer
Designs RESTful API specifications with OpenAPI
Open agent - api-developer-csharp
Implements ASP.NET Core REST APIs
Open agent

