Php 如何断言模拟对象';s函数调用';s对象参数

Php 如何断言模拟对象';s函数调用';s对象参数,php,phpunit,Php,Phpunit,考虑 class Foo { public $att; public function __construct ( $a ) { $this->att = $a; } } class Some { public function callMe ( Foo $f ) {} } // class I want to test class SuT { public function testMe ( Some $s ) { echo $s-&g

考虑

class Foo {
    public $att;
    public function __construct ( $a ) { $this->att = $a; }
}

class Some {
    public function callMe ( Foo $f ) {}
}

// class I want to test
class SuT {
    public function testMe ( Some $s ) {
        echo $s->callMe( new Foo('hi') );
    }
}
我想检查
Sut::testMe()
是否正确调用了
Some::callMe()
。由于参数是(
Foo
)对象(不是标量类型),因此我不知道如何使用()调用PHPUnit的
,对其运行断言。例如,有一个
assertAttributeEquals
方法,但是如何向它提供调用的参数呢

我想做的是:

class SuTTest extends PHPUnit_Framework_TestCase {
    public function testSuT () {
        $stub = $this->getMock( 'Some' );
        $stub->expects( $this->once() )->method( 'callMe' )
            ->with( $this->assertAttributeEquals('hi', 'att', $this->argument(0) );

        /*
         * Will $stub->callMe be called with a Foo object whose $att is 'hi'?
         */
        $sut = new SuT();
        $sut->testMe( $stub );
    }
}

您只需将期望值传递给“with”方法

您还可以传入一系列phpUnit断言,而不是参数(默认为等于)

因此,对于对象,您可以使用
$this->isInstanceOf(“stdClass”)
作为
->with

有关可能的断言列表,请查看:

对于返回
新PHPUnit\u框架\u约束的函数


小型演示 第一个测试用例只匹配了2个参数,可以正常工作

第二个匹配两个,但在参数2上失败

最后一个测试传入的对象的类型是否为
stdClass

<?php

class MockMe {
    public function bla() {

    }
}

class Demo {

    public function foo(MockMe $x) {
        $x->bla(1, 2);
    }

    public function bar(MockMe $x) {
        $x->bla(1, new stdClass());
    }

}

class DemoTest extends PHPUnit_Framework_TestCase {

    public function testWorks() {
        $x = new Demo();
        $mock = $this->getMock("MockMe");
        $mock->expects($this->once())->method("bla")->with(1,2);
        $x->foo($mock);
    }

    public function testFails() {
        $x = new Demo();
        $mock = $this->getMock("MockMe");
        $mock->expects($this->once())->method("bla")->with(1,3);
        $x->foo($mock);
    }

    public function testObject() {
        $x = new Demo();
        $mock = $this->getMock("MockMe");
        $mock->expects($this->once())->method("bla")->with(1, $this->isInstanceOf("stdClass"));
        $x->bar($mock);
    }
}

这是一个简单的示例,可以满足您的需要:

$mock->expects ($this->once())
     ->method ('dummyFunction')
     ->with ($this->logicalAnd ($this->isInstanceOf ('DummyClass')
                               ,$this->attributeEqualTo ('attribute1', 1001)
                               ,$this->attributeEqualTo ('attribute2', 200)))
     ->will ($this->returnValue (null));
代码的其余部分:

class DummyClass { 
   private $attribute1 = 1001;
   private $attribute2 = 200;
} 
function dummyFunction (DummyClass $p) {...}
dummyFunction (new DummyClass());

我希望我已经帮助了您

即使这个问题已经得到了充分的回答(如何断言作为mock参数传递的对象的属性),我认为值得注意的是PHPUnit支持通过()传递给的回调约束

当我试图找出如何对模拟对象参数运行进一步的断言时,我一直在使用这个线程。例如,我需要检查某个方法的返回值。显然,出于理智的考虑,没有任何methodReturnValueEqualTo()等效于上面答案中使用的属性断言

幸运的是,PHPUnit确实支持(至少从3.7开始)回调约束,这很有意义,并且可以在专门的模拟库中找到,如

当前状态如下:

callback()约束可用于更复杂的参数验证。此约束将PHP回调作为其唯一参数。PHP回调将接收要验证的参数作为其唯一参数,如果参数通过验证,则应返回TRUE,否则返回FALSE

因此,使用回调约束,OP示例现在可以表示为:

class SuTTest extends PHPUnit_Framework_TestCase {
    public function testSuT () {
        $stub = $this->getMock('Some');
        $stub->expects($this->once())
            ->method('callMe')
            ->with($this->callback(function($arg) {
                    return ($arg instanceof Some) && ($arg->att === 'hi');
                })
            );

        /*
         * Will $stub->callMe be called with a Foo object whose $att is 'hi'?
         */
        $sut = new SuT();
        $sut->testMe($stub);
    }
}
而测试失败的情况类似于:

1) SuTTest::testSuT
调用1次时,方法名称等于的预期失败
调用Some::callMe(Foo对象(…)的参数0与预期值不匹配。
断言指定的回调接受了Foo对象()失败

现在可以对该参数运行任何逻辑

更好的是,尽管不是PHPUnit文档中的一个文档化功能,但您实际上可以使用断言并从断言错误消息中获得好处:

class SuTTest extends PHPUnit_Framework_TestCase {
    public function testSuT () {
        // alias to circumvent php closure lexical variable restriction
        $test = $this;
        // proceed as normal
        $stub = $this->getMock('Some');
        $stub->expects($this->once())
            ->method('callMe')
            // inject the test case in the closure
            ->with($this->callback(function($arg) use ($test) {
                    // use test assertions
                    $test->assertInstanceOf('Some', $arg);
                    $test->assertAttributeEquals('hi', 'att', $arg);
                    // return true to satisfy constraint if all assertions passed
                    return true;
                })
            );

        /*
         * Will $stub->callMe be called with a Foo object whose $att is 'hi'?
         */
        $sut = new SuT();
        $sut->testMe( $stub );
    }
}
我真的不知道这种使用断言并返回true的策略是如何证明未来的。它没有文档记录,并且在错误消息中存在折衷。您不再获得参数约束消息,因此如果您对多个参数设置断言,您将不得不推断(如果可能)哪一个失败。但是您确实可以在闭包中更好地描述失败的断言

1) SuTTest::testSuT
调用1次时,方法名称等于的预期失败
断言两个字符串相等失败。
---预期的
+++实际值
@@@@
-“嗨”
+“不”


希望这能帮助任何面临类似问题的人。

谢谢您的详细回答!两种建议的断言(
instanceOf
equals($object)
)都可以很好地工作,但我希望使用更细粒度的断言(例如,检查对象的属性,或断言对象的函数返回值之一)@JayVee:检查PHPUnit/Framework/assert.php(上面的链接)中不以“assert”开头的函数。您可能要寻找的是:
->attributeEqualTo($attributeName,$value)
对象属性,如果您真的想检查传入对象在调用函数时是否返回正确的值(不确定是否真的需要,也许有更简单的方法来构建测试?),您需要(afaik)创建您自己的PHPUnit_框架_约束_*类。啊,这就是诀窍
attributeEqualTo
的工作方式完全符合我的预期(和需要)。再次感谢!太棒了,真不敢相信这么简单!谢谢,你是个大人物!,你帮了我太多的忙。在尝试此操作时,我得到了“未捕获的异常:标准输入代码中不允许对“Closure”进行序列化”
class DummyClass { 
   private $attribute1 = 1001;
   private $attribute2 = 200;
} 
function dummyFunction (DummyClass $p) {...}
dummyFunction (new DummyClass());
class SuTTest extends PHPUnit_Framework_TestCase {
    public function testSuT () {
        $stub = $this->getMock('Some');
        $stub->expects($this->once())
            ->method('callMe')
            ->with($this->callback(function($arg) {
                    return ($arg instanceof Some) && ($arg->att === 'hi');
                })
            );

        /*
         * Will $stub->callMe be called with a Foo object whose $att is 'hi'?
         */
        $sut = new SuT();
        $sut->testMe($stub);
    }
}
class SuTTest extends PHPUnit_Framework_TestCase {
    public function testSuT () {
        // alias to circumvent php closure lexical variable restriction
        $test = $this;
        // proceed as normal
        $stub = $this->getMock('Some');
        $stub->expects($this->once())
            ->method('callMe')
            // inject the test case in the closure
            ->with($this->callback(function($arg) use ($test) {
                    // use test assertions
                    $test->assertInstanceOf('Some', $arg);
                    $test->assertAttributeEquals('hi', 'att', $arg);
                    // return true to satisfy constraint if all assertions passed
                    return true;
                })
            );

        /*
         * Will $stub->callMe be called with a Foo object whose $att is 'hi'?
         */
        $sut = new SuT();
        $sut->testMe( $stub );
    }
}