PHP项目PHPUnit如何配置使用运行

wen PHP项目 26

本文目录导读:

PHP项目PHPUnit如何配置使用运行

  1. 安装PHPUnit
  2. 基本目录结构
  3. PHPUnit配置文件 (phpunit.xml)
  4. 编写测试类
  5. 运行测试
  6. 常用断言方法
  7. 高级配置
  8. 常见问题解决
  9. composer.json配置建议
  10. 最佳实践

我来详细说明PHP项目中PHPUnit的配置、使用和运行方法。

安装PHPUnit

使用Composer安装(推荐)

# 全局安装
composer global require phpunit/phpunit
# 或作为项目依赖
composer require --dev phpunit/phpunit

版本选择建议

{
    "require-dev": {
        "phpunit/phpunit": "^9.6"  // PHP 7.3+
    }
}

基本目录结构

project/
├── src/
│   ├── Calculator.php
│   └── UserService.php
├── tests/
│   ├── Unit/
│   │   └── CalculatorTest.php
│   └── Feature/
│       └── UserServiceTest.php
├── phpunit.xml
├── composer.json
└── vendor/

PHPUnit配置文件 (phpunit.xml)

<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
         bootstrap="vendor/autoload.php"
         colors="true"
         verbose="true"
         stopOnFailure="false">
    <php>
        <ini name="error_reporting" value="-1"/>
        <env name="APP_ENV" value="testing"/>
        <env name="DB_HOST" value="localhost"/>
    </php>
    <testsuites>
        <testsuite name="Unit">
            <directory>tests/Unit</directory>
        </testsuite>
        <testsuite name="Feature">
            <directory>tests/Feature</directory>
        </testsuite>
    </testsuites>
    <coverage>
        <include>
            <directory>src</directory>
        </include>
        <exclude>
            <directory>src/Exceptions</directory>
        </exclude>
    </coverage>
    <logging>
        <junit outputFile="build/report.xml"/>
    </logging>
    <filter>
        <whitelist processUncoveredFilesFromWhitelist="true">
            <directory suffix=".php">src</directory>
        </whitelist>
    </filter>
</phpunit>

编写测试类

基础测试示例

<?php
// tests/Unit/CalculatorTest.php
namespace Tests\Unit;
use PHPUnit\Framework\TestCase;
use App\Calculator;
class CalculatorTest extends TestCase
{
    private Calculator $calculator;
    protected function setUp(): void
    {
        parent::setUp();
        $this->calculator = new Calculator();
    }
    public function testAddition(): void
    {
        $result = $this->calculator->add(2, 3);
        $this->assertEquals(5, $result);
        $this->assertIsInt($result);
    }
    public function testDivision(): void
    {
        $result = $this->calculator->divide(10, 2);
        $this->assertEquals(5, $result);
    }
    public function testDivisionByZero(): void
    {
        $this->expectException(\InvalidArgumentException::class);
        $this->expectExceptionMessage('Cannot divide by zero');
        $this->calculator->divide(10, 0);
    }
    /**
     * @dataProvider additionProvider
     */
    public function testAddWithDataProvider(int $a, int $b, int $expected): void
    {
        $result = $this->calculator->add($a, $b);
        $this->assertEquals($expected, $result);
    }
    public function additionProvider(): array
    {
        return [
            [1, 2, 3],
            [0, 0, 0],
            [-1, -1, -2],
            [100, 200, 300]
        ];
    }
}

Mock对象示例

<?php
// tests/Feature/UserServiceTest.php
namespace Tests\Feature;
use PHPUnit\Framework\TestCase;
use App\UserService;
use App\Repositories\UserRepository;
use App\Mail\WelcomeMail;
use Mockery;
class UserServiceTest extends TestCase
{
    private UserService $userService;
    private $userRepositoryMock;
    private $mailerMock;
    protected function setUp(): void
    {
        parent::setUp();
        // 创建Mock
        $this->userRepositoryMock = $this->createMock(UserRepository::class);
        $this->mailerMock = $this->createMock(\App\Mail\Mailer::class);
        $this->userService = new UserService(
            $this->userRepositoryMock,
            $this->mailerMock
        );
    }
    public function testCreateUser(): void
    {
        $userData = [
            'name' => 'John Doe',
            'email' => 'john@example.com'
        ];
        $expectedUser = (object) array_merge($userData, ['id' => 1]);
        // 设置Mock期望
        $this->userRepositoryMock
            ->expects($this->once())
            ->method('create')
            ->with($userData)
            ->willReturn($expectedUser);
        $this->mailerMock
            ->expects($this->once())
            ->method('send')
            ->with($this->isInstanceOf(WelcomeMail::class));
        $result = $this->userService->createUser($userData);
        $this->assertEquals($expectedUser, $result);
    }
    public function testGetUserByIdNotFound(): void
    {
        $this->userRepositoryMock
            ->expects($this->once())
            ->method('findById')
            ->with(999)
            ->willReturn(null);
        $this->expectException(\RuntimeException::class);
        $this->userService->getUserById(999);
    }
    protected function tearDown(): void
    {
        Mockery::close();
        parent::tearDown();
    }
}

运行测试

命令行运行

# 运行所有测试
./vendor/bin/phpunit
# 运行特定测试套件
./vendor/bin/phpunit --testsuite Unit
# 运行特定测试文件
./vendor/bin/phpunit tests/Unit/CalculatorTest.php
# 运行特定测试方法
./vendor/bin/phpunit --filter testAddition tests/Unit/CalculatorTest.php
# 生成覆盖率报告
./vendor/bin/phpunit --coverage-html coverage/

运行指定测试组

class CalculatorTest extends TestCase
{
    /**
     * @group slow
     * @group database
     */
    public function testComplexCalculation(): void
    {
        // ...
    }
}
./vendor/bin/phpunit --group slow

常用断言方法

$this->assertEquals($expected, $actual);
$this->assertSame($expected, $actual);  // 严格比较
$this->assertTrue($condition);
$this->assertFalse($condition);
$this->assertNull($value);
$this->assertNotNull($value);
$this->assertInstanceOf($class, $object);
$this->assertArrayHasKey($key, $array);
$this->assertCount($expectedCount, $array);
$this->assertStringContainsString($needle, $haystack);
$this->assertMatchesRegularExpression('/pattern/', $string);

高级配置

数据库测试配置

<php>
    <env name="DB_CONNECTION" value="sqlite"/>
    <env name="DB_DATABASE" value=":memory:"/>
    <env name="APP_ENV" value="testing"/>
    <env name="CACHE_DRIVER" value="array"/>
    <env name="SESSION_DRIVER" value="array"/>
</php>

并行测试

# 使用paratest进行并行测试
composer require --dev brianium/paratest
./vendor/bin/paratest -p4 tests/

常见问题解决

内存不足

php -d memory_limit=-1 ./vendor/bin/phpunit

跳过慢速测试

class CalculatorTest extends TestCase
{
    /**
     * @requires extension pdo
     */
    public function testWithDatabase(): void
    {
        // ...
    }
}

composer.json配置建议

{
    "scripts": {
        "test": "phpunit",
        "test-coverage": "phpunit --coverage-html coverage",
        "test-unit": "phpunit --testsuite Unit",
        "test-feature": "phpunit --testsuite Feature"
    }
}

最佳实践

  1. 测试命名规范:测试方法名使用test前缀或@test注解
  2. 单一断言:每个测试方法只测试一个功能点
  3. 使用FixturessetUp()tearDown()方法管理测试资源
  4. 避免外部依赖:使用Mock对象隔离测试
  5. 保持测试独立:测试之间不应相互依赖

这样配置后,你就可以通过简单的命令运行PHPUnit测试了,记得定期运行测试确保代码质量。

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