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

发布于 2024-11-06 13:53:32 字数 1044 浏览 5 评论 0原文

假设

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 的 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 );
    }
}

Consider

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') );
    }
}

I want to check whether Sut::testMe() properly invokes Some::callMe(). Since the parameter is a (Foo) object (not a scalar type), I can't figure out how to call PHPUnit's with() to run assertions on it. There is an assertAttributeEquals method for example, but how do I feed it the invocation's argument?

What I'd like to do is this:

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 );
    }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

-小熊_ 2024-11-13 13:53:32

尽管问题已经得到充分解答(如何断言作为参数传递给模拟的对象的属性),但我认为值得注意的是 PHPUnit 支持传递给 with() 的回调约束。

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

幸运的是,PHPUnit 确实支持(至少从 3.7 开始)回调约束,这很有意义,您可以在专门的 Mock 库中找到它,例如 嘲讽

当前 PHPUnit 版本文档< /a> 声明以下内容:

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 Object (...)) 的参数 0 与预期值不匹配。
无法断言指定回调接受 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 );
    }
}

真的不知道这种使用断言并返回 true 的策略如何面向未来 >。它没有记录在案,并且错误消息中存在权衡。您不再收到参数约束消息,因此如果您对多个参数设置断言,您将必须推断(如果可能)哪一个参数失败了。但您确实可以更好地描述闭包内失败的断言。

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

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

Even though the question is already answered fully (how to assert attribute of object passed as argument to mock) I think it's worth noting that PHPUnit supports a callback constraint to be passed to with().

I kept bumping with this thread while trying to find out how to run further assertions on the mocked object arguments. For instance, I needed to check the return value of some method. Obviously, and for sanity sake, there is no methodReturnValueEqualTo() equivalent to the attribute assertion used in the answer above.

Fortunately, PHPUnit does support (as of 3.7 at least) a callback constraint, which makes a lot of sense and is something you can find in specialised Mock libraries like Mockery.

Current PHPUnit version docs state the following:

The callback() constraint can be used for more complex argument verification. This constraint takes a PHP callback as its only argument. The PHP callback will receive the argument to be verified as its only argument and should return TRUE if the argument passes verification and FALSE otherwise.

Therefore, using the callback constraint, the OP example can now be expressed as:

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);
    }
}

And a test fail would look something like:

1) SuTTest::testSuT
Expectation failed for method name is equal to <string:callMe> when invoked 1 time(s)
Parameter 0 for invocation Some::callMe(Foo Object (...)) does not match expected value.
Failed asserting that Foo Object () is accepted by specified callback.

You can now run any logic on that argument.

Even better, despite not being a documented feature in PHPUnit docs, you can actually use assertions and get the benefits of assertion error messages:

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 );
    }
}

Don't really know how future proof is this strategy of using assertions and returning true. It is not documented and there is a tradeoff in the error message. You no longer get the parameter constraint message, so if you set assertions on multiple arguments you will have to infer, if possible, which one failed. But you do get a better description of the failed assertion inside the closure.

1) SuTTest::testSuT
Expectation failed for method name is equal to <string:callMe> when invoked 1 time(s)
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'hi'
+'no'

Hope this helps anyone facing a similar problem.

影子是时光的心 2024-11-13 13:53:32

您只需将预期值传递给“with”方法即可。

->with(1, $object, "paramThree");

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

->with(1, $this->equalTo($object), "paramThree");

因此对于对象您可以使用 $this->isInstanceOf("stdClass") 作为 ->with 的参数有关

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

用于返回 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);
    }
}

结果:

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.

You just pass the expected values to the "with" method.

->with(1, $object, "paramThree");

you can also pass in a range of phpUnit assertions instead of the parameters (it defaults to equal to)

->with(1, $this->equalTo($object), "paramThree");

so for objects you would use $this->isInstanceOf("stdClass") as a parameter to ->with

For the list of possible assertions look into: PHPUnit/Framework/Assert.php

for functions that return a new PHPUnit_Framework_Constraint


Small Demo

The first testcase just matches 2 arguments and works

The second one matches two and fails on argument 2

The last one tests that the passed in object is of type 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);
    }
}

Results in:

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.
握住我的手 2024-11-13 13:53:32

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

$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());

我希望对您有所帮助

This is a simple example that does what you need:

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

And the rest of code:

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

I hope that I've helped you

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文