Is there a way to get PHPUnit to print inner exceptions when something goes wrong?
Here's a simple example:
class TestTest extends \PHPUnit\Framework\TestCase
{
function test() {
throw new Exception("Outer exception message", 0, new Exception("Inner exception message"));
}
}
I want to see both messages but I only see the outer one.
Decorator pattern is helpful.
class OuterDecoratorException extends \Exception {
private $innerException;
public function __construct(string $message, \Exception $innerException) {
$this->message = $message;
$this->innerException = $innerException;
}
public function __toString(): string {
return \sprintf(
"Outer: %s Inner: %s",
$this->getMessage(),
$this->innerException->getMessage()
);
}
}
throw new OuterDecoratorException("Outer message", new \Exception("Inner message"));
2. Prefer over a decorator, direct compilation of exception message in every test.
class TestTest extends \PHPUnit\Framework\TestCase
{
function test() {
throw new \Exception(
sprintf(
"Inner: %s Outer: %s",
"Inner message",
(new \Exception("Outer message"))->getMessage()
),
0
);
}
}
Otherwise you must extends a library. See manual - how to extends a framework.
PHPUnit already supports this.
$ cat TestTest.php
<?php
require_once 'vendor/autoload.php';
class TestTest extends PHPUnit_Framework_TestCase {
function test() {
throw new Exception("Outer exception message", 0, new Exception("Inner exception message"));
}
}
$ ./vendor/bin/phpunit TestTest.php
PHPUnit 5.3.5 by Sebastian Bergmann and contributors.
E 1 / 1 (100%)
Time: 57 ms, Memory: 4.25MB
There was 1 error:
1) TestTest::test
Exception: Outer exception message
/tmp/TestTest.php:7
Caused by
Exception: Inner exception message
/tmp/TestTest.php:7
FAILURES!
Tests: 1, Assertions: 0, Errors: 1.
That it doesn't suggests your environment isn't booting properly. I suspect it's because you're using namespaces, which IIRC, PHPUnit doesn't support. Change \PHPUnit\Framework\TestCase to PHPUnit_Framework_TestCase.
Related
Update: with $stub = $this->createMock('Config'); this example works, but I get a warning:
OK, but incomplete, skipped, or risky tests! Tests: 1, Assertions: 0,
Risky: 1.
In the video-tutorial this example works without any warnings. Is it possible to fix this warning?
I can't find why I am getting this error and how to fix it. This code is from a video tutorial. And in the Video it works. Maybe a typo?
Error:
c:\laragon\www\phpunit λ phpunit --colors tests\DateFormatterTest.php
PHPUnit 6.0.0 by Sebastian Bergmann and contributors.
E 1
/ 1 (100%)
Time: 35 ms, Memory: 4.00MB
There was 1 error:
1) DateFormatterTest::testFormattingDatesBasedOnConfig Error: Call to
undefined method DateFormatterTest::getMock()
C:\laragon\www\phpunit\tests\DateFormatterTest.php:10
ERRORS! Tests: 1, Assertions: 0, Errors: 1.
Here my code:
Config.php
<?php
class Config {
public function get() {
return 'd-m-Y';
}
}
DateFormatter.php
class DateFormatter {
protected $config;
public function __construct (Config $config) {
$this->config = $config;
}
public function getFormattedDate($timestamp) {
return date($this->config->get('date.format'), $timestamp);
}
}
DateFormatterTest.php
<?php
use PHPUnit\Framework\TestCase;
require_once 'C:\laragon\www\phpunit\src\DateFormatter.php';
require_once 'C:\laragon\www\phpunit\src\Config.php';
class DateFormatterTest extends TestCase {
public function testFormattingDatesBasedOnConfig() {
$stub = $this->getMock('Config');
var_dump($stub);
}
}
getMock() no longer exists in PHPUnit 6. Use createMock() or getMockBuilder() instead.
I am new to PHP and trying to write a basic test case that verifies a connection to a database. Clearly I'm missing something fundamental. I understand from reading the manual online that this involves extending the PHPUnit_Extensions_Database_TestCase and implementing a couple of functions (getConnection() and getDataSet()). Please see my code below of the simplest case I could come up with to still get the head-scratching issue I'm encountering:
<?php
abstract class DBTest extends PHPUnit_Extensions_Database_TestCase
{
public function getConnection()
{
return true;
}
public function getDataSet()
{
return true;
}
}
?>
As you can see, the tests do nothing but return true. However, when I do a "phpUnit DBTest" I get the following message back:
PHPUnit 4.2.6 by Sebastian Bergmann.
F
Time: 1 ms, Memory: 7.50Mb
There was 1 failure:
1) Warning
No tests found in class "DBTest".
FAILURES!
Tests: 1, Assertions: 0, Failures: 1.
What am I missing? Any advice would help. Thanks.
PHPUnit complains about not finding any test. You must add at least a test method:
<?php
abstract class DBTest extends PHPUnit_Extensions_Database_TestCase
{
public function getConnection()
{
return true;
}
public function getDataSet()
{
return true;
}
public function testDummy()
{
$this->assertTrue(true);
}
}
?>
I have div() in Cal class in my CalTest class has following methods to test the div().
public fucnction div($a,$b){
if($b == 0){
throw new Exception("Divided by zero");
}
return $a/$b
}
I can pass only testDiv() but testDiv2().
I want to catch check wheather the exeption thrown correctly using PHPUnit. What am I missing here?? Your help is highly appreciated. Thank you!
Your 2nd screenshot (the one with the error) has
"#expectedException Exception"
while the third has
#expectedException InvalidArgumentException
Do you really still get the error? Did you save the file?
Works for me:
Foo.php
<?php
class Foo
{
static function t()
{
throw new InvalidArgumentException('Hi there');
}
}
?>
FooTest.php
<?php
require_once 'Foo.php';
class FooTest extends PHPUnit_Framework_TestCase
{
/**
* #expectedException InvalidArgumentException
*/
public function testT()
{
Foo::t();
}
}
?>
Result
$ phpunit .
PHPUnit 3.6.10 by Sebastian Bergmann.
.
Time: 0 seconds, Memory: 5.25Mb
OK (1 test, 1 assertion)
Just came across the same issue. For some reason PHPUnit will not allow you to set the expectedException to a generic exception and I am not sure why. Personally I opt to throw custom Exception codes rather than have to create a new exception class every time I want to differentiate exceptions.
Here is how I got around it:
/**
* #expectedException Test_Exception
*/
public function testDivByZero()
{
try {
// Fyi you don't need to do an assert test here, as we are only testing the exception, so just make the call
$result = $this->object->div(1,0);
} catch (Exception $e) {
if ('Exception' === get_class($e)) {
throw new Test_Exception($e->getMessage(), $e->getCode());
}
}
}
// Test_Exception.php
class Test_Exception extends Exception
{
public function __construct($message = null, $code = 0, Exception $previous = null)
{
parent::__construct($message, $code, $previous);
}
}
This will allow you to engineer your code the way you want, and throw "generic" exceptions. Basically it just tests the Exception class and if it's generic, re-wrap it as a different exception; Test_Exception.
-- Update --
Found out yesterday that they have removed the generic exception restriction in the current "master" branch, which will be 3.7. Apparently the lead engineer has no desire of patching 3.6.
This problem has been fixed in PHPUnit 3.7.x
You can use the following Generic Exception in PHPUnit 3.7.x
/**
* #expectedException Exception
*/
It says so in the exception thrown by PHPUnit:
You must not except the generic exception class.
Make your class throw more detailed Exceptions, and further specify the exception type your expecting in your unit test.
you can do something like this
write your unit test so you inject the values required to trigger the exception then
//assert
$this->assertTrue($this->setExpectedException('PHPUnit_Framework_ExpectationFailedException'));
I create a parent class method to do it. In fact, this class came from the laravel but is valid in any context.
The cool part of this method is using anonymous functions into PHP
<?php
class TestCase
/* if you are not using laravel, you dont need this
extends Illuminate\Foundation\Testing\TestCase */ {
protected function assertThrows( $function , $classException = "/Exception" )
{
try
{
// Anonymous functions FTW
$function();
}
catch( /Exception $objException )
{
// the assertInstanceOf should solve from here but
// it is not working that great
// #see http://stackoverflow.com/questions/16833923/phpunit-assertinstanceof-not-working
if( $objException instanceof $classException ) {
return $this->assertTrue( true );
} else {
// this will throw a error with a cool message
return $this->assertEquals( $classException , get_class( $objException ));
}
}
// no exception happened.
return $this->fail( "Exception " . $classException . " expected." );
}
}
class LanguageTest extends TestCase {
/**
* Test the translation
*
* #return void
*/
public function testTranslation()
{
// this test may be ok
$this->assertThrows(
function(){ throw new Full\Path\Exception(":("); },
"Full\Path\Exception" );
// this test may fail
$this->assertThrows(
function(){ return 1 + 1; },
"Some\Excepted\Exception" );
// this test may work
$this->assertThrows(
function(){ throw new Exception( "sad" ); }
);
}
}
I use a class to execute a testsuite with PhpUnit like :
$suite = new PHPUnit_Framework_TestSuite('PHPUnit Framework');
$suite->addTestSuite('ClassOne');
$suite->addTestSuite('ClassTwo');
return $suite;
To start the unit test :
# phpunit --stop-on-failure TestSuite.php
If "ClassOne" has an error or exception, the test continue with "ClassTwo".
How I could stop all the testsuites if the first test failed?
Works with PHPUnit 3.5.14
Using the code below the outputs are as expected:
Two fails running it normally:
phpunit AllTests.php
PHPUnit 3.5.14 by Sebastian Bergmann.
FF
Time: 0 seconds, Memory: 6.25Mb
There were 2 failures:
1) Framework_AssertTest::testFails
Failed asserting that <boolean:true> matches expected <boolean:false>.
/home/edo/test/AllTests.php:7
2) Other_AssertTest::testFails
Failed asserting that <boolean:true> matches expected <boolean:false>.
/home/edo/test/AllTests.php:13
FAILURES!
Tests: 2, Assertions: 2, Failures: 2.
and one fail running it with --stop-on-failure
phpunit --stop-on-failure AllTests.php
PHPUnit 3.5.14 by Sebastian Bergmann.
F
Time: 0 seconds, Memory: 6.25Mb
There was 1 failure:
1) Framework_AssertTest::testFails
Failed asserting that <boolean:true> matches expected <boolean:false>.
/home/edo/test/AllTests.php:7
FAILURES!
Tests: 1, Assertions: 1, Failures: 1.
AllTests.php
<?php
class Framework_AssertTest extends PHPUnit_Framework_TestCase {
public function testFails() {
$this->assertSame(false, true);
}
}
class Other_AssertTest extends PHPUnit_Framework_TestCase {
public function testFails() {
$this->assertSame(false, true);
}
}
class Framework_AllTests
{
public static function suite()
{
$suite = new PHPUnit_Framework_TestSuite('PHPUnit Framework');
$suite->addTestSuite('Framework_AssertTest');
$suite->addTestSuite('Other_AssertTest');
return $suite;
}
}
class Other_AllTests
{
public static function suite()
{
$suite = new PHPUnit_Framework_TestSuite('PHPUnit Framework');
$suite->addTestSuite('Other_AssertTest');
return $suite;
}
}
class AllTests
{
public static function suite()
{
$suite = new PHPUnit_Framework_TestSuite('PHPUnit');
$suite->addTest(Framework_AllTests::suite());
return $suite;
}
}
As a side node. If you look at the documentation of the current version only "suites by file system" and "suites by xml config" are explained options. Just to keep that in mind if you can migrate at the point.
Sounds like a bug in phpunit. Upgrade phpunit to the latest version. If that does not help, open a bug report.
In my TDD project I am trying to test a method in an abstract class.
abstract class Database_Mapper_Abstract
{
public function setTable($sTablename){
return('foo');
}
}
This is the way I wrote my simple test:
public function testCanSetTable(){
$oMock = $this->getMockForAbstractClass('JCMS_Database_Mapper_Abstract');
$oMock->expects($this->once())
->method('setTable')
->with($this->equalTo('foo'))
->will($this->returnValue('foo'));
$this->assertEquals('foo',$oMock->setTable());
}
When I run this test i get the following error:
PHPUnit 3.5.13 by Sebastian Bergmann.
E
Time: 1 second, Memory: 6.75Mb
There was 1 error:
1)
Database_Mapper_AbstractTest::testCanSetTable
Missing argument 1 for
Database_Mapper_Abstract::setTable(), called in
K:\xampp\htdocs\tests\library\Database\Mapper\Abstract.php
on line 15 and defined
K:\xampp\htdocs\library\Database\Mapper\Abstract.php:4
K:\xampp\htdocs\tests\library\Database\Mapper\Abstract.php:15
FAILURES! Tests: 1, Assertions: 0,
Errors: 1.
The way I understand this is that it can't find the argument for the setTable function.
But I set it with the with() method. I also tried with('foo'). That also doesn't help me.
Does anyone have an idea?
Testing an abstract class:
For testing an abstract class you don't want to use the "create behavior methods".
Just getMockForAbstractClass() like this:
<?php
abstract class JCMS_Database_Mapper_Abstract
{
public function setTable($sTablename){
return $sTablename."_test";
}
}
class myTest extends PHPUnit_Framework_TestCase {
public function testCanSetTable(){
$oMock = $this->getMockForAbstractClass('JCMS_Database_Mapper_Abstract');
$this->assertEquals('foo_test', $oMock->setTable('foo'));
}
}
You just use the mocking functionality to create an instance of that abstract class and test against that.
It's only a shortcut for writing
class MyDataMapperAbstractTest extends JCMS_Database_Mapper_Abstract {
// and filling out the methods
}
The actual error:
What happens is that you have a method with one parameter:
public function setTable($sTablename){
but you call it with zero paremters:
$oMock->setTable()
so you get an error from PHP and if PHP throws a warnings PHPUnit will show you an error.
Reproduce:
<?php
abstract class JCMS_Database_Mapper_Abstract
{
public function setTable($sTablename){
return('foo');
}
}
class myTest extends PHPUnit_Framework_TestCase {
public function testCanSetTable(){
$oMock = $this->getMockForAbstractClass('JCMS_Database_Mapper_Abstract');
$oMock->expects($this->once())
->method('setTable')
->with($this->equalTo('foo'))
->will($this->returnValue('foo'));
$this->assertEquals('foo',$oMock->setTable());
}
}
Results in:
phpunit blub.php
PHPUnit 3.5.13 by Sebastian Bergmann.
E
Time: 0 seconds, Memory: 3.50Mb
There was 1 error:
1) myTest::testCanSetTable
Missing argument 1 for JCMS_Database_Mapper_Abstract::setTable(), called in /home/.../blub.php on line 19 and defined
Fixing
Change:
$this->assertEquals('foo',$oMock->setTable());
to
$this->assertEquals('foo',$oMock->setTable('foo'));
then you don't get a PHP Warning and it should work out :)