Исключение исключения PHPUnit

Поэтому я играю с PHPUnit и хотел бы получить представление о выходе, который генерирует PHPUnit, когда я пытаюсь проверить Exception. Я смущен, почему я получаю неудачный тест. Вот мой тест:

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, и вытягивает для него конфигурацию 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 о тестировании исключения, результат выглядит аналогичным. Является ли мой тест работоспособным?


EDIT: новый тест

Используя 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 

С этим я не знаю, откуда приходит третье утверждение. Также я не понимаю, почему Im получает тест Fail. Если я прокомментирую первый тест, то второй пройдет. Кто-нибудь думает?

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

}

Исключение @expectedException – это не очень хорошая идея. Если в вашей тестовой установке выбрано исключение (например, первая строка теста), ваш тест все равно пройдет.

Вы можете использовать:

 //given $conf = ...; try { //when $conf->getConfig(); $this->fail("YourException expected"); //then } catch (YourException $e) {} 

Но это грязно и не будет работать с Exception (потому что phpunit fail также выбрасывает Exception). Таким образом, вам придется использовать настраиваемое исключение.

Вы можете попробовать CatchException от ouzo goodies :

 //given $conf = ...; //when CatchException::when($conf)->getConfig(); //then CatchException::assertThat()->isInstanceOf("Exception"); 

Очевидный ответ на ваш вопрос заключается в том, что на момент запуска теста на самом деле есть работа в вашей базе данных, поэтому ваш код не бросает ожидаемое вами исключение.

Это может быть или не быть правильным, но из вашего кода нам не удастся это знать; на самом деле, из вашего кода нет возможности для теста адекватно протестировать этот случай, потому что вы не знаете, что будет в базе данных во время запуска теста. Вот почему смехотворные зависимости, такие как база данных, настолько важны: вы фактически не будете тестировать свой код так, как вы думаете, его проверяете, если все внешние элементы настроены точно так, как вы хотите.

Мне лично трудно обернуть голову вокруг тестирования, и когда я был вдали от него на несколько дней (например, в понедельник утром), я считаю полезным придерживаться формулы, чтобы вернуть меня в паз. Мне нравится использовать шаблон «данный, когда, тогда» при написании моих тестов. Я выбрал это из книги Джеффри Путина «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 должен вызываться с некоторыми аргументами и возвращать значение. Смысл может быть намного сложнее, чем это, но для начала это довольно просто.

Почему это важно?

Этот тест должен рассказать вам, что происходит, когда в базе данных нет работы. В вашем тесте у вас нет способа узнать, действительно ли ваш тест проверяет это, потому что у вас действительно нет способа узнать, что возвращает класс базы данных при запуске запроса. В моем примере, с другой стороны, я «подделал» класс базы данных и гарантировал, что он вернет именно то, что я хочу, чтобы он возвращался, каждый раз при запуске теста.

Вы ожидаете, что исключение будет выброшено, но оно не будет выбрано. Вот почему ваш тест терпит неудачу. Проверьте, действительно ли ваш код с dataset test.php исключение.