Controller unit test in slim3 - phpunit

At the outset, I would like to say - I'm new in unit testing in PHP (phpunit).
In my new project (slim3 framework) I would like to test my controllers for example LoginController.
My idea is (in unit test method)
Create instance of LoginController
Mock some services in controller (DI)
Execute method which is response for request (in my controllers method __invoke)
My problem is about parameters for __invoke method.
In Slim3 callable method for request has two first params:
RequestInterface $request and ResponseInterface $response
How can I create this parameters in my unit test class? I was searching for some examples for this issue but without success.
Any suggestions?
I've found some code in Slim3 tests to mock request:
protected function requestFactory()
{
$uri = Uri::createFromString('https://example.com:443/foo/bar?abc=123');
$headers = new Headers();
$cookies = array(
'user' => 'john',
'id' => '123',
);
$env = Slim\Http\Environment::mock();
$serverParams = $env->all();
$body = new Body(fopen('php://temp', 'r+'));
$request = new Request('GET', $uri, $headers, $cookies, $serverParams, $body);
return $request;
}
But I'm not sure that is good way.
Thanks for any help

I wrote up one solution here: https://akrabat.com/testing-slim-framework-actions/
I use Environment::mock() to create a $request and then I can run the action. Making each route callable a class where all dependencies are injected into the constructor makes this all much easier too.
Essentially, a test looks like this:
class EchoActionTest extends \PHPUnit_Framework_TestCase
{
public function testGetRequestReturnsEcho()
{
// instantiate action
$action = new \App\Action\EchoAction();
// We need a request and response object to invoke the action
$environment = \Slim\Http\Environment::mock([
'REQUEST_METHOD' => 'GET',
'REQUEST_URI' => '/echo',
'QUERY_STRING'=>'foo=bar']
);
$request = \Slim\Http\Request::createFromEnvironment($environment);
$response = new \Slim\Http\Response();
// run the controller action and test it
$response = $action($request, $response, []);
$this->assertSame((string)$response->getBody(), '{"foo":"bar"}');
}
}

Related

Silverstripe - FormSchema with submissions and validation

I'm trying to make use of the new FormSchema class in Silverstripe 4 but I'm having a tough time with the workflow for submitting the form. I've been able to return the schema and state, but when submitting the form back to the controller is where I run in to issues. Here is some example code:
class TestController extends Controller {
private static $allowed_actions = [
'schema',
'TestForm'
];
public function schema(HTTPRequest $request) {
$schema = new FormSchema();
return json_encode($schema->getMultipartSchema(['schema', 'state', 'errors'], "FormID", $this->TestForm()));
}
public function TestForm() {
$fields = FieldList::create(
TextField::create('Name', 'Name'),
EmailField::create('Email', 'Email')
);
$actions = FieldList::create(
FormAction::create('doTestSubmit', 'Submit')
);
$required = RequiredFields::create(['Name', 'Email']);
return Form::create($this, 'TestForm', $fields, $actions, $required);
}
public function doTestSubmit($data, $form) {
return json_encode(array('response' => 'The form validated and was submitted.'));
}
}
So in this scenario hitting /schema returns TestForm schema in json then the front end renders the form. Submitting the form sends the data back to /TestForm where it is validated. If the submission is valid it'll continue to doTestSubmit and return the response. That's great! But, if the submission is not valid then TestForm attempts to return the form and not the schema with the validation messages.
I first though about using a condition in TestForm() like if($this->request->isPOST()) or if($form->validationResult()->isValid()) but it doesn't seem like the proper way to handle it.
Any input or a simple code sample would be great.
I think you cannot use the standard Form > Form Post > Form Action way, you have to create an own Form Handler that validates the form. Like this (untested code):
public function doTestSubmit($request) {
$form = $this->TestForm()
$form->loadDataFrom($request->postVars);
if(!$form->validationResult()->isValid()) {
$formSchema = new FormSchema();
$state = $formSchema->getState($form);
return json_encode($state);
}
//Valid, continue
}

handle and personalize symfony no route found exception response

I have a controller that response a json data to another application , this is the controller code :
/**
*
* #Get("/getXXX/{id}")
*/
public function getDataAction($id,Request $request){
$ceService = $this->container->get('comptexpertcews.service');
$employeNumber= $request->get('employeNumber') ;
$url = $this->container->getParameter('serverUri') . $id;
$res = new Response();
$res->setContent($ceService->getCews($url, wsUrl::ws_Headers));
$res->headers->set('Content-TYpe','application/json; charset=UTF-8');
return $res;
}
The problem is by default , if you don't give id in the url , symfony rise exception : not route foundexception , what i want is to handle the exception and personalize with my owner response like sending
{"error" :" id undefined "}
instead of the long message expcetion of symfony
You have two simple options:
Don't use param converter, get you data from a repository and then you can wrap it in try catch and create your own exception/message
If this is something you want to do globally, you can implement an event listener that catches onKernelException event and work with it from there, e.g.:
public function onKernelException(GetResponseForExceptionEvent $event): void
{
$exception = $event->getException();
if ($exception instanceof NotFoundHttpException) {
$response = $this->resourceNotFoundResponse(json_encode($exception->getMessage()));
}
if (isset($response)) {
$event->setResponse($response);
}
}
You also need to register you listener as a service, see the documentation here http://symfony.com/doc/current/event_dispatcher.html

FOSRestBundle & invalid form CRF token

I'm trying to implement FOSRestBundle and Symfony forms.
I have found this tutorial but I have problem with this part
private function processForm(PageInterface $page, array $parameters, $method = "PUT")
{
$form = $this->formFactory->create(new PageType(), $page, array('method' => $method));
$form->submit($parameters, 'PATCH' !== $method);
if ($form->isValid()) { //form is not valid.
$page = $form->getData();
$this->om->persist($page);
$this->om->flush($page);
return $page;
}
throw new InvalidFormException('Invalid submitted data', $form);
}
ERROR: The CSRF token is invalid. Please try to resubmit the form.
Here is the controller from tutorial. And here is my class controller:
public function newAction(Request $request)
{
$form = new EntryType();
$newEntry = $this->container->get('entries.entry.handler')->post(
$request->request->get($form->getName())
);
return View::create()
->setStatusCode(200)
->setFormat('json')
->setSerializationContext(SerializationContext::create()->setGroups(array('list')))
->setData($newEntry);
}
Should I skip checking isValid() or fix this somehow? How?
OK, It is clear now. CRF verification (csrf_protection) should be disabled
CSRF token is invalid when calling rest post api from php Client
https://github.com/liuggio/symfony2-rest-api-the-best-2013-way/issues/1#issuecomment-31435232
CSRF validation needed or not when using RESTful API?
From part 3 of the tutorial :
It's possible to disable the CSRF based on the user’s role.
# app/config/config.yml
fos_rest:
disable_csrf_role: ROLE_API
# you can also try
# disable_csrf_role: IS_AUTHENTICATED_FULLY
See also this issue.

samlspbundle integration with fosuserbundle

I try to integrate the bundle samlspbundle on a project running with fosuserbundle.
I actually received information from my idp which send me the saml with the email address of the user.
What i'm trying to do is load the user from my table fosuser and then authenticate it.
this is the method i am in my model SamlToUser :
private function loadUserByTargetedID($targetedID)
{
$repository = $this->container->get('doctrine')->getManager()->getRepository('MCCAppBDDBundle:User');
$user = $repository->findOneBy(
array('email' => $targetedID)
);
if ($user) {
$userManager = $this->container->get('fos_user.user_manager');
$url = $this->container->get('router')->generate('homepage');
$response = new RedirectResponse($url);
$this->container->get('fos_user.security.login_manager')->loginUser(
$this->container->getParameter('fos_user.firewall_name'),
$user,
null
);
$userManager->updateUser($user);
return $user;
}
throw new \Symfony\Component\Security\Core\Exception\UsernameNotFoundException();
}
After that i have this error : PHP Warning: session_regenerate_id(): Cannot regenerate session id - headers already sent
I'm not sure is the right thing to do.
If you need other detail, i can give you.
Thanks to help.

Symfony2 Doctrine - Flushing in kernel.response listener flushs bad data

In order to do some logging for my Symfony2 app, I created a service that logs any connection, here is the method called on kernel.response :
public function log(FilterResponseEvent $event)
{
$log = new Log();
$request = $event->getRequest();
$response = $event->getResponse();
//fill the Log entity with stuff from request & response data
$manager = $this->container->get('doctrine.orm.entity_manager');
$manager->persist($log);
$manager->flush();
}
All of this seems fine, however when I execute a test like this one (patch with empty data to trigger a failure):
$this->client->request(
'PATCH',
'/users/testificate',
array(
'firstName' => '',
)
);
Which calls this action :
protected function processForm($item, $method = 'PATCH')
{
$form = $this->createForm(new $this->form(), $item, array('method' => $method));
$form->handleRequest($this->getRequest());
if ($form->isValid()) {
$response = new Response();
// Set the `Location` header only when creating new resources
if ($method == 'POST') {
$response->setStatusCode(201);
$response->headers->set('Location',
$this->generateUrl(
'get_' . strtolower($class), array('slug' => $item->getId()),
true // absolute
)
);
}
else {
$response->setStatusCode(204);
}
$this->em->flush();
return $response;
}
$this->em->detach($item);
return RestView::create($form, 400);
}
Although the test fails, the entity is patched, and of course it must not.
After some search what I've learnt is:
The parameters enter the form validator
The validation fails, thus returning a 400 http code without flushing the entity
However during the validation process, the entity gets hydrated with the invalid data
When the service is called on kernel.response, the $manager->flush(); flush all the data... including the bad data provided by the PATCH test.
What I've tried thus far:
1) Do a $manager->clear(); before $manager->persist(); ... doesn't change anything
2) Do a $manager->detach($item); if the form validation failed... doesn't change anything
Thanks !
I recently stumbled across problems with flushing in kernel.response when upgrading from Doctrine 2.3.4 to the latest 2.4 branch. Try flusing the log entities from kernel.terminate. Leave any modifications to the Response in kernel.response.

Resources