本文目录导读:

在 PHP 中进行模型测试,通常涉及单元测试和集成测试,以下是系统性的测试方法:
基础测试框架配置
使用 PHPUnit
composer require --dev phpunit/phpunit
phpunit.xml 配置
<?xml version="1.0" encoding="UTF-8"?>
<phpunit bootstrap="vendor/autoload.php"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true">
<testsuites>
<testsuite name="Model Test Suite">
<directory>tests/Models</directory>
</testsuite>
</testsuites>
</phpunit>
模型单元测试示例
Laravel 模型测试
<?php
namespace Tests\Unit\Models;
use Tests\TestCase;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
class UserModelTest extends TestCase
{
use RefreshDatabase;
/** @test */
public function user_can_be_created()
{
$user = User::create([
'name' => 'John Doe',
'email' => 'john@example.com',
'password' => bcrypt('secret123')
]);
$this->assertDatabaseHas('users', [
'email' => 'john@example.com'
]);
$this->assertInstanceOf(User::class, $user);
}
/** @test */
public function user_has_correct_fillable_fields()
{
$user = new User();
$this->assertEquals([
'name',
'email',
'password',
], $user->getFillable());
}
/** @test */
public function user_password_is_hashed()
{
$user = User::create([
'name' => 'Jane Doe',
'email' => 'jane@example.com',
'password' => 'plain-password'
]);
$this->assertNotEquals('plain-password', $user->password);
$this->assertTrue(password_verify('plain-password', $user->password));
}
}
纯 PHP 模型测试
<?php
namespace Tests\Unit;
use PHPUnit\Framework\TestCase;
use App\Models\Product;
class ProductModelTest extends TestCase
{
/** @var Product */
private $product;
protected function setUp(): void
{
parent::setUp();
$this->product = new Product();
}
/** @test */
public function product_can_calculate_total_price()
{
$this->product->setPrice(100);
$this->product->setQuantity(3);
$this->assertEquals(300, $this->product->getTotalPrice());
}
/** @test */
public function product_name_is_required()
{
$this->expectException(\InvalidArgumentException::class);
$this->product->setName('');
}
}
关系测试
测试关联关系
<?php
namespace Tests\Feature\Models;
use Tests\TestCase;
use App\Models\Order;
use App\Models\User;
use App\Models\OrderItem;
use Illuminate\Foundation\Testing\RefreshDatabase;
class OrderModelTest extends TestCase
{
use RefreshDatabase;
/** @test */
public function order_belongs_to_user()
{
$user = User::factory()->create();
$order = Order::factory()->create(['user_id' => $user->id]);
$this->assertInstanceOf(User::class, $order->user);
$this->assertEquals($user->id, $order->user->id);
}
/** @test */
public function order_has_many_order_items()
{
$order = Order::factory()->create();
OrderItem::factory()->count(3)->create(['order_id' => $order->id]);
$this->assertCount(3, $order->orderItems);
$this->assertInstanceOf(OrderItem::class, $order->orderItems->first());
}
/** @test */
public function order_can_calculate_total()
{
$order = Order::factory()->create();
OrderItem::factory()->create([
'order_id' => $order->id,
'price' => 100,
'quantity' => 2
]);
OrderItem::factory()->create([
'order_id' => $order->id,
'price' => 50,
'quantity' => 1
]);
$this->assertEquals(250, $order->calculateTotal());
}
}
使用工厂模式测试
定义 Model Factory
<?php
namespace Database\Factories;
use App\Models\User;
use Illuminate\Database\Eloquent\Factories\Factory;
class UserFactory extends Factory
{
protected $model = User::class;
public function definition()
{
return [
'name' => $this->faker->name,
'email' => $this->faker->unique()->safeEmail,
'password' => bcrypt('password'),
];
}
public function admin()
{
return $this->state([
'is_admin' => true,
]);
}
}
使用工厂测试
/** @test */
public function admin_users_have_admin_privileges()
{
$admin = User::factory()->admin()->create();
$regularUser = User::factory()->create();
$this->assertTrue($admin->isAdmin());
$this->assertFalse($regularUser->isAdmin());
}
数据验证测试
/** @test */
public function model_validates_required_fields()
{
$order = new Order(['user_id' => null]);
$this->assertFalse($order->validate());
$this->assertArrayHasKey('user_id', $order->errors());
}
/** @test */
public function model_validates_email_format()
{
$user = new User(['email' => 'invalid-email']);
$this->assertFalse($user->validate());
$this->assertArrayHasKey('email', $user->errors());
}
模拟外部依赖
use Mockery;
/** @test */
public function model_calls_external_service()
{
$externalService = Mockery::mock(ExternalService::class);
$externalService->shouldReceive('sendEmail')
->once()
->andReturn(true);
$user = new User();
$user->setExternalService($externalService);
$result = $user->sendWelcomeEmail();
$this->assertTrue($result);
}
性能测试
/** @test */
public function model_query_uses_indexes()
{
$queries = 0;
DB::listen(function ($query) use (&$queries) {
$queries++;
// 检查查询是否使用索引
$this->assertStringNotContainsString('TABLE SCAN', $query->sql);
});
// 执行查询
User::where('email', 'john@example.com')->first();
$this->assertLessThan(5, $queries);
}
最佳实践建议
测试层级划分:
- 单元测试 - 单个模型方法
- 集成测试 - 模型与数据库交互
- 特征测试 - 完整业务流
关键要点:
/** @test */
public function test_model_scope()
{
// 测试查询作用域
$activeUsers = User::active()->get();
$this->assertNotEmpty($activeUsers);
}
/** @test */
public function test_model_accessors()
{
$user = User::factory()->create([
'first_name' => 'John',
'last_name' => 'Doe'
]);
$this->assertEquals('John Doe', $user->full_name);
}
/** @test */
public function test_model_mutators()
{
$user = new User();
$user->email = 'TEST@example.com';
$this->assertEquals('test@example.com', $user->email);
}
运行测试
# 运行所有测试 php artisan test # 运行特定测试文件 php artisan test --filter=UserModelTest # 运行测试并生成覆盖率 php vendor/bin/phpunit --coverage-html coverage/
这样系统化的测试方法能确保你的 PHP 模型逻辑正确、可靠且可维护。