Mockery1.0 Preserving Pass-By-Reference Method Parameter Behaviour

Preserving Pass-By-Reference Method Parameter Behaviour

PHP Class method may accept parameters by reference. In this case, changes made to the parameter (a reference to the original variable passed to the method) are reflected in the original variable. An example:

class Foo
{

    public function bar(&$a)
    {
        $a++;
    }

}

$baz = 1;
$foo = new Foo;
$foo->bar($baz);

echo $baz; // will echo the integer 2

In the example above, the variable $baz is passed by reference to Foo::bar() (notice the & symbol in front of the parameter?). Any change bar() makes to the parameter reference is reflected in the original variable, $baz.

Mockery handles references correctly for all methods where it can analyse the parameter (using Reflection) to see if it is passed by reference. To mock how a reference is manipulated by the class method, we can use a closure argument matcher to manipulate it, i.e. \Mockery::on() - see the argument-validation-complex-argument-validation chapter.

There is an exception for internal PHP classes where Mockery cannot analyse method parameters using Reflection (a limitation in PHP). To work around this, we can explicitly declare method parameters for an internal class using \Mockery\Configuration::setInternalClassMethodParamMap().

Here's an example using MongoCollection::insert(). MongoCollection is an internal class offered by the mongo extension from PECL. Its insert() method accepts an array of data as the first parameter, and an optional options array as the second parameter. The original data array is updated (i.e. when a insert() pass-by-reference parameter) to include a new _id field. We can mock this behaviour using a configured parameter map (to tell Mockery to expect a pass by reference parameter) and a Closure attached to the expected method parameter to be updated.

Here's a PHPUnit unit test verifying that this pass-by-reference behaviour is preserved:

public function testCanOverrideExpectedParametersOfInternalPHPClassesToPreserveRefs()
{
    \Mockery::getConfiguration()->setInternalClassMethodParamMap(
        'MongoCollection',
        'insert',
        array('&$data', '$options = array()')
    );
    $m = \Mockery::mock('MongoCollection');
    $m->shouldReceive('insert')->with(
        \Mockery::on(function(&$data) {
            if (!is_array($data)) return false;
            $data['_id'] = 123;
            return true;
        }),
        \Mockery::any()
    );

    $data = array('a'=>1,'b'=>2);
    $m->insert($data);

    $this->assertTrue(isset($data['_id']));
    $this->assertEquals(123, $data['_id']);

    \Mockery::resetContainer();
}

Protected Methods

When dealing with protected methods, and trying to preserve pass by reference behavior for them, a different approach is required.

class Model
{
    public function test(&$data)
    {
        return $this->doTest($data);
    }

    protected function doTest(&$data)
    {
        $data['something'] = 'wrong';
        return $this;
    }
}

class Test extends \PHPUnit\Framework\TestCase
{
    public function testModel()
    {
        $mock = \Mockery::mock('Model[test]')->shouldAllowMockingProtectedMethods();

        $mock->shouldReceive('test')
            ->with(\Mockery::on(function(&$data) {
                $data['something'] = 'wrong';
                return true;
            }));

        $data = array('foo' => 'bar');

        $mock->test($data);
        $this->assertTrue(isset($data['something']));
        $this->assertEquals('wrong', $data['something']);
    }
}

This is quite an edge case, so we need to change the original code a little bit, by creating a public method that will call our protected method, and then mock that, instead of the protected method. This new public method will act as a proxy to our protected method.

ドキュメント章別ページ

ヘッダー項目移動

注目:アイコン:ページ内リンク設置(リンクがないヘッダーへの移動では、リンクがある以前のヘッダーのハッシュをURLへ付加します。

移動

クリックで即時移動します。

バージョン

設定

適用ボタンクリック後に、全項目まとめて適用されます。

カラーテーマ
和文指定 Pagination
和文指定 Scaffold
Largeスクリーン表示幅
インデント
本文フォント
コードフォント
フォント適用確認

フォントの指定フィールドから、フォーカスが外れると、当ブロックの内容に反映されます。EnglishのDisplayもPreviewしてください。

フォント設定時、表示に不具合が出た場合、当サイトのクッキーを削除してください。

バックスラッシュを含むインライン\Code\Blockの例です。

以下はコードブロックの例です。

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    /**
     * ユーザに関連する電話レコードを取得
     */
    public function phone()
    {
        return $this->hasOne('App\Phone');
    }
}

設定を保存する前に、表示が乱れないか必ず確認してください。CSSによるフォントファミリー指定の知識がない場合は、フォントを変更しないほうが良いでしょう。

キーボード・ショートカット

オープン操作

PDC

ページ(章)移動の左オフキャンバスオープン

HA

ヘッダー移動モーダルオープン

MS

移動/設定の右オフキャンバスオープン

ヘッダー移動

T

最初のヘッダーへ移動

E

最後のヘッダーへ移動

NJ

次ヘッダー(H2〜H4)へ移動

BK

前ヘッダー(H2〜H4)へ移動

その他

?

このヘルプページ表示
閉じる