Symfony test get form errors - phpunit

If I have a functional test like:
public function testCreate()
{
$client = $this->makeClient();
$crawler = $client->request('POST', "/post/new");
$form = $crawler->selectButton("Create Post")->form();
$values = $form->getPhpValues();
$values['post']['title'] = 'test';
$values['post']['content'] = null; // Should fail because it isn't a string.
$crawler = $client->request($form->getMethod(), $form->getUri(), $values,
$form->getPhpFiles());
assertEquals() // What goes here?
}
How do I use the crawler to get the form errors and compare them?

I see two possible solutions for you:
Use the crawler to check for the actually rendered form errors in your rendered HTML code.
Make use of the profiler and access the form data collector: http://symfony.com/doc/current/testing/profiling.html

Related

why Update and insert in same method is not working in symfony?

I am using symfony3 with window 7:
This method should work for both action update and add.But its behavior only insertion. while i am setting $id also.
/**
* #Route("entity/entity/{id}", name="entity_entity",defaults={"id" = 0})
*/
public function entityAction(Request $request,$id){
$action = false;
$arr_XYZ_data = array();
$arr_XYZ_prepare_data = array();
$form_title = 'Add New XYZ';
$obj_XYZ = new XYZ();
$form = $this->createForm(XYZType::class, $obj_XYZ);
if($id!=0){
$obj_repo = $this->getDoctrine()->getRepository('AppBundle:XYZ');
$arr_XYZ_data = $obj_repo->find($id);
if($arr_XYZ_data){
$action = true;
$form_title = 'Update XYZ';
$arr_XYZ_data = $obj_repo->findXYZById($id);
$arr_XYZ_prepare_data = $this->_prepareData($arr_XYZ_data);
}
}
$form->handleRequest($request);
if (($form->isSubmitted())) {
$obj_XYZ->setXYZId($id);
$str_hiddenfield_result = $form->get('extraformfield')->getData();
$arr_hiddenfield_result = explode('&',$str_hiddenfield_result);
$obj_XYZ->setDef($obj_XYZ->getDef()->getDefId());
$obj_XYZ->setAbc($arr_hiddenfield_result[3]);
$obj_XYZ->setAuthor(1); //ldap session value
$em = $this->getDoctrine()->getManager();
$em->persist($obj_XYZ);
$em->flush();
$this->addFlash('success', 'Your record has been added successfully!');
return $this->redirectToRoute('XYZ_index', array(), 301);
}
}
anyone can suggest me how can i achieve this ?
Some remarks:
Are you calling the right URL (with $id != 0)?
Check if the form that is submitted is valid before doing anything.
Why are you calling setId() on the entity? Doctrine will set the ID of a new and persisted entity.
Finally, you are using a 301 redirect, which is a Permanent Redirect. This means that the browser will redirect any request to entity/entity/{id} to whichever URL is generated by XYZ_index.
I would recommend the following things:
Use isValid instead of isSubmitted for the form (it might not be valid but you are persisting its data!).
Use the built-in Forms, which you can load with an entity (so that you do not have to process data fields yourself).
Return a $this -> redirectToRoute('...', array(...)) instead of a 301.
After a lot of R&D i got the solution:
use:
$em->merge($obj_XYZ) instead of $em->persist($obj_XYZ);
before
$em->flush();

symfony2 test flashbag or session data

How do you test for FlashBag message?
Tried this:
public function testInvalidLogin()
{
$session = $this->client->getContainer()->get('session');
$crawler = $this->client->request('GET', '/login');
$this->assertTrue($this->client->getResponse()->isSuccessful());
$form = $crawler->filter('form');
$this->assertGreaterThan(0, $form->count());
$form = $form->form();
$this->assertNotEmpty($form);
$form['_username'] = 'username';
$form['_password'] = 'password';
$this->client->submit($form);
$this->assertTrue($this->client->getResponse()->isRedirect('http://localhost/login'));
$this->client->followRedirect();
$session = $this->client->getContainer()->get('session');
var_dump($session->getBag('flashes')->all()); // this print an empty array
}
The login controller is sets a flash message 'Bad credentials' but i'm not able to see it during the tests.
Probably it's because after your redirection you are poping flash message eg. somewhere in your template. Flash bag container remove flash message just after you call get method (to be specified - removing is implemented IN get method...). If you want just get the message without poping it you should use peek method.
I guess that if you move var_dump before followRedirect then you will get the result you are expecting.

Symfony2 functional testing InvalidArgumentException: The current node list is empty

I get "InvalidArgumentException: The current node list is empty." running functional tests through PHPUnit. Here is test i wrote:
public function testAdd()
{
$client = static::createClientWithAuthentication('main');
$crawler = $client->request('GET', 'en/manage');
$send_button = $crawler->selectButton('submit');
$form = $send_button->form(array(
'PrCompany[email]' => 'test#example.ua',
'PrCompany[first_name]' => 'Anton',
'PrCompany[last_name]' => 'Tverdiuh',
'PrCompany[timezone]' => 'Europe/Amsterdam'
));
$form['PrCompany[companies][1]']->tick();
$client->submit($form);
$this->assertTrue($crawler->filter('html:contains("User is invited")')->count() > 0);
}
You can debug the problem by using var_dump($client->getResponse()->getContent());
Additionally, I think you should write this:
$crawler = $client->submit($form);
Otherwise you'll be testing the response of the first url, before form submission.
I was also struggling with this, and It appeared that the selectButton method triggered this error.
After reading up on the DOM Crawler docs I found that the selectButton method takes the actual button text as a string argument. So if your button is 'submit my form please', that will be your text.
It does take different parameters too, as shown below (taken from the docs)
A selectButton() method is available on the Crawler which returns another
Crawler that matches a button (input[type=submit], input[type=image],
or a button) with the given text.
EDIT
After finally successfully completing the test I would also recommend you follow this example for testing forms:
use Goutte\Client;
$client = new Client();
$crawler = $client->request('GET', 'https://github.com/login');
$form = $crawler->selectButton('Log in')->form();
$form['login'] = 'symfonyfan';
$form['password'] = 'anypass';
$crawler = $client->submit($form);
$this->assertTrue($crawler->filter('html:contains("Welcome Back")')->count() > 0);
The main difference being, I have used the Goutte bundle, which I installed with composer from the packagist (in my case I added "fabpot/goutte": "1.0.*#dev")
As a follow up to what #greg0ire wrote, check to see if
var_dump($client->getResponse()->getContent());
Returns a redirect page instead of the actual content. If so, you can add this:
$client->followRedirects(true);
I had the same problem with Silex application. I was looking for
$buttonCrawler = $crawler->selectButton('input[type="submit"]');
Instead, the correct way to do it is give the value of the button
$buttonCrawler = $crawler->selectButton('value_of_the_button');
For example, in your page:
<form>
...
<input type="submit" value="Click Me">
</form>
And in your tests:
$buttonCrawler = $crawler->selectButton('Click Me');
$form = $buttonCrawler->form();
...
I see question still dont have answer. I had the same problem.
In my case goutte was not able to do this request because input name is changed by javascript on the fly.
When goutte received html it saw one form. And when submitting with pre filled params, form input elements could not be matched by $form->setValues($params) so \InvalidArgumentException was thrown.
Solved by doing request by hand.
// $form->setValues($data);
// $this->getGoutte()->submit($form);
$data = array(
'input_name[key]' => 'value'
);
$this->getGoutte()->request($form->getMethod(), $form->getUri(), $params);
You can try to use Codeception with Symfony2 module. It provides flexible interface to Symfony2 functional tests and has better debugging features.
This error would occur when crawler can't find form element requested; Quite tricky when you are using, for instance, form builder as when run, it will create different input name:
$form = $this-> createFormBuilder($store)
->add('storeNumber','text')
->add('storeName','text')
->add('save', 'submit')
->getForm();
will output field name like:
form_storeNumber
which should be used in test class:
$form=$crawler->selectButton('save')->form();
$form['form_storeNumber']='10';

Symfony2 Functional test: Passing form data directly

I am using phpunit to run functional tests but I am having a problem with a few of the forms. The problem is that phpunit is not aware of JS, and I have a form with a dynamically populated select box that needs jQuery.
So I need to pass the form data directly. The 'book' gives the following example:
// Directly submit a form (but using the Crawler is easier!)
$client->request('POST', '/submit', array('name' => 'Fabien'));
When I used this example the controller didn't receive any of the form data. Intially I saw that passing the array key 'name' wasn't correct in my situation as I needed the form name which was 'timesheet' in my code. So I tried something like:
$client->request('POST', '/timesheet/create', array('timesheet[project]' => '100'));
But this still didn't work. In the controller I tried to understand what was happening and what if anything was being received:
$postData = $request->request->get('timesheet');
$project = $postData['project'];
This didn't work and $project remained empty. However if I used the following code I got the value:
$project = $request->request->get('timesheet[project]');
But clearly that's not what I want. Atleast though I can see that there is some POST data. My last attempt was to try the following in the test method:
$this->crawler = $this->client->request('POST', '/timesheet/create/', array('timesheet' => array(project => '100'));
So I am trying to pass a 'timesheet' array as the first element of the request parameter array. But with this I get the error:
Symfony\Component\Form\Exception\UnexpectedTypeException: Expected argument of type "array", "string" given (uncaught exception) at /mnt/hgfs/pmt/src/vendor/symfony/src/Symfony/Component/Form/Form.php line 489
I would be very happy if someone can expand on what's in the 'book' about how I am supposed to get this working.
Form bind in controller:
if ($request->getMethod() == 'POST') {
$form->bindRequest($request);
if ($form->isValid()) {
$postData = $request->request->get('timesheet');
$project = $postData['project'];
$timesheetmanager = $this->get('wlp_pmt.timesheet_db_access');
$timesheetmanager->editTimesheet($timesheet);
return $this->redirect($this->generateUrl('timesheet_list'));
}
}
If you are wanting to know how to inject arrays of POST data using the test client...
In your test method, do something like
$crawler = $client->request('POST', '/foo', array(
'animal_sounds' => array(
'cow' => 'moo',
'duck' => 'quack'
)
); // This would encode to '/foo?animal_sounds%5Bcow%5D=moo&animal_sounds%5Bduck%5D=quack'
$this->assertTrue( ... );
In the controller, you would access your params like this:
$data = $request->request->get('animal_sounds');
$cowNoise = $data['cow'];
$duckNoise = $data['duck'];
Or you could just use the forms API if the test method was injecting valid form data...
do you have a $request parameter in your action?
that was the reason why my request->get() was empty:
//WRONG
public function projectAction()
{
$request = Request::createFromGlobals();
$project = $request->request->get('timesheet[project]');
//$project will be empty
}
//CORRECT
public function projectAction(Request $request)
{
$project = $request->request->get('timesheet[project]');
//$project is not empty
}
see
How do I create a functional test which includes a POST to a page with parameters?
Try to use $form->bind($clientData) instead of $form->bindRequest($request).

How do I create a functional test which includes a POST to a page with parameters?

I've read the documentation from Symfony2 but it doesn't seem to work. Here's my functional test:
public function testSearch()
{
$client = static::createClient();
$crawler = $client->request('POST', '/search/results', array('term' => 'Carteron'));
$this->assertTrue($crawler->filter('html:contains("Carteron")')->count() > 0);
$this->assertTrue($crawler->filter('html:contains("Auctions")')->count() > 0);
}
In my controller the "term" parameter is null when this request comes in. However, search works just fine when I perform it on the site, so I know its a problem with setting up the test.
I had the same problem, neither $request->query nor $request->request worked. Both did the same for me: it returned $default, not the given $parameters (Symfony 2.3).
Same behaviour as chris, in normal web browsers it works. I fixed this by replacing:
public function searchResultsAction($name)
{
$request = Request::createFromGlobals();
$term = trim($request->request->get('term'));
return $this->searchResultsWithTermAction($term);
}
With:
public function searchResultsAction(Request $request, $name)
{
// do NOT overwrite $request, we got it as parameter
//$request = Request::createFromGlobals();
$term = trim($request->request->get('term'));
return $this->searchResultsWithTermAction($term);
}
So createFromGlobals() does only work if $_GET and $_POST are set. Shich is not the case if you use the symfony test client. you have to add the $request parameter to the action.
(I used $name as a GET parameter in my routing)
I've never gotten an answer to this, but have implemented a work around that seems to work for my testing purposes. I'd love to hear feedback or get a real answers to this questions.
What I've done is create a 2nd route for the purposes of testing.
In real usage the uri would be /search/results?term=searchtermhere
For the purposes of testing, this didn't work. I could never get access to the term value when invoked via the automated test.
So what I've done is create a 2nd route just for testing which has a uri of /search/results/{searchtermhere}.
Then my action class used for a real search would call down to another function and pass the term to the function:
public function searchResultsAction()
{
$request = Request::createFromGlobals();
$term = trim($request->request->get('term'));
return $this->searchResultsWithTermAction($term);
}
So my functional tests would excersize searchResultsWithTermAction(), so the only code coverage I'm missing from this workaround is the extraction of the term from the request.
I don't know if anyone is still searching for an answer to this, but basically the problem is that you are looking in the wrong place for the parameter.
For your example you would need:
$term = trim($request->query->get('term'));
Hope this helps!

Resources