I ran into some issues with unit testing in that getting the response body etc from the curl handle was tricky. I didn't want to necessarily use a mock or abstract the curl functionality.A colleague of mine suggested that I just use the localhost as an endpoint and reflect back the request for unit testing. I thought this was a novel approach.The directories in question are /test/echo/ and /test/unit/I'm also interested in seeing if anyone had any opinions on the assertions.GitHubThe echo endpoint:    <?phpheader('Content-Type: application/json');$data = array('headers' => getallheaders(),// 'server' => $_SERVER,'request_method' => $_SERVER['REQUEST_METHOD'],'get' => $_GET,'post' => $_POST,'put' => $_POST,);//If the request is a put then get the file contents and try to parse the string into an arrayif($data['request_method'] == 'PUT'){parse_str(file_get_contents(php://input), $put_data);$data['put'] = $put_data;}echo json_encode($data);An example of one of the unit tests:<?phpclass TransactionTest extends PHPUnit_Framework_TestCase{static $endpoint = 'http://localhost/payjunctionphp/test/echo';public function setUp(){$options = array('username' => 'pj-ql-01','password' => 'pj-ql-01p','appkey' => '2489d40d-a74f-474f-9e8e-7b39507f3101');parent::setUp();$this->client = new TransactionClient($options);$this->client->setEndpoint(self::$endpoint);}private function getRequestPath($client = null){if(!isset($client)) $client = $this->client;return str_replace($client->baseUrl,'',curl_getinfo($client->curl)['url']);}/*** Ensure that the correct verb and path are used for the create method*/public function testCreate(){$data = array('achRoutingNumber' => '987654321','achAccountNumber' => '123456789','achAccountType' => 'CHECKING','foo' => 'bar');$transaction = $this->client->create($data);$this->assertEquals($data, get_object_vars($transaction->post),'Passed variables are not correct');$this->assertEquals('POST', $transaction->request_method,'The PHP Verb Is Incorrect');$this->assertEquals('/transactions', $this->getRequestPath(), 'The path is incorrect');}/*** Ensure that the correct verb and path are used for the read method*/public function testRead(){$transaction = $this->client->read(543);$this->assertEquals('GET', $transaction->request_method,'The PHP Verb Is Incorrect');$this->assertEquals('/transactions/543', $this->getRequestPath(), 'The path is incorrect');}/*** Ensure that the correct verb and path are used for the read method*/public function testUpdate(){$data = array('foo' => 'baz');$transaction = $this->client->Update(654,$data);$this->assertEquals($data, get_object_vars($transaction->put),'Passed variables are not correct');$this->assertEquals('PUT', $transaction->request_method,'The PHP Verb Is Incorrect');$this->assertEquals('/transactions/654', $this->getRequestPath(), 'The path is incorrect');}/*** Ensure that the correct verb and path are used for the read method*/public function testAddSignature(){$data = array('foo' => 'baa');$transaction = $this->client->addSignature(655,$data);$this->assertEquals($data, get_object_vars($transaction->post),'Passed variables are not correct');$this->assertEquals('POST', $transaction->request_method,'The PHP Verb Is Incorrect');$this->assertEquals('/transactions/655/signature/capture', $this->getRequestPath(), 'The path is incorrect');}}The base model from which the various clients extend from:    <?phpclass PayjunctionClient{public $liveEndpoint = 'https://api.payjunction.com';public $testEndpoint = 'https://api.payjunctionlabs.com';public $packageVersion = '0.0.1';public $userAgent;public function __construct(){$this->userAgent = 'PayJunctionPHPClient/' . $this->packageVersion . '(BrandedCreate; PHP/)'; //@todo add process.version$this->baseUrl = $this->testEndpoint;}public function setEndpoint($endpoint){$this->baseUrl = $endpoint;}/*** @description initializes the curl handle with default configuration and settings* @param null $handle* @return $this*/public function initCurl($handle = null){$this->curl = curl_init();curl_setopt($this->curl, CURLOPT_SSL_VERIFYPEER, false); //Don't worry about validating ssl @todo talk about security concernscurl_setopt($this->curl, CURLOPT_RETURNTRANSFER, true);//if we have a password and username then set it by default to be passed for authenticationif (isset($this->defaults['password']) && isset($this->defaults['username'])) {curl_setopt($this->curl, CURLOPT_HTTPAUTH, CURLAUTH_ANY);curl_setopt($this->curl, CURLOPT_USERPWD, $this->defaults['username'] . : . $this->defaults['password']);}//if we have default headers to pass then pass themif (isset($this->defaults['headers']) && is_array($this->defaults['headers'])) {$headers = array();foreach ($this->defaults['headers'] as $key => $value) {array_push($headers, $key . ': ' . $value);}curl_setopt($this->curl, CURLOPT_HTTPHEADER, $headers);}return $this;}/*** @description generates a new client* @param null $options* @return $this*/public function generateClient($options = null){$this->baseUrl = isset($options['endpoint']) ? $options['endpoint'] : $this->baseUrl;$this->defaults['username'] = isset($options['username']) ? $options['username'] : '';$this->defaults['password'] = isset($options['password']) ? $options['password'] : '';$this->defaults['headers']['X-PJ-Application-Key'] = isset($options['appkey']) ? $options['appkey'] : '';$this->defaults['headers']['User-Agent'] = $this->userAgent;$this->initCurl();return $this;}/*** @description takes the response from our curl request and turns it into an object if necessary* @param $response* @param null $contentType* @return array|mixed*/public function processResponse($response){$contentType = curl_getinfo($this->curl, CURLINFO_CONTENT_TYPE);if ($contentType == 'text/html' || is_null($contentType) || !isset($contentType) || $contentType = '' || $contentType == FALSE) {return $response;}try {$object = json_decode($response);return $object;} catch (Exception $e) {return array('errors' => array(0 => 'Invalid Response Type, Error In Processing Response From Payjunction'));}}/*** @description processes a curl post request* @param $path* @param null $params* @return array|mixed*/public function post($path, $params = null){curl_setopt($this->curl, CURLOPT_POST, TRUE);curl_setopt($this->curl, CURLOPT_URL, $this->baseUrl . $path);if (is_object($params) || is_array($params)) {curl_setopt($this->curl, CURLOPT_POSTFIELDS, http_build_query($params));}return $this->processResponse(curl_exec($this->curl));}/*** @description processes a curl get request* @param $path* @param null $params* @return array|mixed*/public function get($path, $params = null){//create the query string if there are any parameters that need to be passed$query_string = ;if (!is_null($params)) {$query_string = ? . http_build_query($params,'','&');}curl_setopt($this->curl, CURLOPT_HTTPGET, TRUE);curl_setopt($this->curl, CURLOPT_URL, $this->baseUrl . $path . $query_string);return $this->processResponse(curl_exec($this->curl));}/*** @description processes a curl put request* @param $path* @param null $params* @return array|mixed*/public function put($path, $params = null){curl_setopt($this->curl, CURLOPT_CUSTOMREQUEST, PUT);if (is_object($params) || is_array($params)) {curl_setopt($this->curl, CURLOPT_POSTFIELDS, http_build_query($params));}curl_setopt($this->curl, CURLOPT_URL, $this->baseUrl . $path);return $this->processResponse(curl_exec($this->curl));}/*** @description processes a curl delete request* @param $path* @param null $params* @return array|mixed*/public function del($path, $params = null){curl_setopt($this->curl, CURLOPT_CUSTOMREQUEST, DELETE);if (is_object($params) || is_array($params)) {curl_setopt($this->curl, CURLOPT_POSTFIELDS, http_build_query($params));}curl_setopt($this->curl, CURLOPT_URL, $this->baseUrl . $path);return $this->processResponse(curl_exec($this->curl));}}The TransactionClient related specifically to this unit test:<?phpclass TransactionClient extends PayjunctionClient{public function __construct($options){parent::__construct();$this->generateClient($options);}/*** @description create a new transaction* @param $params* @return array|mixed*/public function create($params){return $this->post('/transactions',$params);}/*** @description read from an existing transaction* @param $id* @return array|mixed*/public function read($id){return $this->get('/transactions/'.$id);}/*** @description update an existing transaction* @param $id* @param null $params* @return array|mixed*/public function update($id, $params = null){return $this->put('/transactions/'.$id, $params);}/*** @todo this does not appear to be working 405 Method Not Allowed* @description add a signature to an existing transaction* @param $id* @param $params* @return array|mixed*/public function addSignature($id, $params){return $this->post('/transactions/'.$id.'/signature/capture',$params);}}
Unit Testing of Curl based Rest Client Library
php;unit testing;phpunit