如何断言模拟对象的函数调用的对象参数

Jay*_*Vee 11 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->callMe( new Foo('hi') );
    }
}
Run Code Online (Sandbox Code Playgroud)

我想检查是否Sut::testMe()正确调用Some::callMe().由于参数是一个(Foo)对象(不是标量类型),我无法弄清楚如何调用PHPUnit with()来对其运行断言.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 );
    }
}
Run Code Online (Sandbox Code Playgroud)

小智 10

即使问题已经完全回答(如何断言作为参数传递给mock的对象的属性)我认为值得注意的是PHPUnit支持将回调约束传递给with().

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

幸运的是,PHPUnit确实支持(至少3.7)一个回调约束,这很有意义,你可以在像Mockery这样的专用Mock库中找到它.

当前的PHPUnit版本文档声明如下:

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

测试失败会看起来像:

1)SuTTest :: testSuT
方法名称的期望失败等于<string:callMe>,当调用1次时
参数0用于调用Some :: callMe(Foo Object(...))与期望值不匹配.
声明Foo Object()被指定的回调接受失败.

您现在可以在该参数上运行任何逻辑.

更好的是,尽管不是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 );
    }
}
Run Code Online (Sandbox Code Playgroud)

真的不知道这种使用断言和返回true的策略的未来证明.它没有记录,并且在错误消息中存在折衷.您不再获取参数约束消息,因此如果您在多个参数上设置断言,则必须推断哪一个失败.但是你确实可以更好地描述闭包内失败的断言.

1)SuTTest :: testSuT
方法名称的期望失败等于<string:callMe>,当被调用1次时
失败断言两个字符串相等.
---预期
+++实际
@@ @@
-'hi'
+'不'

希望这可以帮助任何面临类似问题的人.


edo*_*ian 8

您只需将期望值传递给"with"方法即可.

->with(1, $object, "paramThree");
Run Code Online (Sandbox Code Playgroud)

你也可以传入一系列phpUnit断言而不是参数(默认等于)

->with(1, $this->equalTo($object), "paramThree");
Run Code Online (Sandbox Code Playgroud)

所以对于你将$this->isInstanceOf("stdClass")用作参数的对象->with

对于可能的断言列表,请查看: PHPUnit/Framework/Assert.php

对于返回a的函数 new PHPUnit_Framework_Constraint


小演示

第一个测试用例只匹配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);
    }
}
Run Code Online (Sandbox Code Playgroud)

结果是:

phpunit DemoTest.php
PHPUnit 3.5.13 by Sebastian Bergmann.

.F.

Time: 0 seconds, Memory: 4.25Mb

There was 1 failure:

1) DemoTest::testFails
Failed asserting that <integer:2> matches expected <integer:3>.

...DemoTest.php:12
...DemoTest.php:34

FAILURES!
Tests: 3, Assertions: 2, Failures: 1.
Run Code Online (Sandbox Code Playgroud)


doc*_*ore 7

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

$mock->expects ($this->once())
     ->method ('dummyFunction')
     ->with ($this->logicalAnd ($this->isInstanceOf ('DummyClass')
                               ,$this->attributeEqualTo ('attribute1', 1001)
                               ,$this->attributeEqualTo ('attribute2', 200)))
     ->will ($this->returnValue (null));
Run Code Online (Sandbox Code Playgroud)

其余的代码:

class DummyClass { 
   private $attribute1 = 1001;
   private $attribute2 = 200;
} 
function dummyFunction (DummyClass $p) {...}
dummyFunction (new DummyClass());
Run Code Online (Sandbox Code Playgroud)

我希望我帮助过你