How do I test a GET request of a REST API with PHPUnit 4.1? I use the Slim PHP-Framework and could manage to test the response code but not the body or header.
This is what I have so far:
TestClass:
class AssetTest extends PHPUnit_Framework_TestCase
{
public function request($method, $path, $options = array())
{
// Capture STDOUT
ob_start();
// Prepare a mock environment
Environment::mock(array_merge(array(
'REQUEST_METHOD' => $method,
'PATH_INFO' => $path,
'SERVER_NAME' => 'slim-test.dev',
), $options));
$app = new \Slim\Slim();
$this->app = $app;
$this->request = $app->request();
$this->response = $app->response();
// Return STDOUT
return ob_get_clean();
}
public function get($path, $options = array()){
$this->request('GET', $path, $options);
}
public function testGetAssets(){
$this->get('/asset');
$this->assertEquals('200', $this->response->status());
}
}
If my JSON response of http://example.com/asset looks like this (Code 200):
[
{
"AssetID": "4b0be88b9e853",
"AssetStatusID": "1"
}
]
Everything is good. To get the body of response just call the
$response->getBody() and use json_decode to decode this response. To get the header call the $response->getHeaders().
In your case it will by $this->response->getBody(). So your test
method will be look like this
public function testGetAssets(){
$this->get('/asset');
$response = json_decode($this->response->getBody(), true); //response body
$headers = $this->response->getHeaders() //response headers
$this->assertEquals('200', $this->response->status());
}
This answer is respect to the latest version of guzzlehttp i.e. 6.0
Related
I have a plugin in my module that was working in Magento 2.3, but after upgrade to 2.4 i am getting the error
TypeError: Argument 2 passed to
..\Plugin\Controller\Checkout\Sidebar\RemoveItemPlugin::afterExecute()
must be an instance of Magento\Framework\App\Response\Http, instance
of Magento\Framework\Controller\Result\Json\Interceptor given
This is the code :
use Magento\Customer\CustomerData\SectionPoolInterface;
use Magento\Framework\Serialize\Serializer\Json;
class RemoveItemPlugin
{
public function __construct(
SectionPoolInterface $sectionPool,
Json $json
) {
$this->sectionPool = $sectionPool;
$this->json = $json;
}
public function afterExecute(
\Magento\Checkout\Controller\Sidebar\RemoveItem $subject,
\Magento\Framework\App\Response\Http $result
): \Magento\Framework\App\Response\Http {
/* Get Cart Items */
$sectionNames = "cart";
$sectionNames = $sectionNames ? array_unique(\explode(',', $sectionNames)) : null;
$forceNewSectionTimestamp = false;
$response = $this->sectionPool->getSectionsData($sectionNames, (bool)$forceNewSectionTimestamp);
/* Prepare Result */
$content = $this->json->unserialize($result->getContent());
$content['cartSection'] = $response;
$content = $this->json->serialize($content);
$result->setContent($content);
return $result;
}
}
I tried the answer in this question :
https://magento.stackexchange.com/questions/351344/magento-2-4-sidebar-removeitem-afterexecute-must-be-an-instance-of-magento-f
but once I change $result to be type resultInterface, I can no longer use $result->getContent() to return the cart json. Is there a way to do this with resultInterface?
I need to get all registed routes to work with into a controller.
In slim 3 it was possible to get the router with
$router = $container->get('router');
$routes = $router->getRoutes();
With $app it is easy $routes = $app->getRouteCollector()->getRoutes();
Any ideas?
If you use PHP-DI you could add a container definition and inject the object via constructor injection.
Example:
<?php
// config/container.php
use Slim\App;
use Slim\Factory\AppFactory;
use Slim\Interfaces\RouteCollectorInterface;
// ...
return [
App::class => function (ContainerInterface $container) {
AppFactory::setContainer($container);
return AppFactory::create();
},
RouteCollectorInterface::class => function (ContainerInterface $container) {
return $container->get(App::class)->getRouteCollector();
},
// ...
];
The action class:
<?php
namespace App\Action\Home;
use Psr\Http\Message\ResponseInterface;
use Slim\Http\Response;
use Slim\Http\ServerRequest;
use Slim\Interfaces\RouteCollectorInterface;
final class HomeAction
{
/**
* #var RouteCollectorInterface
*/
private $routeCollector;
public function __construct(RouteCollectorInterface $routeCollector)
{
$this->routeCollector = $routeCollector;
}
public function __invoke(ServerRequest $request, Response $response): ResponseInterface
{
$routes = $this->routeCollector->getRoutes();
// ...
}
}
This will display basic information about all routes in your app in SlimPHP 4:
$app->get('/tests/get-routes/', function ($request, $response, $args) use ($app) {
$routes = $app->getRouteCollector()->getRoutes();
foreach ($routes as $route) {
echo $route->getIdentifier() . " → ";
echo ($route->getName() ?? "(unnamed)") . " → ";
echo $route->getPattern();
echo "<br><br>";
}
return $response;
});
From there, one can use something like this to get the URL for a given route:
$routeParser = \Slim\Routing\RouteContext::fromRequest($request)->getRouteParser();
$path = $routeParser->urlFor($nameofroute, $data, $queryParams);
With the following caveats:
this will only work for named routes;
this will only work if the required route parameters are provided -- and there's no method to check whether a route takes mandatory or optional route parameters.
there's no method to get the URL for an unnamed route.
I followed the instructions to save token in container with callback function (https://github.com/tuupola/slim-jwt-auth):
$app = new \Slim\App();
$container = $app->getContainer();
$container["jwt"] = function ($container) {
return new StdClass;
};
$app->add(new \Slim\Middleware\JwtAuthentication([
"path" => ["/"],
"passthrough" => ["/version", "/auth"],
"secret" => "mysecret",
"callback" => function ($request, $response, $arguments) use ($container) {
$container["jwt"] = $arguments["decoded"];
},
"error" => function ($request, $response, $arguments) {
$data["status"] = "error";
$data["message"] = $arguments["message"];
return $response
->withHeader("Content-Type", "application/json")
->write(json_encode($data, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT));
}
]));
No data is returned in response, it seems $this->jwt is empty.
$app->get("/user", 'getUsers');
function getUsers($req, $res, $args) {
$decode = $this->jwt;
print_r($decode);
}
Your route definition throws Using $this when not in object context error. To have access to $this you need to use a closure instead. See closure binding in the documentation.
$app->get("/user", function ($request, $response, $arguments) {
$decode = $this->jwt;
print_r($decode);
});
or
$getUsers = function ($request, $response, $arguments) use ($container) {
$decode = $this->jwt;
print_r($decode);
};
$app->get("/user", $getUsers);
With the either of code above you can access decoded token via $this. First example is preferred.
$ curl --include http://localhost:8081/user --header "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.mHEOMTUPhzNDAKheszd1A74EyLmKgy3PFdmKLg4ZNAE"
HTTP/1.1 200 OK
Host: localhost:8081
Connection: close
X-Powered-By: PHP/7.0.12
Content-Type: text/html; charset=UTF-8
Content-Length: 84
stdClass Object
(
[sub] => 1234567890
[name] => John Doe
[admin] => 1
)
The instruction you linked to states:
Callback is called only when authentication succeeds. It receives
decoded token in arguments. If callback returns boolean false
authentication is forced to be failed.
Do you meet this requirement when testing, i.e do you authenticate successfully? Also consider using var_dump($decode) instead of print_r($decode) when testing.
I have been attempting to implement a paypal functionality into my application by following the example here: http://www.alexventure.com/2011/04/02/zend-framework-and-paypal-api-part-2-of-2/
This is my paymentAction in my controller.
public function paymentAction()
{
$auth= Zend_Auth::getInstance();
$user= $auth->getIdentity();
$username = $user->username;
$cart = new Application_Model_DbTable_Cart();
$select = $cart->select()
->from(array('c' => 'cart'))
->join(array('p' => 'product'), 'p.productid = c.productid')
->where('username = ?', $username)
->setIntegrityCheck(false);
$fetch = $cart->fetchAll($select)->toArray();
$paypal = new My_Paypal_Client;
$amount = 0.0;
foreach($fetch as $item) {
$amount = $amount + ($item['price']*$item['quantity']);
}
$returnURL = 'http://www.google.com';
$cancelURL = 'http://www.yahoo.com';
$currency_code = 'USD';
$reply = $paypal->ecSetExpressCheckout(
$amount,
$returnURL,
$cancelURL,
$currency_code
);
if ($reply->isSuccessfull())
{
$replyData = $paypal->parse($reply->getBody());
if ($replyData->ACK == 'SUCCESS' || $replyData->ACK == 'SUCCESSWITHWARNING')
{
$token = $replyData->TOKEN;
$_SESSION['CHECKOUT_AMOUNT'] = $amount;
header(
'Location: ' .
$paypal->api_expresscheckout_uri .
'?&cmd=_express-checkout&token=' . $token
);
}
}
else
{
throw new Exception('ECSetExpressCheckout: We failed to get a successfull response from PayPal.');
}
}
However, this is the error that returns.
Message: No valid URI has been passed to the client
Where did i go wrong? I would be happy to provide code from other areas of my application if needed. Thanks.
Zend_Http_Client::request() has not received a valid instance of Zend_Uri_Http.
Here's where the error occurs:
/**
* Send the HTTP request and return an HTTP response object
*
* #param string $method
* #return Zend_Http_Response
* #throws Zend_Http_Client_Exception
*/
public function request($method = null)
{
if (! $this->uri instanceof Zend_Uri_Http) {
/** #see Zend_Http_Client_Exception */
require_once 'Zend/Http/Client/Exception.php';
throw new Zend_Http_Client_Exception('No valid URI has been passed to the client');//Note the exact message.
}//Truncated
The only obvious error I see in the code you provided is :
$paypal = new My_Paypal_Client;//no () at end of declaration
I hope you implemented part one of the tutorial where the constructor is built. Otherwise you may just need to pass a better uri.
[EDIT]
I think your problem is here:
//needs a uri value for Zend_Http_Client to construct
$paypal = new My_Paypal_Client($url);
ecSetExpressCheckout does not construct the http client so it has no idea of where it's requesting the token from.
Alternatively you could just add this line below $paypal and above $reply:
//pass the uri required to construct Zend_Http_Client
$paypal->setUri($url);
I just hope you know what the url shouild be.
Good Luck.
I'm trying to do searches with the API from last.fm with Zend_Rest_Client.
What am I to do with the response? How do I get the values from the response?
object(Zend_Rest_Client_Result)[226]
protected '_sxml' =>
object(SimpleXMLElement)[228]
public '#attributes' =>
array
'status' => string 'ok' (length=2)
public 'results' =>
object(SimpleXMLElement)[229]
public '#attributes' =>
array
...
public 'trackmatches' =>
object(SimpleXMLElement)[230]
...
protected '_errstr' => null
How do I loop over trackmatches? Everything I try returns null.
$results = $object->getIterator();
foreach($result as $result) {
...
}
Above code will do the magic.
You have to keep dereferencing until you get to something that isn't a SimpleXMLElement object. Trying to print a SimpleXMLElement object doesn't work.
$results = $object->getIterator();
foreach($results->results->trackmatches as $t) {
echo $t->sometagname;
}
I did not manage to be able to do it with Zend. Looks like a useless class like Zend_Http. I had to use the gool 'ol SPL:
$url = $this->host . '?method=track.search';
$url .= '&api_key=' . $this->apikey;
$url .= '&track=' . urlencode($value);
try {
$xmlstr = file_get_contents($url);
$xml = new SimpleXMLElement($xmlstr);
//var_dump($xml->results->trackmatches);
return $xml->results->trackmatches;
} catch (Exception $e) {
echo '<h4>url = ' . $url . '</h4>';
var_dump($e);
}