【问题标题】:How can I (unit) test a class behavior based on a static method call?我如何(单元)测试基于静态方法调用的类行为?
【发布时间】:2017-06-07 03:06:26
【问题描述】:

我有这两个课程:

AbstractTaskDispatcher

<?php

declare(strict_types=1);

namespace MyExample;

abstract class AbstractTaskDispatcher
{
    public final function getResult(Task $task) : Result
    {
        if($worker = $this->getWorker($task))
            return $worker->getResult();
        else
            return Result::getUnprocessableTaskResult();
    }

    abstract protected function getWorker(Task $task) : Worker;
}

?>

结果

<?php

declare(strict_types=1);

namespace MyExample;

class Result
{
    private $code;

    public function __construct(int $code = 0)
    {
        $this->code = $code;
    }

    public static function getUnprocessableTaskResult() : Result
    {
        return new Result(1000);
    }

    public function getCode() : int
    {
        return $this->code;
    }
}

?>

我想用 PHPUnit 编写一个单元测试来确保 AbstractTaskDispatcher::getResult() 在没有找到合适的 Worker 来处理任务时返回 Result::getUnprocessableTaskResult()。

我不想这样做:

  1. 安排:$expectedResult = Result::getUnprocessableTaskResult();
  2. 动作:$result = $dispatcherStub->getResult(New Task());
  3. 断言:assertEquals($result, $expectedResult);

因为它依赖于 Result 类的实现,不会是单元测试。

我试着做点什么:

<?php

    use PHPUnit\Framework\TestCase;
    use MyExample as ex;

    class AbstractDispatcherTest extends TestCase
    {
        public function test_getResultSouldReturnUnprocessableTaskResultIfNoWorkerFound()
        {
            $dispatcher = $this->getMockForAbstractClass(ex\AbstractDispatcher::class);
            $arbitraryCode = 6666;
            $expectedResult = new ex\Result($arbitraryCode);
            $resultClass = $this->getMockClass('Result', ['getUnprocessableTaskResult']);
            $resultClass::staticExpects($this->any())
                ->method('getUnprocessableTaskResult')
                ->will($this->returnValue($expectedResult));

            $result = $dispatcher->getResult(new ex\Task([]));

            $this->assertEquals($expectedResult, $result);
        }
    }

?>

但 staticExpects() 方法已被弃用,并且在当前 PHPUnit 版本中不再存在。

我该如何编写这个测试?

【问题讨论】:

  • 嗨@berty,只有一个问题:如何返回false 方法getWorker?严格的类型定义不能接受你返回与Worker 对象实例不同的东西
  • 从设计的角度来看,您为什么选择使用静态方法进行返回? AbstractTaskDispatcher 没有理由不能只返回一个新的 Result 实例本身,因为它知道 Result 类的开头。
  • 嗨@Matteo,我在编写它时考虑到了 PHP 7.1 和可为空的返回类型,即使我目前使用的是 PHP 7.0
  • @JohnJoseph,我不想在不同的类/方法中多次使用此特定结果的内容(在我的示例中为代码 = 1000)“硬编码”。跨度>
  • @JohnJoseph 这种做法被称为“命名构造器模式”

标签: php unit-testing phpunit static-methods php-7


【解决方案1】:

你可以简单地测试如下:

public function test_getResultSouldReturnUnprocessableTaskResultIfNoWorkerFound()
{
    $dispatcher = $this->getMockForAbstractClass(ex\AbstractTaskDispatcher::class);
    $dispatcher->expects($this->once())
        ->method('getWorker')
        ->willReturn(false);

    $result = $dispatcher->getResult(new ex\Task([]));

    // Unuseful: this is implicit by the method signature
    $this->assertInstanceOf(ex\Result::class, $result);
    $this->assertEquals(1000, $result->getCode());
}

注意:我将 AbstractTaskDispatcher 类的方法定义更改如下,以便返回 false 值:

/**
 * @param Task $task
 * @return Result|false The Result of the task or false if no suitable Worker is found to process the Task
 */
abstract protected function getWorker(Task $task);

编辑:

正如您评论的那样,您不能检查如下而不是硬编码结果代码:

    $this->assertEquals(ex\Result::getUnprocessableTaskResult(), $result);
    // Or
    $this->assertEquals(ex\Result::getUnprocessableTaskResult()->getCode(), $result->getCode());

希望有帮助

【讨论】:

  • 这是我不想做的,因为这依赖于 Result 类,这超出了测试的范围。我可以通过更改 Result 类代码使这个测试失败或成功。例如,即使 AbstractDispatcher::getResult() 被破坏,我也可以通过在 Result::getCode() 中以硬代码返回 1000 来通过测试。所以这不会是一个严格的 unit 测试。否则,如果我选择接受测试的“非统一”,我想我最好在测试中assertEquals(ex\Result::getUnprocessableTaskResult(), $result)以避免写“1000”。
猜你喜欢
  • 2017-02-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-12-24
  • 1970-01-01
  • 2019-05-11
相关资源
最近更新 更多