I'm doing some functional testing with PHPUnit in Symfony2 .
I use PHPUnit version 4.4.1 ,tests were created with the controller generation (doctrine:generate:crud)
So , I only tests the CRUD on an User, and it takes me more than 30 seconds (This varies between 30 and 40s ) . Is this comes from the code? Tests themselves ? The fact that I worked on a remote server?
Here is the test class :
class UserControllerTest extends WebTestCase
{
/**
* Test CRUD functions (Create, read, update, delete)
*/
public function testCompleteScenario()
{
// Create a new client to browse the application
$client = static::createClient();
// Create a new entry in the database
$crawler = $client->request('GET', '/user/');
$this->assertEquals(200, $client->getResponse()->getStatusCode(), "Unexpected HTTP status code for GET /user/");
$crawler = $client->click($crawler->selectLink('Create a new entry')->link());
// Fill in the form and submit it
$form = $crawler->selectButton('Create')->form(array(
's_dosfabbundle_user[login]' => 'test.test',
's_dosfabbundle_user[trigram]' => 'tet',
's_dosfabbundle_user[email]' => 'test#test.com',
's_dosfabbundle_user[productionUnits]' => '11',
's_dosfabbundle_user[roles]' => '55',
));
$client->submit($form);
$crawler = $client->followRedirect();
// Check data in the show view
$this->assertGreaterThan(0, $crawler->filter('td:contains("test.test")')->count(), 'Missing element td:contains("test.test")');
// Edit the entity
$crawler = $client->click($crawler->selectLink('Edit')->link());
$form = $crawler->selectButton('Update')->form(array(
's_dosfabbundle_user[login]' => 'test.testUpdate',
's_dosfabbundle_user[trigram]' => 'tetU',
's_dosfabbundle_user[email]' => 'test_update#test.com',
's_dosfabbundle_user[productionUnits]' => '11',
's_dosfabbundle_user[roles]' => '57',
));
$client->submit($form);
$crawler = $client->followRedirect();
// Check the element contains an attribute with value equals "Foo"
$this->assertGreaterThan(0, $crawler->filter('[value="tetU"]')->count(), 'Missing element [value="tetU"]');
// Delete the entity
$client->submit($crawler->selectButton('Delete')->form());
$crawler = $client->followRedirect();
// Check the entity has been delete on the list
$this->assertNotRegExp('/tetU/', $client->getResponse()->getContent());
}
}
Just a shot in the dark, as more info is needed, but I had the same issue. If you've got xdebug enabled, you could try disabling it, see disabling xdebug. This is of course only relevant if you're not using it for things like code coverage (which always takes quite a while).
Related
I used to use edit and update methods in my controller to submit and handle a PUT form submission. It works fine and the code looks like this,
public function edit(Category $category): Response
{
$form = $this->createForm(CategoryType::class, $category, [
'action' => $this->generateUrl('category_update', [
'id' => $category->getId(),
]),
'method' => 'PUT',
]);
return $this->render('category/edit.html.twig', [
'category_form' => $form->createView(),
]);
}
public function update(Category $category, Request $request): Response
{
$form = $this->createForm(CategoryType::class, $category, ['method' => 'PUT']);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$entityManager = $this->getDoctrine()->getManager();
$entityManager->flush();
return new Response('', Response::HTTP_NO_CONTENT);
}
return new Response('', Response::HTTP_BAD_REQUEST);
}
As PUT is not supported by HTML forms, the edit request uses POST with a '_method' parameter as 'PUT' instead of a real PUT request.
Now I want to remove the edit method and send a real PUT request from the frontend. When I used Postman to test this, I found the update method cannot handle a real PUT request.
When I use Postman to send POST + '_method'='PUT' requests, it works fine, but when I send PUT requests, it shows BAD_REQUEST, which is the last line in my code. isSubmitted() returns false.
I know I don't need to use Forms here, but it's been used in the store method. Is it possible to use it to handle a real PUT request? What should I change in my update method?
Seems like you're missing $entityManager->merge($category); in the update() method. Try adding it above $entityManager->flush(); and let us know if it works.
You need to write _method instead of method
$form = $this->createForm(CategoryType::class, $category, ['_method' => 'PUT']);
Also you need to persist object, before flushing
When I test an entity it creates it in the database but I can't manage to delete it. I think I have the default code to delete the entity but it does not work, is there another way? am I missing something?
Here is the code. I'm using symfony 2.7.8 and Php unit 4.8.0
public function testCreateCurso()
{
// Create a new client to browse the application
$client = static::createAuthorizedClient();
// Create a new entry in the database
$crawler = $client->request('GET', '/admin/curso/');
$this->assertEquals(200, $client->getResponse()->getStatusCode(), 'Unexpected HTTP status code for GET /curso/');
$crawler = $client->click($crawler->selectLink('Crear Nuevo Curso')->link());
// Fill in the form and submit it
$form = $crawler->selectButton('Create')->form(array(
'appbundle_curso[nombreCurso]' => 'Test',
'appbundle_curso[codigoCurso]' => 'Test4',
// ... other fields to fill
));
$client->submit($form);
$this->assertTrue($client->getResponse()->isRedirect());
$crawler = $client->followRedirect();
// Check data in the show view
$this->assertGreaterThan(0, $crawler->filter('td:contains("Test")')->count(), 'Missing element td:contains("Test")');
// Edit the entity
$crawler = $client->click($crawler->selectLink('Editar')->link());
$form = $crawler->selectButton('Update')->form(array(
'appbundle_curso[nombreCurso]' => 'Foo',
'appbundle_curso[codigoCurso]' => 'Foo1',
// ... other fields to fill
));
$client->submit($form);
//
$crawler = $client->followRedirect();
// Check the element contains an attribute with value equals "Foo"
$this->assertGreaterThan(0, $crawler->filter('[value="Foo"]')->count(), 'Missing element [value="Foo"]');
// Delete the entity
$client->submit($crawler->selectButton('Delete')->form());
$crawler = $client->followRedirect();
// Check the entity has been delete on the list
$this->assertNotRegExp('/Foo/', $client->getResponse()->getContent());
var_dump($client->getResponse()->getContent());
}
This code is actually showing us how you test the UI, but the real code that is actually deleting the entity...
So, you should first of all check that both scenarios (adding an entity and deleting it) are actually working properly with unit tests (maybe when deleting the entity you're not flushing changes, for example...).
Then, when you have demonstrated yourself that you can actually add and delete the entity, and the controllers are working, then you should test your user interface, and that's what you're showing us.
So, if you've already done this, the issue is on your UI (for example, your button can't be followed).
Maybe a little bit more of information?
I was missing this method to delete the entity from the database
/**
* Close doctrine connections to avoid having a 'too many connections'
* message when running many tests
*/
public function tearDown(){
parent::tearDown();
}
I'm making an app mobile and I need to upload a photo from my camera via my API. But when doctrine try to INSERT INTO, he isn't happy because my name field is null. So the name generate by my entity is missing.
I don't understand where the code block. My web app uses already the same entity photo and everything works fine.
My Api controller:
<?php
/**
* #throws AccessDeniedException
* #return array
* #FOSRest\View()
*/
public function apiUploadAction()
{
$photo = new Photo;
$photo->setUser($this->getUser());
$photo->setFile = new UploadedFile(
$_FILES["file"]["tmp_name"],
$_FILES["file"]["name"],
$_FILES["file"]["type"],
$_FILES["file"]["size"],
$_FILES["file"]["error"],
$test = false
);
$em = $this->getDoctrine()->getManager();
$em->persist($photo);
$em->flush();
$apiResponse = array(
"code" => true,
"style" => "success",
/*********** SCREENSHOT COMES FROM HERE ***********/
"message" => $_FILES["file"]["tmp_name"]." ".$_FILES["file"]["name"]." ".$_FILES["file"]["type"]." ".$_FILES["file"]["size"]." ".$_FILES["file"]["error"],
);
$view = View::create();
$view->setFormat('json');
$view->setData($apiResponse);
return $this->get('fos_rest.view_handler')->handle($view);
}
The $apiResponse.message (screenshot):
As you can see symfony has the image so the problem don't come from my app mobile. new UploadedFile(); is the right way ? To upload without form.
Here is the solution :
Replace :
$photo->setFile = new UploadedFile(
$_FILES["file"]["tmp_name"],
$_FILES["file"]["name"],
$_FILES["file"]["type"],
$_FILES["file"]["size"],
$_FILES["file"]["error"],
$test = false
);
by
$photo->setFile($this->getRequest()->files->get('file'));
I need to be able to do an operation on form data before it gets persisted to the database. The problem is, the operation can be risky and I need the user's consent each time.
I'd like to do this through a confirmation form (with a little message explaining what's going on), not with a Javascript confirm window.
How can I achieve this functionality?
Here is an example of a controller action method that handles the form:
<?php
public function indexAction(Request $request)
{
...
$form = $this->createFormBuilder($myEntity)
->add('someField', 'integer', array('required' => true))
// Lots and lots of fields
->getForm();
if ($request->isMethod('POST')) {
$form->bind($request);
if ($form->isValid()) {
// CUSTOM VALIDATION HERE
// If invalid, must display a new confirmation form to ask user
// if it's alright to do a somewhat risky operation that would
// validate the form data.
// Else, persist the data.
$db = $this->getDoctrine()->getManager();
$db->persist($myEntity);
$db->flush();
return $this->redirect($this->generateUrl('my_path_to_success_page'));
}
}
return $this->render('MyBundle:Preferences:index.html.twig', array(
'form' => $form->createView(),
'errors' => $form->getErrors(),
));
}
You might want to look into CraueFormFlowBundle, which allows you to create multi-step forms.
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).