Exception not triggered with try/catch/finally and stub method calls - php

I am facing a PHPUnit issue in a Symfony2 context. I am testing the following method :
public function updatePassword(LdapUserInterface $user)
{
$data = array();
Attribute::setPassword($data, $user->getPassword(), Attribute::PASSWORD_UNICODEPWD);
try {
$this->ldap->bind();
$this->ldap->update($user->getDn(), $data);
return true;
} catch (\Exception $e) {
$this->logger->error($e->getMessage());
throw new ConnectorException($this, 'Connector cannot connect to directory.');
} finally {
$this->ldap->disconnect();
}
return false;
}
I used the finally instruction PHP >5.5. My unitary test is ($this->logger references a stub defined in the setUp method):
/**
* #expectedException UserBundle\Connectors\Exceptions\ConnectorException
*/
public function testUpdatePasswordException()
{
$ldap = $this->getMockBuilder(Ldap::class)
->disableOriginalConstructor()
->setMethods(array('bind', 'disconnect', 'update'))
->getMock();
$ldap->method('update')->will($this->throwException(new LdapException($ldap, 'Fake Exception')));
$user = $this->getMockBuilder(User::class)
->setMethods(array('getDn', 'getPassword'))
->getMock();
$user->expects($this->once())->method('getPassword')->willReturn('#!12345mD');
$user->expects($this->once())->method('getDn')->willReturn('cn=John Doe,ou=people,dc=athome.com,dc=com');
$connector = new LdapConnector($this->logger, $ldap);
$connector->updatePassword($user);
}
The UT did not trigger any exception and failed. Apparently, the problem comes from the finally instruction. Normally, the disconnect method is called once and that's why I added it into the stub. But, when I removed it, the test passes.
While debugging, all instructions are called (try, catch, finally and then trigger of exception). I don't understand the behaviour of these, is it a php problem ? Seems not, so I wonder if there is not a problem with my mock or phpunit.
Any idea ?

Related

Fatal Error on using $exception variable in render() Method in App\Exceptions\Handler.php

I am new to Laravel and have an Issue regarding the Handler.php File.
I am trying to create a class that takes an exceptions and transforms it into a JSON Response.
Sadly though, upon calling the constructor a series of Errors are thrown:
(ErrorErrorErrorErrorErrorErrorErrorErrorErrorErrorErrorSymfony\Component\ErrorHandler\Error\FatalError)
My code:
render() in Handler.php:
public function render($request, Throwable $exception)
{
$errorResource = new ErrorResource($exception);
return $errorResource->getJsonResponse();
}
class ErrorResource in ErrorResource.php:
<?php
namespace Transformers;
use Throwable;
class ErrorResource
{
private $exception;
private $defaultCodes = [TypeError::class => 400];
private $defaultMessages = [TypeError::class => 'Untgültige URL Parameter'];
function __construct(Throwable $exception)
{
$this->exception = $exception;
}
public function getJsonResponse($exception)
{
$codeToThrow = 500;
$messageToThrow = "Internal Server Error";
$type = get_class($this->exception);
if (empty($exception->getCode())) {
$codeToThrow = $this->defaultCodes[$type];
} else {
$codeToThrow = $exception->getCode();
}
if (empty($exception->getMessage())) {
$messageToThrow = $this->defaultMessages[$type];
} else {
$messageToThrow = $exception->getMessage();
}
return response()->json(array(
'Type' => $type,
'Message' => $messageToThrow
), $codeToThrow);
}
}
I have also tried to move the method getJsonResponse() to the Handler.php file and call it from there, but without any luck.
I am really confused as to why I am not allowed to do certain things with the $exception variable (I have also tried to create a clone of this object - but the same error occures)
I hope you can help me resolving this issue,
Greetins,
Franz
The issue is, that PHP is call by value. That is why it is implicitely trying to clone an unclonable object -> Error. To resolve this issue one can use wrapper objects, but I decided to simply use call by reference (https://www.javatpoint.com/php-call-by-reference)

Try catch private method phpunit symfony

I have the following code:
public function addSomething($paramDto) {
try {
$this->privateMethod($param);
} catch(\Exception $e) {
return ['error' => true, 'messages' => [$e->getMessage()]];
}
return ['error' => false, 'messages' => 'success'];
}
private function privateMethod($param) {
if(!$param) {
throw new \Exception('errorMessage');
}
}
I'm trying to test the addSomething method, what the catch block returns, I don't want to test the private method.
public function testAddSomethingThrowError($paramDto) {
$param = \Mockery::mock('MyEntity');
$method = new \ReflectionMethod(
'MyService', 'privateMethod'
);
$method->setAccessible(TRUE);
$this->expectException(\Exception::class);
$this->getMyService()
->shouldReceive($method->invoke($param)
->withAnyArgs()
->andThrow(\Exception::class);
$this->getMyService()->addSomething($paramDto);
}
The thing is that if i run the test, it coverages the private method in the if statement and returns the exception, but the catch method in my addSomething method is not covered, actually it does not cover the addSomething method at all.
I am using the sebastian bergmann phpunit framework.
What am I doing wrong?
The correct answer should be Jakub Matczak's answer:
"You want to "assert if the public method is returning the message that it is indeed returning". There's no sense in doing that. Consider your tested class as a blackbox without possibility to check its source. Then make tests according to how to want it to work using its public interface. "

Phpunit, testing a method that calls a method from repository with arguments

I want to test a public method which calls a method from different class with methods as the arguments.
public function __construct(LeadData $leadData,LeadRepository $leadRepository){....}
public function proceedtosave($leads,$payout,$received)
{
try {
//save the lead
$this->savedLead = $this->leadRepository->leadCreate($leads,$payout,$received);
$this->responseData['lead_id'] = $this->savedLead->id;
} catch (QueryException $exception) {
$errorCode = $exception->errorInfo[1];
Log::info(['Lead insert error code: '.$errorCode,'Error message: '.$exception->getMessage()]);
if ($this->validator->errorCodeChecker($errorCode) === false) {
$this->leadDuplicateRepository->leadDuplicateCreate($this->leads, $payout, $received);
return $this->validator->getErrors();
}
}
}
This is how I wrote the test
/**
* #test
*
*/
public function save_leads_to_leads_table_if_not_duplicate()
{
$this->getLeadsForValidator();
$leadData = $this->getMock('App\Helpers\..\..Data');
$leadIn = $this->getMockbuilder('App\Helpers\Repositories\..Interface')
->setMethods(array('leadCreate'))
->getMock();
$leadIn->expects($this->once())
->method('leadCreate')
->will($this->returnValue(1));
$leadIn->leadCreate($this->results,1,2);
$this->SUT = new LeadStore($leadData,$leadIn);
$this->SUT->proceedtosave($this->results,1,2);
}
I updated my original question here since I realized that I have to refactor my code. How would you pass this test?I got the error which I am trying to solve
1) StoreLeadsTest::save_leads_to_leads_table_if_not_duplicate
.....::leadCreate(Array (...), 1, 2) was not expected to be called more than once.
/home/vagrant/Code/l.../../LeadStore.php:87
/home/vagrant/Code/./../StoreLeadsTest.php:46
And the lines of code
$this->savedLead = $this->leadRepository->leadCreate($leads,$payout,$received);//LeadStore.php:87
$this->SUT->proceedtosave($this->results,1,2);//StoreLeadsTest.php:46
Swapping Mockery expectations from
->expects($this->once())
To
->expects($this->any())
Will result ErrorException
ErrorException: Trying to get property of non-object
That point to this line of code
$this->responseData['lead_id'] = $this->savedLead->id;
If I remove that line above, my test pass, how to make phpunit skip that line so that I can continue to test the other lines?

How to reach the exception block

So I am messing around with symfony router component and I created a small wrapper.
One thing that came up was how do I get a request to throw a 500 in unit tests? The method in question is:
public function processRoutes(Request $request) {
try {
$request->attributes->add($this->_matcher->match($request->getPathInfo()));
return call_user_func_array($request->attributes->get('callback'), array($request));
} catch (ResourceNotFoundException $e) {
return new RedirectResponse('/404', 302);
} catch (Exception $e) {
return new RedirectResponse('/500', 302);
}
}
And the test in question is:
public function testFiveHundred() {
$router = new Router();
$router->get('/foo/{bar}', 'foo', function($request){
return 'hello ' . $request->attributes->get('bar');
});
$response = $router->processRoutes(Request::create('/foo/bar', 'GET'));
$this->assertEquals(500, $response->getStatusCode());
}
Right now the test will fail because we are defined and the status code will be 200. Is there something special I can do to the Request object I create, to make it throw a 500?
I think you got several options here you can play with:
Decide that a specific path will always throw an exception.
This will force you to make some changes in your code.
public function processRoutes(Request $request) {
...
if ($request->getRequestUri() == '/path/that/throws/exception') {
throw Exception('Forced to throw exception by URL');
}
...
}
public function testFiveHundred() {
...
$response = $router->processRoutes(Request::create('/path/that/throws/exception', 'GET'));
...
}
Make a DummyRequest object that will extends your original Request class and make sure this object will raise an Exception (for example - you know for sure that you use the getPathInfo(), so you can use this).
class DummyRequest extends Request {
public function getPathInfo() {
throw new Exception('This dummy request object should only throw an exception so we can test our routes for problems');
}
}
public function testFiveHundred() {
...
$dummyRequest = new DummyRequest();
$response = $router->processRoutes($dummyRequest);
...
}
Since the function getRequestUri of our $dummyRequest throws an exception, your call to $router->processRoutes will have our dummy to throw that exception.
This is a general idea, you would probably need to play a bit with the namespaces and the functions there (I didn't test it, however this should work).

testing exceptions with phpunit

i am trying to test a function when i know error i going to be thrown. the function looks like this:
function testSetAdsData_dataIsNull(){
$dataArr = null;
$fixture = new AdGroup();
try{
$fixture->setAdsData($dataArr);
} catch (Exception $e){
$this->assertEquals($e->getCode(), 2);
}
$this->assertEmpty($fixture->ads);
$this->assertEmpty($fixture->adIds);
}
Now i am trying to use the phpunit exceptions assertions methods to replace the try catch part but i can't figure out how to do that.
i did lots of reading including this post PHPUnit assert that an exception was thrown? but i couldnt really understand how it shuold be implemented.
i tried something like this:
/**
* #expectedException dataIsNull
*/
function testSetAdsData_dataIsNull(){
$dataArr = null;
$fixture = new AdGroup();
$this->setExpectedException('dataIsNull');
$fixture->setAdsData($dataArr);
$this->assertEmpty($fixture->ads);
$this->assertEmpty($fixture->adIds);
}
but obviously it didn't work and i got this error:
1) adGroupTest::testSetAdsData_dataIsNull
ReflectionException: Class dataIsNull does not exist
what am i doing wrong and how exactly can i assert if exception was thrown plz?
I generally use the #expectedException annotations for just such cases. See all exception-related annotations here:
/**
* #expectedException \Exception
* #expectedExceptionCode 2
*/
function testSetAdsData_dataIsNull()
{
$dataArr = null;
$fixture = new AdGroup();
$fixture->setAdsData($dataArr);
}
Checking that $fixture->ads is really null doesn't really add up here, you can add these asserts prior to the call that actually triggers an exception:
$this->assertNull($fixture->ads);
$fixture->setAdsData($dataArr);//throws exception
You're unit testing. This test serves a clear purpose: it makes sure an exception is thrown in a given situation. If it does, then that's where the test ends.
Still, if you want to keep those assertEmpty calls, you could do this:
try {
$fixture->setAdsData($dataArr);
$e = null;
} cathc (Exception $e) {}
$this->assertEmpty($fixture->ads);
$this->assertEmpty($fixture->adIds);
if (!$e instanceof \Exception) {
//if the exception is not thát important:
$this->markTestIncomplete('No Exception thrown');
//do other stuff here... possibly
$this->fail('The exception was not thrown');
}
throw $e;//throw exception a bit later
An alternative approach would be to call $this->setExpectedException manually as explained here. Since we don't seem to know/care what the exception message will look like, I'm going to use the setExpectedExceptionRegExp method:
$fixture = new AdGroup();
$this->setExpectedExceptionRegExp(
//exception class, message regex, exception code
'Exception', '/.*/'. 2
);
$fixture->setAdsData(null);//passing null seems to be what you're doing anyway

Categories