Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/php/271.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Php 单元测试在第n次调用函数时引发的异常_Php_Phpunit - Fatal编程技术网

Php 单元测试在第n次调用函数时引发的异常

Php 单元测试在第n次调用函数时引发的异常,php,phpunit,Php,Phpunit,假设你有一个最终归结为 类寻呼机 { 私人一元; 公共职能下一步() { 如果($this->i>=3){ 抛出新的边界外sexception(); } $this->i++; } } 你将如何对这个类进行单元测试。例如,使用PHPUnit测试在第三次调用next()时是否抛出异常?我已经添加了我的尝试作为答案,但我不确定这是否真的是一条路。以下是我目前拥有的,但我想知道是否有更好的方法来做到这一点 class页面测试 { 公共功能设置() { $this->pager=new pager()

假设你有一个最终归结为

类寻呼机
{
私人一元;
公共职能下一步()
{
如果($this->i>=3){
抛出新的边界外sexception();
}
$this->i++;
}
}

你将如何对这个类进行单元测试。例如,使用PHPUnit测试在第三次调用
next()
时是否抛出异常?我已经添加了我的尝试作为答案,但我不确定这是否真的是一条路。

以下是我目前拥有的,但我想知道是否有更好的方法来做到这一点

class页面测试
{
公共功能设置()
{
$this->pager=new pager();
}
公共函数testTooManyNextCalls()
{
对于($i=0;$i<10;$i++){
试一试{
$this->pager->next();
}捕获(边界外异常$e){
如果($i==3){
返回;
}否则{
$this->fail('OutOfBoundsException在迭代时意外抛出。$i);
}
}
如果($i>3){
$this->fail('OutOfBoundsException未在预期时抛出');
}
}
}
}

您可以将值$this->i传递给异常实例化,然后异常实例化将作为异常消息

class Pager
{
    private $i;

    public function next()
    {
        if ($this->i >= 3) {
            throw new OutOfBoundsException($this->i);
        }

        $this->i++;
    }
}
$a=new Pager();
$a->next();
$a->next();
$a->next();
$a->next();

//outputs: "Exception: 3"

您可以使用以下内容:

class PagerTest extends PHPUnit_Framework_TestCase {

    /**
     * @expectedException OutOfBoundsException
     */
     public function testTooManyNextCalls() {
         $this->pager = new Pager();

         $this->pager->next();
         $this->pager->next();
         $this->pager->next();

         $this->assertTrue(false);
    }
}

如果在第三个方法调用中抛出异常,则永远不会到达始终失败的assert语句,并且测试应该通过。另一方面,如果没有抛出异常,测试将失败。

在前两个调用上测试
null
,并测试抛出的异常,如下所示:

class PagerTest
{
    public function setUp()
    {
        $this->pager = new Pager();
    }

    public function testTooManyNextCalls()
    {
        $this->assertNull($this->pager->next());
        $this->assertNull($this->pager->next());
        $this->assertNull($this->pager->next());

        $this->setExpectedException('OutOfBoundsException');
        $this->pager->next();
    }
}

在进行单元测试时,避免测试实现细节非常重要。相反,您希望限制自己只测试代码的公共接口。为什么?因为实现细节经常改变,但是您的API应该很少改变。测试实现细节意味着随着这些实现的变化,您将不得不不断地重写测试,而您不希望被困在这一过程中

那么这对OP的代码意味着什么呢?让我们看看public
Pager::next
方法。使用Pager类API的代码不关心Pager::next如何确定是否应引发异常。它只关心
Pager::next
在出现问题时实际抛出异常

我们不想测试该方法如何决定抛出
OutOfBoundsException
——这是一个实现细节。我们只想测试它是否在适当的时候这样做

所以为了测试这个场景,我们模拟了一种情况,
Pager::next
将抛出。为了实现这一点,我们只需实现所谓的“测试接缝”

请注意,此测试特别不关心实现方法
Pager::isValid
如何确定当前增量无效。该测试只是模拟调用该方法时返回
false
,这样我们就可以测试我们的public
Pager::next
方法在应该抛出异常时是否抛出异常


PHPUnit模拟API已在中完全介绍。API并不是世界历史上最直观的东西,但经过反复使用,它通常是有意义的。

除非有一种方法可以解决您的问题,请把它放在您的问题中。这是一种解决方案,但我希望有一种更好的解决方案。我希望有一种更好的解决方案。谢谢你@PeeHaa-看来你错过了“phpunit”标签。我想问题是如何单元测试在第三次调用时是否发生异常。不确定,但在第一次/第二次调用中抛出异常时,will也会通过吗?你是对的-来自任何一次调用的异常都会使测试通过。第二次测试只调用了2次就可以解决这个问题,但这仍然不是一个最佳的解决方案。这比我之前做的要干净得多,并且使用了我想要的断言。真不敢相信我竟然没有想到这个方法!您应该真正测试这是否按预期工作。因为当异常在第一次或第二次
next()
调用中抛出时,我无法100%确定这是通过还是失败。@PeeHaa-re您的评论:能否将
setExpectedException
调用移到前两次调用之后,如果
Pager::next
的合同是“允许您最多调用三次,但不能超过三次”,那么这是最干净的解决方案。请注意,
$i
开始时等于零,因此在第四次调用时会引发异常。@DavidHarkness
$i
基于零的事实在OP中不存在,但我已更新了我的答案。在不编辑答案的情况下,我想提请注意封装问题。通常,您希望保持
私有
事物
私有
,但请注意,您不能模拟
私有
实例方法。如果降低实现方法或属性的可访问性可以显著提高可测试性(例如,允许模拟),我强烈认为
protected
是有帮助的,可以忽略封装问题。在某个时候,您必须测试
isValid
Pager::next
的总体合同,客户机代码关心的是哪一个。如果有效性测试在
Pager
之外,我会使用这种方法,但不是在这里。@DavidHarkness Testing
isValid
应该被理解;必须这样做。关键是,您不应该直接测试它,您应该在不模拟其结果的场景中测试公共接口,从而在
isValid
上获得测试覆盖率。我们不需要直接测试PHP计算
$i>=3<?php
class Pager
{
    protected $i;

    public function next()
    {
        if ($this->isValid()) {
            $this->i++;
        } else {
            throw new OutOfBoundsException();
        }
    }

    protected function isValid() {
        return $this->i < 3;
    }
}
class PagerTest extends PHPUnit_Framework_TestCase
{
    /**
     * @covers Pager::next
     * @expectedException OutOfBoundsException
     */
    public function testNextThrowsExceptionOnInvalidIncrementValue() {
        $pagerMock = $this->getMock('Pager', array('isValid'));
        $pagerMock->expects($this->once())
                  ->method('isValid')
                  ->will($this->returnValue(false));
        $pagerMock->next();
    }
}