You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用PHPUnit测试LoadEnvironmentVariables类?

嘿,刚接触PHPUnit就能想到要覆盖这几个关键场景,很棒!针对你提到的三个测试需求,我们可以通过**模拟依赖对象(也就是Core类的实例)**来绕过外部模块的限制,精准测试每个代码分支。下面是具体的实现思路和代码示例:

1. 测试变量是否已加载

要验证这个场景,我们需要让$app->configurationIsCached()返回false,触发DotEnv的加载逻辑,然后检查环境变量是否被正确设置。

2. 测试配置已缓存时不执行加载操作

这里需要让$app->configurationIsCached()返回true,然后验证load()方法完全没有被调用过——这时候我们可以用PHPUnit的**Spy(带调用跟踪的Mock)**来监控方法调用情况。

3. 测试缺少必填变量时是否抛出异常

同样让$app->configurationIsCached()返回false,但提供一个缺少必填变量的环境配置,然后断言代码会抛出DotEnv的ValidationException。

完整测试类示例

<?php declare(strict_types=1);
namespace Tests\Foundation\Bootstrap;

use Foundation\Bootstrap\LoadEnvironmentVariables;
use Foundation\Core;
use PHPUnit\Framework\TestCase;
use Dotenv\Exception\ValidationException;

class LoadEnvironmentVariablesTest extends TestCase
{
    /**
     * 每个测试前清理环境变量,避免测试间互相干扰
     */
    protected function setUp(): void
    {
        parent::setUp();
        
        $requiredVars = ['APP_URL', 'DB_NAME', 'DB_USER', 'DB_PASS', 'DB_HOST'];
        foreach ($requiredVars as $var) {
            putenv($var);
            unset($_ENV[$var], $_SERVER[$var]);
        }
    }

    /**
     * 测试配置未缓存时,环境变量被正确加载
     */
    public function testEnvironmentVariablesAreLoadedWhenConfigIsNotCached()
    {
        // 模拟Core实例:配置未缓存,返回测试用.env文件的路径
        $appMock = $this->createMock(Core::class);
        $appMock->method('configurationIsCached')->willReturn(false);
        // 假设你的测试用.env放在tests/fixtures目录下
        $appMock->method('get')->with('paths.base')->willReturn(__DIR__ . '/../../fixtures');

        // 实例化测试类,触发加载逻辑
        new LoadEnvironmentVariables($appMock);

        // 断言所有必填变量都已加载
        $this->assertEquals('http://test.local', getenv('APP_URL'));
        $this->assertEquals('test_db', getenv('DB_NAME'));
        $this->assertNotNull(getenv('DB_USER'));
        $this->assertNotNull(getenv('DB_PASS'));
        $this->assertNotNull(getenv('DB_HOST'));
    }

    /**
     * 测试配置已缓存时,load方法不会被调用
     */
    public function testLoadMethodIsSkippedWhenConfigIsCached()
    {
        // 创建LoadEnvironmentVariables的Spy,用来跟踪load方法的调用
        $loadEnvSpy = $this->getMockBuilder(LoadEnvironmentVariables::class)
            ->setConstructorArgs([$this->createMock(Core::class)])
            ->onlyMethods(['load'])
            ->getMock();

        // 模拟Core实例:配置已缓存
        $appMock = $this->createMock(Core::class);
        $appMock->method('configurationIsCached')->willReturn(true);

        // 重新调用构造函数(因为Spy已经实例化过一次,这里传入配置好的appMock)
        $loadEnvSpy->__construct($appMock);

        // 断言load方法从未被调用
        $loadEnvSpy->expects($this->never())->method('load');
    }

    /**
     * 测试缺少必填变量时抛出异常
     */
    public function testThrowsExceptionWhenRequiredVariablesAreMissing()
    {
        // 模拟Core实例:配置未缓存,返回缺少变量的.env目录路径
        $appMock = $this->createMock(Core::class);
        $appMock->method('configurationIsCached')->willReturn(false);
        // tests/fixtures/missing-env目录下的.env故意缺少DB_PASS
        $appMock->method('get')->with('paths.base')->willReturn(__DIR__ . '/../../fixtures/missing-env');

        // 断言会抛出DotEnv的验证异常
        $this->expectException(ValidationException::class);
        $this->expectExceptionMessage('DB_PASS is required');

        // 实例化测试类,触发异常
        new LoadEnvironmentVariables($appMock);
    }
}

几个关键注意事项

  • 测试用.env文件:你需要在tests/fixtures下创建两个.env文件:
    • 一个包含所有必填变量的正常.env
    • 一个故意缺少某个变量(比如DB_PASS)的.env,放在missing-env子目录下
  • 环境变量隔离:setUp方法里的清理操作很重要,确保每个测试都从干净的环境开始,避免之前的测试残留影响结果
  • Mock的灵活使用:如果不想依赖真实文件,可以用DotEnv的createFromString方法直接在内存中构建环境变量字符串,这样测试会更轻量,比如:
    // 在testEnvironmentVariablesAreLoadedWhenConfigIsNotCached里替换load方法的实现
    $loadEnvMock = $this->getMockBuilder(LoadEnvironmentVariables::class)
        ->setConstructorArgs([$appMock])
        ->onlyMethods(['load'])
        ->getMock();
    
    $loadEnvMock->method('load')->willReturnCallback(function () {
        $dotenv = \Dotenv\Dotenv::createFromString(
            "APP_URL=http://test.local\nDB_NAME=test_db\nDB_USER=root\nDB_PASS=secret\nDB_HOST=127.0.0.1"
        );
        $dotenv->safeLoad();
        $dotenv->required(['APP_URL', 'DB_NAME', 'DB_USER', 'DB_PASS', 'DB_HOST']);
    });
    

内容的提问来源于stack exchange,提问作者KeironLowe

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 08:11:24