是一种组成并执行可命令的CLI命令的方法吗?

时间:2015-04-27 03:58:07

标签: php unit-testing phpunit

这是我第一次进入单元测试,我想知道是否有一种方法可以从一个简单的类中测试以下方法。

我基本上需要检查命令是否包含通过该方法传递的每个参数的某些参数。

只要在返回的控制台输出中检查命令,该命令控制的外部工具就不会返回任何有意义的内容。

我已经读过方法的主体是实现细节,因此它是不可测试的。如果这是真的我假设我不能测试这样的方法吗?

方法示例

public function doSomething(array $params)
{
    $command = ($this->x64 ? 'test_x64' : 'test_enc') . ' '
             . $params['a'] . ' '
             . $params['b'] . ' '
             . $params['c'];

    if (isset($params['d'])) {
        $command .= ' -d=' . $params['d'];
    }

    if (isset($params['e'])) {
        $command .= ' -e=' . $params['e'];
    }

    if (isset($params['f'])) {
        $command .= ' -' . $params['f'] . 'bit';
    }

    return shell_exec($command);
}

2 个答案:

答案 0 :(得分:1)

如果您使用名称空间,则可以mock global php functions

或者您可以使用class ShellExecutor方法创建execute($command)。然后你必须以某种方式将实例注入你的函数。所以,直接在函数调用中:

public function doSomething(array $params, ShellExecutor $executor = null) {
    if (!$executor) $executor = new ShellExecutor();
    ...
    return $executor->execute($command);
}

或者在班级的构造函数中:

public function __construct(ShellExecutor $executor = null) {
    if (!$executor) $executor = new ShellExecutor();
    $this->executor = ShellExecutor;
}

public function doSomething(array $params) {
    ...
    return $this->executor->execute($command);
}

然后在你的测试中,你可以模拟ShellExecutor并测试它的方法调用,就像这样(我在这个例子中使用Mockery):

use Mockery as m;

class YourTestedClassTest {

   ...
   public function testParsesArguments() {
       $mockExecutor = m::mock('ShellExecutor');
       $mockExecutor->shouldReceive('execute')
           ->with(/*command arguments validation*/)
           ->andReturn('fakeReturnValue');

       $yourTestedInstance->doSomething(['a' => 'foo', 'b' => 'bar'], $mockExecutor);

   }

对于参数验证选项,请参阅mockery documentation

答案 1 :(得分:1)

我建议在一个不同的帮助器类上用方法包装shell_exec调用。这样你就可以模拟或伪造助手类。模拟将意味着注入依赖,所以我将举例说明faking方法,因为它更简单(虽然我是DI和嘲笑的粉丝):

// the class used in code
class ExecUtil {
    public static function shellExec($command) {
        return shell_exec($command);
    }
}

// the class used in the unit tests
class ExecUtil {
    public static $lastShellExecCommand = null;
    public static function shellExec($command) {
        static::$lastShellExecCommand = $command;
    }
}

//your test
public function testDoSomethingLaunchesTheProperProgramWithTheProperArguments() {
    //call doSomething()
    $expectedCommand = '<the command you expect here>';
    $this->assertEquals(ExecUtil::$lastShellExecCommand, $expectedCommand);
}

只需确保通过自动加载器加载类,以便能够在运行时决定加载哪个类。

作为一个完成的方面,我还建议您重构方法中的代码,因为在构建参数列表时,您目前有一些代码冗余:

$arguments = array($this->x64 ? 'test_x64' : 'test_enc'));

array_push($arguments, $params['a'], $params['b'], $params['c'];

if (isset($params['d'])) {
    array_push($arguments, '-d=' . $params['d']);
}

if (isset($params['e'])) {
    array_push($arguments,'-e=' . $params['e']);
}

if (isset($params['f'])) {
    array_push($arguments, '-' . $params['f'] . 'bit');
}

$command = implode(' ', $arguments);