Skip to content

Commit

Permalink
TestCase: annotation @throws is applied to testMethods() only, not to…
Browse files Browse the repository at this point in the history
… setUp() nor tearDown() (BC break) [Closes nette#238]

BC break: default value of runTest() 2nd parameter is NULL, empty array bypasses dataprovider
BC break: runTest() now takes into account a @throws annotation, so expected exception is caught, not thrown
BC break: runTest() now run for all dataprovider records
  • Loading branch information
milo committed Aug 12, 2015
1 parent fc6d855 commit 710c4b5
Show file tree
Hide file tree
Showing 2 changed files with 150 additions and 53 deletions.
107 changes: 54 additions & 53 deletions src/Framework/TestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,10 @@ public function run($method = NULL)

if ($method === NULL) {
foreach ($methods as $method) {
$this->runMethod($method);
$this->runTest($method);
}
} elseif (in_array($method, $methods, TRUE)) {
$this->runMethod($method);
$this->runTest($method);
} else {
throw new TestCaseException("Method '$method' does not exist or it is not a testing method.");
}
Expand All @@ -57,16 +57,17 @@ public function run($method = NULL)

/**
* Runs the test method.
* @param string test method name
* @param array test method parameters (dataprovider bypass)
* @return void
*/
private function runMethod($method)
public function runTest($method, array $args = NULL)
{
$method = new \ReflectionMethod($this, $method);
if (!$method->isPublic()) {
throw new TestCaseException("Method {$method->getName()} is not public. Make it public or rename it.");
}

$data = array();
$info = Helpers::parseDocComment($method->getDocComment()) + array('dataprovider' => NULL, 'throws' => NULL);

if ($info['throws'] === '') {
Expand All @@ -77,66 +78,66 @@ private function runMethod($method)
$throws = preg_split('#\s+#', $info['throws'], 2) + array(NULL, NULL);
}

$defaultParams = array();
foreach ($method->getParameters() as $param) {
$defaultParams[$param->getName()] = $param->isDefaultValueAvailable() ? $param->getDefaultValue() : NULL;
}

foreach ((array) $info['dataprovider'] as $provider) {
$res = $this->getData($provider);
if (!is_array($res)) {
throw new TestCaseException("Data provider $provider() doesn't return array.");
$data = array();
if ($args === NULL) {
$defaultParams = array();
foreach ($method->getParameters() as $param) {
$defaultParams[$param->getName()] = $param->isDefaultValueAvailable() ? $param->getDefaultValue() : NULL;
}
foreach ($res as $set) {
$data[] = is_string(key($set)) ? array_merge($defaultParams, $set) : $set;

foreach ((array) $info['dataprovider'] as $provider) {
$res = $this->getData($provider);
if (!is_array($res)) {
throw new TestCaseException("Data provider $provider() doesn't return array.");
}
foreach ($res as $set) {
$data[] = is_string(key($set)) ? array_merge($defaultParams, $set) : $set;
}
}
}

if (!$info['dataprovider']) {
if ($method->getNumberOfRequiredParameters()) {
throw new TestCaseException("Method {$method->getName()}() has arguments, but @dataProvider is missing.");
if (!$info['dataprovider']) {
if ($method->getNumberOfRequiredParameters()) {
throw new TestCaseException("Method {$method->getName()}() has arguments, but @dataProvider is missing.");
}
$data[] = array();
}
$data[] = array();
} else {
$data[] = $args;
}

foreach ($data as $args) {
foreach ($data as $params) {
try {
if ($info['throws']) {
$tmp = $this;
$e = Assert::error(function () use ($tmp, $method, $args) {
$tmp->runTest($method->getName(), $args);
}, $throws[0], $throws[1]);
if ($e instanceof AssertException) {
throw $e;
$this->setUp();

try {
if ($info['throws']) {
$tmp = $this;
$e = Assert::error(function () use ($tmp, $method, $params) {
call_user_func_array(array($tmp, $method->getName()), $params);
}, $throws[0], $throws[1]);
if ($e instanceof AssertException) {
throw $e;
}
} else {
call_user_func_array(array($this, $method->getName()), $params);
}
} else {
$this->runTest($method->getName(), $args);
} catch (\Exception $testException) {
}
} catch (AssertException $e) {
throw $e->setMessage("$e->origMessage in {$method->getName()}" . (substr(Dumper::toLine($args), 5)));
}
}
}

try {
$this->tearDown();
} catch (\Exception $tearDownException) {
}

/**
* Runs the single test.
* @return void
*/
public function runTest($name, array $args = array())
{
$this->setUp();
try {
call_user_func_array(array($this, $name), $args);
} catch (\Exception $e) {
}
try {
$this->tearDown();
} catch (\Exception $tearDownEx) {
throw isset($e) ? $e : $tearDownEx;
}
if (isset($e)) {
throw $e;
if (isset($testException)) {
throw $testException;
} elseif (isset($tearDownException)) {
throw $tearDownException;
}

} catch (AssertException $e) {
throw $e->setMessage("$e->origMessage in {$method->getName()}" . (substr(Dumper::toLine($params), 5)));
}
}
}

Expand Down
96 changes: 96 additions & 0 deletions tests/Framework/TestCase.annotationThrows.setUp.tearDown.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
<?php

use Tester\Assert;
use Tester\TestCase;

require __DIR__ . '/../bootstrap.php';


class SuccessTestCase extends TestCase
{
/** @throws Exception SuccessTestCase::testMe */
public function testMe()
{
throw new Exception(__METHOD__);
}
}

$test = new SuccessTestCase;
$test->run();



class FailingTestCase extends TestCase
{
/** @throws RuntimeException Wrong message */
public function testMe()
{
throw new Exception(__METHOD__);
}
}

Assert::exception(function () {
$test = new FailingTestCase;
$test->run();
}, 'Tester\AssertException', 'RuntimeException was expected but got Exception (FailingTestCase::testMe) in testMe()');



class SuccessButSetUpFails extends SuccessTestCase
{
public function setUp()
{
throw new Exception(__METHOD__);
}
}

Assert::exception(function () {
$test = new SuccessButSetUpFails;
$test->run();
}, 'Exception', 'SuccessButSetUpFails::setUp');



class SuccessButTearDownFails extends SuccessTestCase
{
public function tearDown()
{
throw new Exception(__METHOD__);
}
}

Assert::exception(function () {
$test = new SuccessButTearDownFails;
$test->run();
}, 'Exception', 'SuccessButTearDownFails::tearDown');



class FailingAndSetUpFails extends FailingTestCase
{
public function setUp()
{
throw new Exception(__METHOD__);
}
}

Assert::exception(function () {
$test = new FailingAndSetUpFails;
$test->run();
}, 'Exception', 'FailingAndSetUpFails::setUp');



class FailingAndTearDownFails extends FailingTestCase
{
public function tearDown()
{
throw new Exception(__METHOD__);
}
}

// tearDown() exception is never thrown when @throws assertion fails
Assert::exception(function () {
$test = new FailingAndTearDownFails;
$test->run();
}, 'Tester\AssertException', 'RuntimeException was expected but got Exception (FailingTestCase::testMe) in testMe()');

0 comments on commit 710c4b5

Please sign in to comment.