I have this exception:
throw new \DomainException(__("message d'erreur"));
I don't know how can I do the test of this instruction by PHPUnit: __().
Im sure there are other ways which i'm not aware of but I would do it like the following, if you downvote please comment why. If I learn something new I'm happy.
Ok, depending on whether the application expects the function to be defined to work or whether its expected but not part of the current test you could mock it:
Using: php-mock/php-mock-phpunit
<?php
use PHPUnit\Framework\TestCase;
class SomeTest extends TestCase
{
use \phpmock\phpunit\PHPMock;
/**
* __() function must exist or test fails.
*/
public function testDoubleUndescoreFunction()
{
$this->assertTrue(function_exists('__'), '__() function does not exist');
}
// or
/**
* __() function will be mocked.
*/
public function testDoubleUndescoreFunction()
{
// mock __() function
if (!function_exists('__')) {
$mocks['__'] = $this->getFunctionMock(__NAMESPACE__, "__");
$mocks['__']->expects($this->any())->willReturnCallback(
function ($value) {
$this->assertInternalType('string', $value);
return $value;
}
);
}
// test
$this->assertTrue(function_exists('__'), '__() function does not exist');
try {
//
// run test which will cause the exception
} catch (\Exception $e) {
$this->assertInstanceOf('DomainException', $e);
$this->assertEquals('message d\'erreur', $e->getMessage());
}
}
}
?>
Related
I am trying to test the below class using PHPUnit
class stripe extends paymentValidator {
public $apiKey;
public function __construct ($apiKey){
$this->apiKey = $apiKey;
}
public function charge($token) {
try {
return $this->requestStripe($token);
} catch(\Stripe\Error\Card $e) {
echo $e->getMessage();
return false;
}
}
public function requestStripe($token) {
// do something
}
}
My test scripts is like the below:
class paymentvalidatorTest extends PHPUnit_Framework_TestCase
{
/**
* #test
*/
public function test_stripe() {
// Create a stub for the SomeClass class.
$stripe = $this->getMockBuilder(stripe::class)
->disableOriginalConstructor()
->setMethods(['requestStripe', 'charge'])
->getMock();
$stripe->expects($this->any())
->method('requestStripe')
->will($this->returnValue('Miaw'));
$sound = $stripe->charge('token');
$this->assertEquals('Miaw', $sound);
}
}
With my test script I was expecting the test double of stripe::charge() method will do exactly as the defined in the original class and the stripe::requestStripe() will return 'Miaw'. Therefore, $stripe->charge('token') should also return 'Miaw'. However, when I run the test I get:
Failed asserting that null matches expected 'Miaw'.
How should I fix this ?
Where you're calling setMethods, you're telling PHPUnit that the mock class should mock the behaviour of those methods:
->setMethods(['requestStripe', 'charge'])
In your case it looks like you want to partially mock the class, so that requestStripe() returns Miaw, but you want charge to run its original code - you should just remove charge from the mocked methods:
$stripe = $this->getMockBuilder(stripe::class)
->disableOriginalConstructor()
->setMethods(['requestStripe'])
->getMock();
$stripe->expects($this->once())
->method('requestStripe')
->will($this->returnValue('Miaw'));
$sound = $stripe->charge('token');
$this->assertEquals('Miaw', $sound);
While you're at it you may as well specify how many times you expect requestStripe() to be called - it's an extra assertion with no extra effort, as using $this->any() doesn't provide you with any added benefit. I've included using $this->once() in the example.
I am writing a basic PDO wrapper class and when I want to simulate the throwing of an exception by PDOStatement::prepare() using willThrowException() with the mock of PDOException in my unit test, the returned value of getMessage() is always and empty string instead of what I set up.
Here is how I tried it:
// WrapperClass.php
<?php
class WrapperClass
{
private $pdo;
private $error = '';
public function __construct(\PDO $pdo)
{
$this->pdo = $pdo;
}
public function save()
{
$sql = 'INSERT INTO ...';
try {
$this->pdo->prepare($sql);
// some value binding and executing the statement
} catch (\PDOException $pdoException) {
$this->error = $pdoException->getMessage();
}
}
public function getError()
{
return $this->error;
}
}
and my test:
// WrapperClassTest.php
<?php
class WrapperClassTest extends \PHPUnit_Framework_TestCase
{
/**
* #test
*/
public function save_saves_PDOException_message_in_error_property()
{
$pdoMock = $this->getMockBuilder('WrapperClass')
->disableOriginalConstructor()
->setMethods(['prepare'])
->getMock();
$pdoMock->expects($this->once())
->method('prepare')
->willThrowException($pdoExceptionMock);
$pdoExceptionMock = $this->getMockBuilder('\PDOException')
->setMethods(['getMessage'])
->getMock();
$message = 'Message from PDOException';
$pdoExceptionMock->expects($this->once())
->method('getMessage')
->willReturn($message);
$wrapperClass = new WrapperClass($pdoMock);
$wrapperClass->save();
$this->assertEquals($message, $wrapperClass->getError());
}
}
I also tried to replace ->willThrowException($pdoException) with ->will($this->throwException($pdoException)) but it does not work.
I noticed that if I replace ->willThrowException($pdoException) with ->willThrowException(new \PDOException('Message from PDOException')) it works but then I'm relying on the PDOException class instead of mocking it.
Any ideas?
Just 2 statements:
1) All exceptions in PHP 5.x extends base Exception and it defines 'getMessage' method as final:
final public string Exception::getMessage ( void )
2) PHPUnit silently do nothing when you try to mock final methods (you can see code that generate mocks here, canMockMethod returns false for final methods)
So
->setMethods(['getMessage'])
has no effect.
On the other side you don't really need to mock exceptions because they are value objects. Passing new PDOException('Message from PDOException') is pretty ok.
Say you have a method which ultimately boils down to
class Pager
{
private $i;
public function next()
{
if ($this->i >= 3) {
throw new OutOfBoundsException();
}
$this->i++;
}
}
How would you unit test this class. I.e. test whether the exception gets thrown on the third call of next() using PHPUnit? I've added my attempt as an answer, but I'm not sure whether this really is the way to go.
What about testing for null on the first two calls and also test for the exception being thrown like follows:
class PagerTest
{
public function setUp()
{
$this->pager = new Pager();
}
public function testTooManyNextCalls()
{
$this->assertNull($this->pager->next());
$this->assertNull($this->pager->next());
$this->assertNull($this->pager->next());
$this->setExpectedException('OutOfBoundsException');
$this->pager->next();
}
}
It's very important when unit-testing to avoid testing implementation details. Instead, you want to limit yourself to testing only the public interface of your code. Why? Because implementation details change often, but your API should change very rarely. Testing implementation details means that you'll constantly have to rewrite your tests as those implementations change, and you don't want to be stuck doing this.
So what does this mean for the OP's code? Let's look at the public Pager::next method. Code that consumes the Pager class API doesn't care how Pager::next determines if an exception should be thrown. It only cares that Pager::next actually throws an exception if something is wrong.
We don't want to test how the method arrives at it's decision to throw an OutOfBoundsException -- this is an implementation detail. We only want to test that it does so when appropriate.
So to test this scenario we simulate a situation in which the Pager::next will throw. To accomplish this we simply implement what's called a "test seam." ...
<?php
class Pager
{
protected $i;
public function next()
{
if ($this->isValid()) {
$this->i++;
} else {
throw new OutOfBoundsException();
}
}
protected function isValid() {
return $this->i < 3;
}
}
In the above code, the protected Pager::isValid method is our test seam. It exposes a seam in our code (hence the name) that we can latch onto for testing purposes. Using our new test seam and PHPUnit's mocking API, testing that Pager::next throws an exception for invalid values of $i is trivial:
class PagerTest extends PHPUnit_Framework_TestCase
{
/**
* #covers Pager::next
* #expectedException OutOfBoundsException
*/
public function testNextThrowsExceptionOnInvalidIncrementValue() {
$pagerMock = $this->getMock('Pager', array('isValid'));
$pagerMock->expects($this->once())
->method('isValid')
->will($this->returnValue(false));
$pagerMock->next();
}
}
Notice how this test specifically doesn't care how the implementation method Pager::isValid determines that the current increment is invalid. The test simply mocks the method to return false when it's invoked so that we can test that our public Pager::next method throws an exception when it's supposed to do so.
The PHPUnit mocking API is fully covered in Test Doubles section of the PHPUnit manual. The API isn't the most intuitive thing in the history of the world, but with some repeated use it generally makes sense.
Here's what I have at the moment, but I was wondering if there were any better ways of doing this.
class PagerTest
{
public function setUp()
{
$this->pager = new Pager();
}
public function testTooManyNextCalls()
{
for ($i = 0; $i < 10; $i++) {
try {
$this->pager->next();
} catch(OutOfBoundsException $e) {
if ($i == 3) {
return;
} else {
$this->fail('OutOfBoundsException was thrown unexpectedly, on iteration ' . $i);
}
}
if ($i > 3) {
$this->fail('OutOfBoundsException was not thrown when expected');
}
}
}
}
You could use something like:
class PagerTest extends PHPUnit_Framework_TestCase {
/**
* #expectedException OutOfBoundsException
*/
public function testTooManyNextCalls() {
$this->pager = new Pager();
$this->pager->next();
$this->pager->next();
$this->pager->next();
$this->assertTrue(false);
}
}
If an exception is thrown in the 3rd method call, the always-failing assert-statement should never be reached and the test should pass. on the other hand if no exception gets thrown, the test will fail.
You may pass the value $this->i to the exception instantiation, which will then be the message of the exception.
class Pager
{
private $i;
public function next()
{
if ($this->i >= 3) {
throw new OutOfBoundsException($this->i);
}
$this->i++;
}
}
$a=new Pager();
$a->next();
$a->next();
$a->next();
$a->next();
//outputs: "Exception: 3"
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 have code like this:
class ToBeTested
{
function simpleMethod($param)
{
if(0 === $param)
{
trigger_error("Param is 0!", E_USER_WARNING);
return false;
}
return true;
}
}
and test for this code:
class SimpleTest extends PHPUnit_Framework_TestCase
{
function testSimpleMethod()
{
$toBeTestedObject = new ToBeTested();
$this->assertFalse($toBeTestedObject->simpleMethod(0));
}
}
I know how to test, if the error is triggered ($this->setExpectedException()), but I don't know how to execute the code after trigger_error() function.
Remember that in PHPUnit E_USER_WARNING is not converted into PHPUnit_Framework_Error_Warning (which can be disabled), but it is converted into PHPUnit_Framework_Error (which can't be disabled).
This is one of those places where you are 'officially' allowed to use the # operator :)
Make one test to check the return value, another test to check if the warning gets triggered. And by the way, I'd suggest you do test if the warning is triggered.
class SimpleTest extends PHPUnit_Framework_TestCase
{
function testSimpleMethodReturnValue()
{
$toBeTestedObject = new ToBeTested();
$this->assertFalse(#$toBeTestedObject->simpleMethod(0));
}
/**
* #expectedException PHPUnit_Framework_Error
*/
function testSimpleMethodEmitsWarning() {
$toBeTestedObject = new ToBeTested();
$toBeTestedObject->simpleMethod(0);
}
}
What you should be using is set_error_handler() (link) and restore_error_handler() which lets you set a function to deal with errors of a given type. It also has the added bonus of giving you a place to test the warning at the same time.
So, something like this:
class SimpleTest extends PHPUnit_Framework_TestCase
{
function testSimpleMethod()
{
set_error_handler(array($this, '_handleWarnedMethod'), E_USER_WARNING);
$toBeTestedObject = new ToBeTested();
$this->assertFalse($toBeTestedObject->simpleMethod(0));
restore_error_handler();
}
private function _handleWarnedMethod($errno, $errstr)
{
$this->assertEquals(E_USER_WARNING, $errno);
$this->assertEquals('Param is 0!', $errstr);
}
}
As always, error suppression isn't the best idea :)
The answer is that in PHPUnit 3.4.15 there is PHPUnit_Util_ErrorHandler class with handleError method which is executed when any error occurs. For error like E_USER_* this method always throws PHPUnit_Framework_Error, so execution of the rest of code is stopped.
The only way to prevent this is to disable user errors reporting, I think. It could be done like this:
class SimpleTest extends PHPUnit_Framework_TestCase
{
function testSimpleMethod()
{
$toBeTestedObject = new ToBeTested();
// disable user errors reporting
$oldReportingLevel = error_reporting();
error_reporting($oldReportingLevel ^ (E_USER_ERROR | E_USER_WARNING | E_USER_NOTICE));
// check the condition
$this->assertFalse($toBeTestedObject->simpleMethod(0));
// recover old error reporting level
error_reporting($oldReportingLevel);
}
}
Nearly 9 years later, and this question still comes up regularly.
You can use Netsilik/BaseTestCase (MIT License) to get extended functionality to assert errors/warnings are triggered as expected:
composer require netsilik/base-test-case
Testing for a E_WARNING:
<?php
namespace Tests;
class MyTestCase extends \Netsilik\Testing\BaseTestCase
{
/**
* {#inheritDoc}
*/
public function __construct($name = null, array $data = [], $dataName = '')
{
parent::__construct($name, $data, $dataName);
$this->_convertNoticesToExceptions = false;
$this->_convertWarningsToExceptions = false;
$this->_convertErrorsToExceptions = true;
}
public function test_whenWarningTriggered_weCanTestForIt()
{
$foo = new Foo();
$foo->bar();
self::assertErrorTriggered(E_WARNING, 'The warning string');
}
}