所以我在玩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;
}
}
答案 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
的代码是否应该引发异常。