【问题标题】:PHPUnit Mock that returns the same array every time每次返回相同数组的 PHPUnit Mock
【发布时间】:2012-08-07 05:26:54
【问题描述】:

我正在构建一个会话管理类和相关的单元测试。为了将类与 $_SESSION 的全局状态分开,我使用了一个非常简单的类来管理类和会话数据之间的绑定。

Binding类的整个源码如下:

class Binding implements iface\Binding
{
    public function &getNamespace ($namespace)
    {
        return $_SESSION [$namespace];
    }
}

在消费会话类中,我有以下内容:

protected function initStorage ()
{
    // Check that storage hasn't already been bound to the session
    if ($this -> storage === NULL)
    {
        // Attempt to start the session if it hasn't already been started
        if (($this -> sessionId () !== '')
        || ((!$this -> headersSent ())
        && ($this -> startSession ())))
        {
            // Bind the storage to the session
            $this -> storage    =& $this -> binding -> getNamespace ($this -> namespace);
            // Make sure the session is in a usable state
            if (!$this -> hasData ())
            {
                $this -> reset ();
            }
        }
        else
        {
            // We couldn't start the session
            throw new \RuntimeException (__METHOD__ . ': Unable to initiate session storage at this time');
        }
    }

    return $this;
}

sessionId、headersSent 和 startSession 是用作“测试接缝”的简单的单行函数,我可以很容易地用 PHPUnit 中的模拟替换它们。

我在编写测试时意识到,我可以使用模拟绑定类做更多的事情,而不仅仅是将会话从类中分离出来,我还可以将它用作观察类的非公共属性的一种方式,而不必实际暴露任何内部状态,从而使类变得脆弱。由于该类对数组的引用而不是直接对数组进行操作,因此我可以观察到被引用的数组。

我希望用 PHPUnit 的模拟 API 来做到这一点,但我不知道该怎么做。

我知道我可以创建一个返回如下数组的模拟:

$mock = $this -> getMock ('iface\Binding');

$mock -> expects ($this -> any ())
      -> method ('getNamespace')
      -> will ($this -> returnValue (array ()));

虽然它对观察状态变化没有用处,因为它每次都返回一个不同的数组。我需要的是一个模拟,它每次都返回对同一个数组的引用。

最后我写了一个类来代替真正的 Binding 类并使用它:

class BindingMock implements iface\Binding
{
    protected $storage = array ();

    public function &getNamespace ($namespace)
    {
        return $this -> storage [$namespace];
    }
}

使用这个类可以让我在调用 Session API 之前和之后检查 $storage 的内容,因为我可以查看存储数组中的内容,而无需在 Session 类中公开非公共状态。这是一个使用该技术的示例测试:

public function testCreateItem ()
{
    $storage    =& $this -> binding -> getNamespace ('unittest');
    $this -> assertEmpty ($storage);
    $this -> object ->  createItem ('This is a test', 'test');
    $this -> assertNotEmpty ($storage);
    $this -> assertEquals ('This is a test', $storage ['test']);
}

我宁愿能够使用 PHPUnit 生成替代类,但是,只为单元测试使用附加类似乎是错误的方法,除非在 PHPUnit 中无法实现相同的目标。

【问题讨论】:

    标签: php unit-testing mocking phpunit


    【解决方案1】:

    我认为您可以返回一个 ArrayObject,它将内部数组公开给您的测试,但在这种情况下,我宁愿完全避免从 Binding 公开数组。相反,Binding 接口应该提供获取、设置和清除值以及其他更高级别操作的方法。然后你可以传入一个期望这些调用的模拟对象,而不是返回原始数组的模拟对象。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-11-23
      • 2011-08-24
      • 1970-01-01
      • 2019-11-08
      • 1970-01-01
      • 1970-01-01
      • 2023-04-06
      相关资源
      最近更新 更多