测试异常PHPUnit

时间:2014-05-19 14:15:50

标签: php unit-testing testing phpunit

所以我在玩PHPUnit,想要了解一下当我尝试测试Exception时PHPUnit生成的输出。我很困惑为什么我的测试失败了。这是我的测试:

class ConfigTest extends PHPUnit_Framework_Testcase
{
    public function testTrueIfJobGivenExists()
    {
       $conf = Config::getInstance('test1.php', new Database());
       $setup = $conf->getConfig();
       $this->assertTrue($setup);
    }

    /**
     * @expectedException   Exception
     */
    public function testExceptionIfJobGivenNotExists()
    {
        $conf = Config::getInstance('test.php', new Database());
        $setup = $conf->getConfig();
    }
}

在这里我不是在嘲笑数据库类(我还没有学会如何做到这一点)但基本上代码查找和输入一个名为test.php的作业并为此拉出配置文件。如果作业不存在,则抛出新的异常。这是我的输出:

PHPUnit 4.1.0 by Sebastian Bergmann.

.F

Time: 26 ms, Memory: 3.50Mb

There was 1 failure:

1) ConfigTest::testExceptionIfJobGivenNotExists
Failed asserting that exception of type "Exception" is thrown.

FAILURES!
Tests: 2, Assertions: 2, Failures: 1.

对我来说,似乎测试失败但是查看关于测试异常的PHPUnit文档,输出看起来类似。我的测试有效吗?


编辑:新测试失败

使用Mockery我创建了我的测试:

class ConfigTest extends PHPUnit_Framework_Testcase
{
    public function tearDown()
    {
        Mockery::close();
    }
    public function testTrueIfConfigForGivenJobExists()
    {
        $dbJSON = array( array(
                    'jobConfig' => '{
                        "config": {
                            "aquisition": {
                            "type": "xx",
                            "customerKey": "xxxxx",
                            "login":"xxxx",
                            "password":"xxxxx",
                            "host":"xxxxxx",
                            "account":"",
                            "email":""
                         }
                     }
                 }'
             ) );

        $database = Mockery::mock('Database');
        $database->shouldReceive('select->where->runQuery->fetch')->andReturn($dbJSON);
        $conf = Config::getInstance('getLoadsPE.php', $database);
        $setup = $conf->getConfig();
        $this->assertTrue($setup);
    }

    /**
     * @expectedException   Exception
     */
    public function testExceptionIfJobGivenNotExists()
    {
        $database = Mockery::mock('Database');
        $database->shouldReceive('select->where->runQuery->fetch')->andReturn(null);

        $conf = Config::getInstance('getLoadsPE.php', $database);
        $setup = $conf->getConfig();
        $this->assertTrue($setup);
    }
}

我得到了

PHPUnit 4.1.0 by Sebastian Bergmann.

.F

Time: 39 ms, Memory: 4.75Mb

There was 1 failure:

1) ConfigTest::testExceptionIfJobGivenNotExists
Failed asserting that exception of type "Exception" is thrown.

FAILURES!
Tests: 2, Assertions: 3, Failures: 1

有了这个,我不知道第三个断言来自何处。另外我不知道为什么我得到失败测试。如果我评论第一次测试然后第二次测试。有什么想法吗?

FYI

这就是getConfig()的样子:

public function getConfig()
{
    if ($this->flag) {
        // Config has already been set
        return true;
    }

    $data = self::$database->select('configs', ['jobConfig'])
                            ->where('jobName', self::$jobName)
                            ->runQuery()
                            ->fetch();
    if (empty($data)) {
        throw new Exception("Config Exception: No config available for " . self::$jobName, 1);
    }
    if (count($data) > 1) {
        throw new Exception("Config Exception: More than one config for same job!!!", 1);
    }

    $arr = json_decode($data[0]['jobConfig'], true);
    // maybe threre is a better way of doing this
    if (array_key_exists('aquisition', $arr['config'])) {
        $this->aquisition = $arr['config']['aquisition'];
    }
    if (array_key_exists('ftpSetup', $arr['config'])) {
        $this->ftpSetup = $arr['config']['ftpSetup'];
    }
    if (array_key_exists('fileSetup', $arr['config'])) {
        $this->fileSetup = $arr['config']['fileSetup'];
    }
    if (array_key_exists('fileMaps', $arr['config'])) {
        $this->fileMaps = $arr['config']['fileMaps'];
    }
    if (array_key_exists('fileRows', $arr['config'])) {
        $this->fileRows = $arr['config']['fileRows'];
    }
    $this->flag = true;
    return true;
}

}

3 个答案:

答案 0 :(得分:3)

@expectedException异常在这里不是一个好主意。如果您的测试设置中出现异常(例如,测试的第一行),您的测试仍会通过。

您可以使用:

//given
$conf = ...;

try {
    //when
    $conf->getConfig();

    $this->fail("YourException expected");
//then
} catch (YourException $e) {}

但是它很乱并且无法使用Exception(因为phpunit失败也会抛出异常)。所以你必须使用自定义异常。

您可以尝试CatchException中的ouzo goodies

//given
$conf = ...;

//when
CatchException::when($conf)->getConfig();

//then
CatchException::assertThat()->isInstanceOf("Exception");

答案 1 :(得分:2)

您的问题的明显答案是,在运行测试时,数据库中的 实际上是,因此您的代码不会抛出您期望的异常。

这可能是也可能不正确,但是从您的代码中我们无法知道这一点;实际上,从您的代码中,测试无法对该情况进行充分测试,因为您不知道在运行测试时数据库中会出现什么情况。这就是为什么像数据库那样模拟依赖项是如此重要的原因:你不会按照你认为测试它的方式实际测试你的代码,除非所有外部的东西都按照你想要的方式设置。

我个人觉得很难绕过测试,当我离开它几天(比如周一早上)时,我觉得坚持一个公式让我回到槽。我喜欢在编写测试时使用“给定,何时,然后”模式。我从Jeffrey Way的书“Laravel Testing Decoded”中选择了它。这是它的样子:

public function testSomething()
{
    // given
    // ... set up everything for the test here

    // when
    // ... execute the code that's being tested

    // then
    // ... assert
}

所以在你的情况下,它可能看起来像这样:

/**
 * @expectedException   Exception
 */
public function testExceptionIfJobGivenNotExists()
{
    // given
    // I don't know what your database interface looks like, so I'm
    // making stuff up here
    $database = Mockery::mock("Database");
    $database->shouldReceive("query")->with("SELECT something")->andReturn(null);

    // set up the config object
    $conf = Config::getInstance('test.php', $database);

    // when
    // execute your code here
    $setup = $conf->getConfig();

    // then
    // normally you'd assert something here, but in this case, the exception
    // handler will take care of it
}

(我假设你正在使用Mockery进行模拟。我还假设当你查询一个作业而你的数据库类不存在时,你的数据库类将返回null。)

不要嘲笑嘲笑 - 这真的不那么难。我在这里所做的就是将你的new Database()替换为“假”数据库对象(即模拟)。 shouldReceive方法只是告诉Mockery应该使用一些参数调用query方法并返回一个值。模拟可以比这复杂得多,但首先,这很简单。

为什么这很重要?

这个测试应该告诉你在数据库中没有工作时会发生什么。在您的测试中,您无法知道您的测试是否实际测试了,因为在运行查询时您无法知道数据库类返回的内容。另一方面,在我的例子中,我“伪造”了数据库类,并确保每次运行测试时它都会返回我想要它返回的内容。

答案 2 :(得分:1)

您希望抛出异常,但不会抛出异常。这就是你的测试失败的原因。检查带有数据集test.php的代码是否应该引发异常。