【问题标题】:Is a method which composes and executes a CLI command testable?编写和执行 CLI 命令的方法是否可测试?
【发布时间】:2015-04-27 03:58:06
【问题描述】:

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

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

就检查返回的控制台输出中的命令而言,由命令控制的外部工具没有返回任何有意义的东西。

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

方法示例

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);
}

【问题讨论】:

  • 您是否可以检查-d 是否是命令行可执行文件的有效选项?
  • @Devon 不。我在问我是否/如何对这个方法进行单元测试。

标签: php unit-testing phpunit


【解决方案1】:

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

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

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);

   }

有关参数验证选项,请参阅嘲笑documentation

【讨论】:

  • 一篇不错的“模拟全局函数”文章。
【解决方案2】:

我建议将 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);

【讨论】:

    猜你喜欢
    • 2011-03-18
    • 1970-01-01
    • 2013-05-31
    • 2022-01-14
    • 2011-08-27
    • 2015-09-09
    • 1970-01-01
    • 2012-09-10
    • 1970-01-01
    相关资源
    最近更新 更多