I'm trying to 100% code coverage my service. Here is a method:
<?php
* Search to public accounts.
*
* #param string $query
*
* #return TwitterResponse
*/
public function search(string $query): TwitterResponse
{
try {
$response = $this->client->getClient()->get(UserEnum::URI_SEARCH, [
'query' => ['q' => $query,]
]);
} catch (ClientException $e) {
$response = $e->getResponse();
}
return new TwitterResponse($response);
}
It simply GET a user with Twitter API.
In my opinion, I should develop two tests : one for the try and one for the catch. Bellow is my test for the try.
<?php
/**
* #return void
*/
public function setUp(): void
{
$this->prophet = new Prophet();
$this->client = $this->prophet->prophesize(Client::class);
$this->client->get(Argument::any(), Argument::any())->willReturn(new TwitterResponse(new Response()));
$this->client->post(Argument::any(), Argument::any())->willReturn(new TwitterResponse(new Response()));
$this->twitterClient = $this->prophet->prophesize(TwitterClient::class);
$this->twitterClient->getClient()->willReturn($this->client);
$this->userService = new UserService($this->twitterClient->reveal());
}
/**
* Tests if a TwitterResponse is returned with status HTTP_OK.
*
* #return void
*/
public function testGetOk(): void
{
$actual = $this->userService->get('');
$this->assertEquals(get_class($actual), TwitterResponse::class);
$this->assertEquals(HttpResponse::HTTP_OK, $actual->getStatusCode());
}
Bellow the code coverage of get().
As you can see, I don't test the catch case. How can I do it ? I already tried to mock a 404 HTTP response catch something but it did not work. Do you have an idea of how I can do it ?
Thanks.
EDIT : I tried this for the catch case ->
public function testGetKo(): void
{
$response = new TwitterResponse(new Response(HttpResponse::HTTP_NOT_FOUND));
$response->setStatusCode(HttpResponse::HTTP_NOT_FOUND);
$this->client = $this->prophet->prophesize(Client::class);
$this->client->get(Argument::any(), Argument::any())->willReturn($response);
$this->twitterClient = $this->prophet->prophesize(TwitterClient::class);
$actual = $this->userService->get('');
$this->assertEquals(get_class($actual), TwitterResponse::class);
$this->assertEquals(HttpResponse::HTTP_NOT_FOUND, $actual->getStatusCode());
}
Phpunit returns : Failed asserting that 200 matches expected 404. It seems that my mock client doesn't work well.
I know, it's an old post, but..
Maybe try to mock client, and when it's triggered throw exception?
So when you throw ClientException, you should check for TwitterResponse result. And when you throw DummyException, you should expect DummyException.
This is untested, as I don't usually use prophecy, but I do simialry with other mocking frameworks:
public function testGetKo(): void
{
// ... other setup
$exception = new ClientException();
$this->client = $this->prophet->prophesize(Client::class);
$this->client->get(Argument::any(), Argument::any())->willThrow($exception);
And you will likely add $this->expectException(ClientException::class); before you run the function being tested.
Related
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. "
I'm following a tutorial on how to use the Google Reseller API. I've come to the section on determining whether a customer already exists in Google Apps (Step 2) but have come unstuck in handling the Google_Service_Exception object.
If a customer doesn't exist then a call to the API will return a 404 error. I'm using the code property of the Google_Service_Exception object $e to determine if the response has a 404 error. However when I try to return the error code with $e->code with:
try {
// Call to the Google Reseller API
} catch (Google_Service_Exception $e) {
if($e->code == 404){
return false;
}
}
I get the following PHP error:
Fatal error: Cannot access protected property Google_Service_Exception::$code.
The Google_Service_Exception class is as follows:
<?php
require_once 'Google/Exception.php';
class Google_Service_Exception extends Google_Exception
{
/**
* Optional list of errors returned in a JSON body of an HTTP error response.
*/
protected $errors = array();
/**
* Override default constructor to add ability to set $errors.
*
* #param string $message
* #param int $code
* #param Exception|null $previous
* #param [{string, string}] errors List of errors returned in an HTTP
* response. Defaults to [].
*/
public function __construct(
$message,
$code = 0,
Exception $previous = null,
$errors = array()
) {
if (version_compare(PHP_VERSION, '5.3.0') >= 0) {
parent::__construct($message, $code, $previous);
} else {
parent::__construct($message, $code);
}
$this->errors = $errors;
}
/**
* An example of the possible errors returned.
*
* {
* "domain": "global",
* "reason": "authError",
* "message": "Invalid Credentials",
* "locationType": "header",
* "location": "Authorization",
* }
*
* #return [{string, string}] List of errors return in an HTTP response or [].
*/
public function getErrors()
{
return $this->errors;
}
}
So I assumed the error has something to do with the fact that $errors is protected. I imagine it is protected for a reason so I was a bit wary of changing the class. Any help / pointers in working around this error would be greatly appreciated. Thanks
Just use the getCode() method:
try {
// Call to the Google Reseller API
} catch (Google_Service_Exception $e) {
if($e->getCode() == 404){ // <- Change is here
return false;
}
}
Google_Service_Exception extends Google_Exception and Google_Exception extends Exception. You may read the documentation about Exception here. You will see the getCode method.
I getting this is exception in my application, and don't understand why. This exception throw in for one collection, over collection not throw this exception, with processed insert(),update(), or delete()
/**
* insert to mongo db method
* #param $dataArray
* #param $collection
*/
static function insert($dataArray, $collection) {
$connect = Core_Model_Mongo::getConnect();
$write = new MongoDB\Driver\BulkWrite();
$writeConcern = new MongoDB\Driver\WriteConcern(MongoDB\Driver\WriteConcern::MAJORITY);
$write->insert($dataArray);
$connect->executeBulkWrite(
Config_Db::getConf()['mongodb']['db'].'.'.$collection,
$write,
$writeConcern
);
}
end get connect method
/**
* #return \MongoDB\Driver\Manager
*/
static function getConnect() {
if(!is_null(self::$_connect)) {
return self::$_connect;
}
self::$_connect = new \MongoDB\Driver\Manager(Config_Db::getConf()['mongodb']['connect']);
return self::$_connect;
}
data save in collection success full, and this exception not take insert(),update() and other methods. I temporary resolve this question by try catch block
try {
// my code
} catch(MongoDB\Driver\Exception\BulkWriteException $error) {
Core_App::log(var_export($error,true));
}
but this is resolve not correct because I need application without any exception,error,notice, and over log level error.
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 ?
Can anyone help me with my problem?
I'm trying to get data from my web service and sometimes server responses with invalid XML.
I've noticed that it depends on data size. When response reach a certain size it ends with </SOAP-ENV:Envelop instead of </SOAP-ENV:Envelope>.
Here is my example code: client and server in one controller.
Method "success" works fine, but similiar method "fail" does not.
Even more: for nginx and php-fpm "success" method fails too (for apache with php_mod "success" method works).
<?php
class SoapController extends CController
{
public function actions()
{
return array('wsdl' => array('class' => 'CWebServiceAction'));
}
private function getData($size)
{
return array_fill(0, $size, '18ad96e6-5526-11e0-9c19-00248c654095');
}
/**
* #return array
* #soap
*/
public function success()
{
return $this->getData(104);
}
/**
* #return array
* #soap
*/
public function fail()
{
return $this->getData(105);
}
public function actionTest()
{
$client = new SoapClient($this->createAbsoluteUrl('wsdl'), array('trace' => true));
$methods = array('success', 'fail');
foreach ($methods as $method) {
try {
$result = call_user_func(array($client, $method));
$result = count($result);
} catch (SoapFault $ex) {
$result = $ex->getMessage();
}
echo $method . ' result: ' . $result . '<br>';
echo $method . ' response: <br>' . htmlspecialchars($client->__getLastResponse()) . '<br><br>';
}
}
}
Software versions:
Ubuntu Server 12.10
Yii 1.1.13
PHP 5.4.6
Apache 2.2.22
Nginx 1.2.1
Problem was in controller file.
It has UTF-8 charset with BOM. After removing BOM it works fine.