PHPUnit 模拟 - 当方法调用 x 次时立即失败

发布于 2024-11-14 02:12:55 字数 693 浏览 2 评论 0 原文

使用 PHPUnit,我正在使用 ->at() 测试一系列方法调用,如下所示:

$mock->expects($this->at(0))->method('execute')->will($this->returnValue('foo'));
$mock->expects($this->at(1))->method('execute')->will($this->returnValue('bar'));
$mock->expects($this->at(2))->method('execute')->will($this->returnValue('baz'));

How can I set up the mock以便在上述场景中调用 ifexecute()四次或四次以上,会立即失败吗?我尝试了这个:

$mock->expects($this->at(3))->method('execute')->will($this->throwException(new Exception('Called)太多次了。')));

但是,如果execute() 没有被调用四次,这也会失败。它需要立即失败,否则被测系统会产生自己的错误,从而导致生成的错误消息不明确。

With PHPUnit, I am testing a sequence of method calls using ->at(), like so:

$mock->expects($this->at(0))->method('execute')->will($this->returnValue('foo'));
$mock->expects($this->at(1))->method('execute')->will($this->returnValue('bar'));
$mock->expects($this->at(2))->method('execute')->will($this->returnValue('baz'));

How can I set up the mock so that, in the above scenario, if execute() is called four or more times, it will immediately fail? I tried this:

$mock->expects($this->at(3))->method('execute')->will($this->throwException(new Exception('Called too many times.')));

But this also fails if execute() is not called four times. It needs to fail immediately, otherwise the system under test will produce errors of its own, which causes the resulting error message to be unclear.

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

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

发布评论

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

评论(4

花桑 2024-11-21 02:12:55

我最终设法找到了解决方案。我使用了 $this->returnCallback() 的组合并传递 PHPUnit 匹配器来跟踪调用计数。然后你可以抛出一个 PHPUnit 异常,这样你也能得到很好的输出:

$matcher = $this->any();
$mock
    ->expects($matcher)
    ->method('execute')
    ->will($this->returnCallback(function() use($matcher) {
        switch ($matcher->getInvocationCount())
        {
            case 0: return 'foo';
            case 1: return 'bar';
            case 2: return 'baz';
        }

        throw new PHPUnit_Framework_ExpectationFailedException('Called too many times.');
    }))
;

I managed to find a solution in the end. I used a comination of $this->returnCallback() and passing the PHPUnit matcher to keep track of the invocation count. You can then throw a PHPUnit exception so that you get nice output too:

$matcher = $this->any();
$mock
    ->expects($matcher)
    ->method('execute')
    ->will($this->returnCallback(function() use($matcher) {
        switch ($matcher->getInvocationCount())
        {
            case 0: return 'foo';
            case 1: return 'bar';
            case 2: return 'baz';
        }

        throw new PHPUnit_Framework_ExpectationFailedException('Called too many times.');
    }))
;
海之角 2024-11-21 02:12:55

对于像这样的特殊情况,我通常使用如下内容:

public function myMockCallback() {
     ++$this -> _myCounter;
     if( $this -> _myCounter > 3 ) {
          // THROW EXCEPTION OR TRIGGER ERROR
     }
     ... THEN YOUR CASE STATEMENT OR IF/ELSE WITH YOUR CHOICE OF RETURN VALUES
} 

... INSIDE TEST FUNCTION ....

$mockObject ->expects($this->any())
            ->method('myMethod')
            ->will($this->returnCallback( array ($this, 'myMockCallback' )));

For special cases like this, I typically use something like the following:

public function myMockCallback() {
     ++$this -> _myCounter;
     if( $this -> _myCounter > 3 ) {
          // THROW EXCEPTION OR TRIGGER ERROR
     }
     ... THEN YOUR CASE STATEMENT OR IF/ELSE WITH YOUR CHOICE OF RETURN VALUES
} 

... INSIDE TEST FUNCTION ....

$mockObject ->expects($this->any())
            ->method('myMethod')
            ->will($this->returnCallback( array ($this, 'myMockCallback' )));
春夜浅 2024-11-21 02:12:55

您可以将测试分成 2 个 依赖方法,使用@depends 注释。

在这种情况下,您的第一个测试仅测试是否有确切的 3 个方法执行,第二个测试是其他逻辑。

You could separate test to 2 dependent methods, using @depends annotation.

In this case your first test only tests that there are exact 3 method executions, and second - other logic.

2024-11-21 02:12:55

使用 数据提供者

class MyTest extends PHPUnit.... { 
    /** 
     * @var const how much till throwing exception 
    */
    const MAX_EXECUTE_TILL_EXCEPTION = 3; 

    public function setUp(){} 

    public function tearDown(){} 

    /**
     *  @dataProvider MyExecuteProvider  
    */
    pulbic function testMyExecuteReturnFalse($data){
        $mock = //setup your mock here

        //if using "$ret" doesn't work you cant just call another private helper that will decide if you need to 
        //  return value or throwing exception 
        if (self::MAX_EXECUTE_TILL_EXCEPTION == $data){ 
            $ret = $this->throwException(new Exception('Called too many times.'));
        } else {
            $ret = $this->returnValue('foo'); 
        } 
        $mock->expects($this->at($data))->method('execute')->will($ret);
    }

    public function MyExecuteProvider(){
            return array(
            0,1,2,3
        )
    }
}

这只是另一个想法,我认为 zerkms 也提出了非常好的想法

What about using data providers?

class MyTest extends PHPUnit.... { 
    /** 
     * @var const how much till throwing exception 
    */
    const MAX_EXECUTE_TILL_EXCEPTION = 3; 

    public function setUp(){} 

    public function tearDown(){} 

    /**
     *  @dataProvider MyExecuteProvider  
    */
    pulbic function testMyExecuteReturnFalse($data){
        $mock = //setup your mock here

        //if using "$ret" doesn't work you cant just call another private helper that will decide if you need to 
        //  return value or throwing exception 
        if (self::MAX_EXECUTE_TILL_EXCEPTION == $data){ 
            $ret = $this->throwException(new Exception('Called too many times.'));
        } else {
            $ret = $this->returnValue('foo'); 
        } 
        $mock->expects($this->at($data))->method('execute')->will($ret);
    }

    public function MyExecuteProvider(){
            return array(
            0,1,2,3
        )
    }
}

This is just another idea, and I think that zerkms suggested very good idea as well

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