【问题标题】:Testing exception PHPUnit测试异常 PHPUnit
【发布时间】:2014-07-07 13:14:52
【问题描述】:

因此,我使用 PHPUnit 并想了解当我尝试测试异常时 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();
    }
}

在这里,我不是在模拟 Database 类(我还没有学会如何做到这一点),但基本上代码会查找并输入一个名为 test.php 的作业,并为此拉取配置 col。如果作业不存在,它会抛出一个新的异常。这是我的输出:

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

有了这个,我不知道第三个断言来自哪里。我也不明白为什么我会通过失败测试。如果我评论第一个测试然后第二个通过。有什么想法吗?

仅供参考

这是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;
}

}

【问题讨论】:

  • 唯一的原因是该测试没有抛出异常(Exception 类型)

标签: php unit-testing testing phpunit


【解决方案1】:

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

你可以使用:

//given
$conf = ...;

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

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

但是它很乱,并且不能与异常一起工作(因为 phpunit 失败也会抛出异常)。所以你必须使用自定义异常。

您可以从ouzo goodies 尝试CatchException

//given
$conf = ...;

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

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

【讨论】:

    【解决方案2】:

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

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

    我个人觉得很难全神贯注于测试,当我离开它几天(比如星期一早上)时,我发现坚持一个公式让我重新回到测试中很有帮助槽。在编写测试时,我喜欢使用“given, when, then”模式。我是从 Jeffrey Way 的书“Laravel 测试解码”中挑选出来的。这是它的样子:

    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 方法并返回一个值。模拟可以比这复杂得多,但一开始,这很简单。

    为什么这很重要?

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

    【讨论】:

    • 我同意“给定”部分应该在那里,你的样本看起来更具描述性。我没有使用 Mockery,但会尝试一下。谢谢!
    • 所以我的数据库类不像你的例子那样直接,所以我在模拟类时遇到了问题,因为我对 Mockery 完全不感兴趣。你愿意看看this
    【解决方案3】:

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

    【讨论】:

    • 如果我运行$conf = Config::getInstance('test.php', new Database()); $setup = $conf->getConfig(); 我确定我会抛出异常我得到PHP Fatal error: Uncaught exception 'Exception' with message 'Config Exception: No config available for test.php'
    • 你在使用命名空间吗?
    • 不,我没有使用命名空间
    • 只需在测试中添加throw new Exception(); 以确保确实抛出异常:)
    猜你喜欢
    • 2015-10-17
    • 2016-04-27
    • 1970-01-01
    • 1970-01-01
    • 2011-03-29
    • 2012-03-20
    • 2018-03-31
    • 2020-12-21
    • 1970-01-01
    相关资源
    最近更新 更多