PHP 怎么给代码加测试

wen PHP项目 2

本文目录导读:

PHP 怎么给代码加测试

  1. 安装 PHPUnit
  2. 基础测试示例
  3. 运行测试
  4. 常用断言方法
  5. 数据提供器(Data Providers)
  6. 测试异常
  7. 模拟对象(Mocking)
  8. 清理数据库测试数据
  9. phpunit.xml 配置
  10. 最佳实践建议
  11. 其他测试框架

在 PHP 中给代码加测试,最主流的方式是使用 PHPUnit,以下是完整的实践指南:

安装 PHPUnit

使用 Composer 安装(推荐)

# 全局安装
composer global require phpunit/phpunit
# 或项目内安装(开发环境)
composer require --dev phpunit/phpunit

基础测试示例

待测试的类文件 Calculator.php

<?php
class Calculator {
    public function add($a, $b) {
        return $a + $b;
    }
    public function divide($a, $b) {
        if ($b === 0) {
            throw new InvalidArgumentException('Division by zero');
        }
        return $a / $b;
    }
}

对应的测试文件 CalculatorTest.php

<?php
use PHPUnit\Framework\TestCase;
class CalculatorTest extends TestCase {
    private $calculator;
    // 每个测试方法执行前运行
    protected function setUp(): void {
        $this->calculator = new Calculator();
    }
    // 测试加法
    public function testAdd() {
        $result = $this->calculator->add(2, 3);
        $this->assertEquals(5, $result);
    }
    // 测试除法正常情况
    public function testDivide() {
        $result = $this->calculator->divide(10, 2);
        $this->assertEquals(5, $result);
    }
    // 测试除零异常
    public function testDivideByZero() {
        $this->expectException(InvalidArgumentException::class);
        $this->calculator->divide(10, 0);
    }
}

运行测试

# 运行单个测试文件
vendor/bin/phpunit tests/CalculatorTest.php
# 运行目录下所有测试
vendor/bin/phpunit tests/
# 运行特定方法
vendor/bin/phpunit --filter testAdd tests/CalculatorTest.php
# 输出详细信息
vendor/bin/phpunit --testdox tests/

常用断言方法

// 数值断言
$this->assertEquals(4, $result);          // 值相等
$this->assertSame(4, $result);             // 值和类型都相等
$this->assertGreaterThan(3, $result);      // 大于
$this->assertLessThan(5, $result);         // 小于
// 布尔断言
$this->assertTrue($condition);
$this->assertFalse($condition);
// 数组断言
$this->assertCount(3, $array);
$this->assertContains('value', $array);
$this->assertArrayHasKey('key', $array);
// 对象断言
$this->assertInstanceOf(ClassName::class, $object);
$this->assertNull($object);
$this->assertNotNull($object);
// 字符串断言
$this->assertStringContainsString('substring', $string);
$this->assertMatchesRegularExpression('/pattern/', $string);

数据提供器(Data Providers)

class CalculatorTest extends TestCase {
    /**
     * @dataProvider additionProvider
     */
    public function testAdd($a, $b, $expected) {
        $calculator = new Calculator();
        $this->assertEquals($expected, $calculator->add($a, $b));
    }
    public function additionProvider() {
        return [
            'positive numbers' => [1, 2, 3],
            'zero' => [0, 0, 0],
            'negative numbers' => [-1, -2, -3],
            'decimals' => [1.5, 2.5, 4.0],
        ];
    }
}

测试异常

class UserServiceTest extends TestCase {
    public function testInvalidEmail() {
        $this->expectException(InvalidArgumentException::class);
        $this->expectExceptionMessage('Invalid email format');
        $userService = new UserService();
        $userService->createUser('invalid-email');
    }
    public function testDatabaseError() {
        $mockDb = $this->createMock(PDO::class);
        $mockDb->method('query')
               ->will($this->throwException(new PDOException()));
        $this->expectException(PDOException::class);
        // 测试代码...
    }
}

模拟对象(Mocking)

use PHPUnit\Framework\TestCase;
class UserControllerTest extends TestCase {
    public function testGetUser() {
        // 创建模拟对象
        $userRepository = $this->createMock(UserRepository::class);
        // 配置模拟行为
        $userRepository->expects($this->once())
                      ->method('findById')
                      ->with(1)
                      ->willReturn(new User(['id' => 1, 'name' => 'John']));
        // 注入模拟对象
        $controller = new UserController($userRepository);
        $result = $controller->getUser('1');
        $this->assertEquals('John', $result['name']);
    }
}

清理数据库测试数据

class UserRepositoryTest extends TestCase {
    private $db;
    protected function setUp(): void {
        $this->db = new PDO('sqlite::memory:');
        $this->db->exec('CREATE TABLE users (id INT, name TEXT)');
    }
    protected function tearDown(): void {
        $this->db->exec('DROP TABLE users');
    }
    public function testUserInsert() {
        $this->db->exec("INSERT INTO users VALUES (1, 'John')");
        $result = $this->db->query("SELECT * FROM users WHERE id=1");
        $this->assertEquals('John', $result->fetch()['name']);
    }
}

phpunit.xml 配置

<?xml version="1.0" encoding="UTF-8"?>
<phpunit bootstrap="vendor/autoload.php"
         colors="true"
         stopOnFailure="false">
    <!-- 测试套件路径 -->
    <testsuites>
        <testsuite name="Application Test Suite">
            <directory>./tests</directory>
        </testsuite>
    </testsuites>
    <!-- 代码覆盖率 -->
    <coverage>
        <include>
            <directory>./src</directory>
        </include>
    </coverage>
</phpunit>

最佳实践建议

  1. 测试命名规则:测试文件以 Test.php 方法以 test 开头
  2. 一个方法只测一件事:保持测试单一职责
  3. 使用数据提供器:减少重复测试代码
  4. 测试真实场景:不要过度使用 mock,重要的逻辑要用真实对象
  5. 运行所有测试:CI/CD 里配置自动运行测试
  6. 代码覆盖率:定期检查覆盖率指标
# 生成代码覆盖率报告
vendor/bin/phpunit --coverage-html build/coverage

这样可以直观地看到哪些代码没有测试覆盖。

其他测试框架

  • PHPSpec:行为驱动开发(BDD)风格
  • Codeception:功能测试和验收测试
  • Pest:简洁优雅的测试框架

推荐先学习 PHPUnit,这是最主流的选择,然后根据需求学习其他框架。

抱歉,评论功能暂时关闭!