Php 如何模拟虚拟二进制文件,以便测试exec()/system()/passthru()函数的输出?

Php 如何模拟虚拟二进制文件,以便测试exec()/system()/passthru()函数的输出?,php,unit-testing,mocking,phpunit,Php,Unit Testing,Mocking,Phpunit,我有一个有趣的问题,在网上搜索过,但还没有找到答案 我在一家不允许员工使用OOP的公司工作,这有点可笑,但工作经验很有价值 考虑以下功能: function get_setting_values_from_file( $parameter ) { exec("/usr/var/binary --options $parameter", $output, $return); $settings = file( $output[0] ); foreach( $settings

我有一个有趣的问题,在网上搜索过,但还没有找到答案

我在一家不允许员工使用OOP的公司工作,这有点可笑,但工作经验很有价值

考虑以下功能:

function get_setting_values_from_file( $parameter )
{
    exec("/usr/var/binary --options $parameter", $output, $return);
    $settings = file( $output[0] );
    foreach( $settings as $setting ) {
        if( strstr( $setting, "color") ) {
            $setting = explode( ":", $setting );
            return $setting[1];
        }
    }
    return false;
}
我需要对一个类似的函数进行单元测试。我目前在测试中使用phpUnit和vfstream库来模拟文件系统,但是当我在开发时无法访问实际系统时,如何模拟对
exec(“/usr/var/binary--options$parameter”、$output$return)
的调用呢?处理这样的测试用例的推荐方法是什么


感谢所有反馈。

只需模拟此函数即可返回您试图进入$settings的文本。您不需要调用可执行文件,只需创建文件或返回即可

例如,假设函数get_setting_values_from_file()以数组的形式返回设置,您只需在测试中模拟该函数即可以数组的形式返回设置。创建一个测试存根来模拟包含get_设置_values_from_file()方法的对象,并让该模拟简单地返回测试假设的相同FALSE,1或2

$stub = $this->getMock('GetSettingsClass');
    $stub->expects($this->any())
         ->method('get_settings_from_file')
         ->will($this->returnValue(0));
这来自PHPUnit手册->

或者,您甚至可以绕过调用,通过创建数组并将其传递给这些函数,简单地测试处理返回的函数/代码。 主代码中的假定示例:

...
$settings = get_setting_values_from_file( 'UserType' );
$UserType = get_user_type($settings);
return $UserType;

function get_user_type($settings)
{
    if($settings !== FALSE)         // Returned from your function if parameter is not found
    {
        switch($settings)
        {
            case 1:
                return 'User';      // Best to use Constants, but for example here only
                break;
            case 2:
                return 'Admin';
                break;
            ...
         }
    }
    else
    {
        return FALSE;
    }
}
现在,在您的测试中,您可以

$this->assertFalse(get_user_type(FALSE, 'Ensure not found data is handled properly as FALSE is returned');
$this->assertEqual('User', get_user_type(1), 'Test UserType=1');
$this->assertEqual('Admin', get_user_type(1), 'Test UserType=2');
...
这些函数的工作原理是,代码不调用必须模拟从操作系统读取的函数,而是通过调用处理设置返回值的函数来处理所有预期返回。这里,您只需假设函数“get_setting_values_from_file()”的返回,而不需要文件或任何模拟

但是,这不会测试从文件读取,我会在另一个测试中使用setUp和tearDown来实际创建一个具有所需值的文件(fopen/fwrite),然后调用函数并确保返回预期值

我希望这有助于解释我的想法。

您可以使用函数模拟库模拟
exec()
。我为您制作了一个()文件,它要求您使用名称空间

namespace foo;

use phpmock\phpunit\PHPMock;

class ExecTest extends \PHPUnit_Framework_TestCase
{

    use PHPMock;

    public function testExec()
    {
        $mock = $this->getFunctionMock(__NAMESPACE__, "exec");
        $mock->expects($this->once())->willReturnCallback(
            function ($command, &$output, &$return_var) {
                $this->assertEquals("foo", $command);
                $output = "failure";
                $return_var = 1;
            }
        );

        exec("foo", $output, $return_var);
        $this->assertEquals("failure", $output);
        $this->assertEquals(1, $return_var);
    }
}

史蒂文,非常感谢你的反馈。我真的很感激。你所说的是最合理的处理方法。如果有一个模拟二进制文件的系统,我将不得不模拟整个操作系统。我最近刚进入TDD,想确保我的所有基础都覆盖了。史蒂文,你给出的答案是正确的,但我不太知道如何实施它。我做了一些研究,您可以使用名称空间来实现它。我在这个站点上发布了一个使用PHP名称空间实现您的解决方案的可能方法。()现在,您调用的函数不返回任何内容,而是设置内部变量$settings。这是你的回报。我已经用一个例子更新了我的答案。明白了。很好的例子。我读了PHPUnit手册,读了关于存根的内容。我的印象是存根只在课堂上起作用。我的工作环境不允许我们使用类,所以我一直在尝试测试一个运行二进制文件的函数。模拟fopen和fwrite可以用vfstream完成,但模拟二进制输出是我遇到的问题。另外,我也很抱歉说不清楚,但我必须测试函数get_setting_values_from_file,因为这是我正在制作的函数。是的,mock只用于类。模拟将不起作用,但是测试返回和隔离变量将允许您测试这一点。例如,您可以扩展函数的内部,使其能够独立处理读取,从而使其成为另一个函数,并相应地设置数据,这将允许您对其进行测试,并且在另一个函数中进行实际读取时,您可以简单地调用函数以使用测试值处理数据。通过使用简单的函数,您可以测试它们并将这些函数用于其他目的。与数据共享参数的读取。谢谢!我要试一试。