I have this function in a class
public function doSomething()
{
try {
$result = functionCall();
if ($result === true) {
$this->doGoodResult('With Success Message');
} else {
$this->doBadResult('ERROR - unable to do something');
}
} catch (Exception $e) {
$this->doBadResult('Did you divide by zero?');
}
}
and I'm trying to test it with mocking out the extra functions
public function test_doSomthingWithBadResultGetsDoBadResultCalled()
{
$ajc = $this->getMockBuilder('MyClass')
->setMethods(array('doBadResult'))
->setConstructorArgs(array('doSomething', array('input_var' => 0)))
->getMock();
$ajc->expects($this->once())
->method('doBadResult')
->willReturn(null)
->with($this->contains('ERROR - unable to do something'));
$ajc->doSomething();
}
and the problem is that I'm getting the following error from PHPUnit
MyClass::doBadResult('Did you divide by zero?') was not expected to be called more than once.
I'm not sure why, but it looks like the catch statement is being executed only when in this mock scenario.
Could anyone help me figure out why this is happening and especially how to set this up so that my test doesn't fail?
After some debugging I found the Exception was Invalid argument supplied for foreach() and after some digging, it was in the $this->contains('ERROR...')
the answer is $this->contains should be $this->stringContains
To prevent your need to do more digging in the future. One of the problems that you have in your code is that you are catching the Base Exception type.
} catch (Exception $e) {
$this->doBadResult('Did you divide by zero?');
}
When an assertion fails in PHPUnit, a PHPUnit_Framework_AssertionFailedError is thrown and is caught by PHPUnit. This error extends the Base PHP Exception class. So inside your try block when the mock object gets called, it checks the parameters that were used. Since these are not correct, it throws the PHPUnit_Framework_AssertionFailedError which your catch statement grabs. Which then calls the method on your mock that wasn't expected to be called.
Because of this, it would be possible for your test to end up passing incorrectly. Because the failed assertion gets caught and handled by your code. You should have a specific exception that your catch is looking for and any other exceptions get passed on to the appropriate level.
Related
I want to get an errors object from a callback function. here is what i have done so far.
$errors = null;
set_exception_handler(function($exception) use (&$errors) {
$errors = $exception;
var_dump($exception); // works fine
});
var_dump($errors); // give null ????
throw new Exception("blablblablablabla", 0);
I am sure with an anonymous function and a variable would work very fine !
But in this case with a callback and object I am not sure what's going on ? i can't get my head a round. any help explanation would be really appreciated.
set_exception_handler does not stop execution. It just defines what will happen if/when an exception occurs, so you can var_dump($errors); errors after setting it. $errors will still be null at that point because you have set it to null, and nothing has happened yet to change that.
The thing that does stop execution is when an exception actually occurs and the anonymous function defined in set_exception_handler is called. At that point, the var_dump($exception); within that function is called, but any other code in the main program after the point where the exception occurred will not be executed.
Storing the exception in a global variable from within the exception handler doesn't make really sense, because if the exception handler is called, no more code in the main program will be executed, so you won't be able to use that variable anyway.
You are assigning the anonymous function as an exception handler with set_exception_handler().
Yet you do not throw an exception in your code. Therefore the anonymous function is never called, because there was no event to handle, which means that $error was never set.
If you add a try catch block your function will also not be called. This can be seen by the fact, that the line var_dump($exception) from within your handler is not called either.
If you want to store the last called exception in your code, you may need to use a wrapper function around that code, that uses try catch to overwrite a static resource with the last exception encountered.
Note, that the exception within the given function is never fully thrown, because it was catched by the wrapper!
class Catcher {
private static $_EXCEPTION = null;
public static function GET_EXCEPTION () {
return self::$_EXCEPTION;
}
public static function run($func) {
try {
return $func();
} catch (Exception $e) {
self::$_EXCEPTION = $e;
}
}
}
var_dump(Catcher::GET_EXCEPTION()); //dumps NULL
Catcher::run(function(){
throw new Exception('I was called');
});
var_dump(Catcher::GET_EXCEPTION()); //dumps the last thrown exception
This is more just for documentation, since I've already solved the issue, but it was subtle and difficult enough to debug that I thought it would be useful in the public sphere.
The issue was that I had a try/catch block in an object method that just wasn't working. The reduced example is in two files, which look like this:
TestClass.php:
<?php
//TestClass.php
namespace MyTest;
class TestClass {
public function __construct() {
\e("Initializing object");
try {
\e("Trying object exception");
\throwTestException("Failing gracefully in object");
\e("After exception");
} catch (Exception $e) {
\e($e->getMessage());
}
\e("After object init exception");
}
}
?>
Main.php:
<?php
//Main.php
function e($str) { echo "\n$str"; }
function throwTestException($msg) {
throw new RuntimeException($msg);
}
require "TestClass.php";
e("Beginning");
try {
e("First try");
throwTestException("Failing gracefully first");
e("After exception");
} catch (Exception $e) {
e($e->getMessage());
}
e("Ending");
e('');
e('Beginning object test');
new \MyTest\TestClass();
e('Ending object test');
?>
The expected result on loading Main.php was this:
Beginning
First try
Failing gracefully first
Ending
Beginning object test
Initializing object
Trying object exception
Failing gracefully in object
After object init exception
Ending object test
What I actually got was something like this:
Beginning
First try
Failing gracefully first
Ending
Beginning object test
Initializing object
Trying object exception
Fatal Error: Uncaught Exception: Failing gracefully in object......
As you can see, the exception was not being caught. I tried all sorts of things and just couldn't figure out why it wasn't being caught. And then.... (See answer below)
I realized it was a namespace issue. Because I had declared TestClass within the namespace MyTest, and throwTestException in the global namespace, my reference to Exception within the class method was tacitly resolving to \MyTest\Exception and thus NOT matching the actual exception being thrown, \RuntimeException. And since I wasn't actually trying to instantiate the exception from within the namespace, no "Unknown Class" errors emerged to reveal what was happening.
The solution, then, was simply to properly resolve the exception class I was trying to catch:
catch(\Exception $e) { .... }
To be fair, this became obvious as I built my highly reduced example. It wasn't obvious initially because the exception I was expecting to catch was being generated by the class's superclass (which was the SQLite3 class). Thus, I didn't have to worry about the namespace when generating the exception, and all I was thinking about when catching it was to use the most general form of exception, Exception. And again, since I wasn't instantiating that exception -- only matching against it in a catch block --, I didn't get any notices that it was unresolved.
I was getting an error about an exception thrown in a __toString() method in a class of mine, even though there was a try...catch() in there. I did some tracking down and it turns out an object within my instance was throwing an exception in its __toString(), which meant that it wasn't being caught by the containing class' catch!
I wrote some test code as a demonstration:
class test {
public function __toString() {
try {
$b = new b();
echo (string)$b;
} catch (exception $e) {
return (string)$e;
}
}
}
class b {
public function __toString() {
throw new Exception ("test");
}
}
$test = new test();
echo $test;
I had thought that exceptions always "bubbled up" through the code until they were caught or made it all the way out.
Is there any workaround for this? The instance within my class is from a library; I don't know if I can maintainably modify it to have a catch in its own __toString().
Per PHP's __toString() section in the magic methods docs:
Warning
You cannot throw an exception from within a __toString() method. Doing
so will result in a fatal error.
When executing the code snippet you gave you get a message that says the same thing:
PHP Fatal error: Method b::__toString() must not throw an exception
There's a feature request here to support this, which states that in current state of things it would be hard to implement.
Having said all that, the proper way to fix this is to submit a patch to your upstream library to not throw exceptions from that method.
If that's not possible, the workaround for you is instead of:
echo (string)$b;
write
echo $b->__toString();
In which case you can catch the exception as you expect.
I have a code like this one:
public function one()
{
try {
$this->two();
} catch (Exception $E) {
$this->three();
}
}
How can i test that $this->three() function is called?
I've tried to "mock by code" $this->two() and throw error instead of it's original code, but that ends up with error caught by phpunit itself.
Tried setExpectedException, but it also doesn't solve the problem - catch runs inside phpunit again and just ignored.
Function $this->three() never called in both cases.
Thanks!
The problem was that the described method were in a class which was placed in a namespace containing it's own Exception implementation. So I was catching an \Namespace\Exception while throwing an \Exception.
Throwing correct exception did the thing.
#sectus, thank you!
I'm trying to test a try/catch block using a stub that throws an exception when a certain method create is called. It works fine, the exception is raised, but instead of my application catching it, it stops the execution of the test. What is some better ways to go about doing this.
<?php
// TestCase
$mockDao->expects($this->once())
->method('create')
->will($this->throwException(new \Exception));
$service->addEntity($data);
?>
<?php
// Service
public function addEntity($data)
{
....
try {
...
$this->create($entity); // Test Halts with Exception
...
} catch (Exception $e) {
// Never Gets Called
$this->handleException($e);
}
}
You are throwing \Exception but catching Exception. Is the class that implements addEntity() in a namespace? Does changing it to catch \Exception fix the problem? If not, try changing the test to throw Exception.