Error Unit test symfony - unit-testing

When I try to execute this unit test
I have a problem
so this is my function testErrors
.
public function testErrors(){
$client = static::createClient();
$crawler = $client->request('GET', '/add');
$form = $crawler->selectButton('save')->form(array(
'user[firstName]' => 'test1',
'user[lastName]' => 'test',
'user[email]' => 'test#gmail.com',
));
$crawler = $client->submit($form);
// 3 errors
$this->assertTrue($crawler->filter('.error_list')->count() == 3);
// Error firstName field
$this->assertTrue($crawler->filter('#firstName')->siblings()->first()->filter('.error_list')->count() == 1);
// Error lasName field
$this->assertTrue($crawler->filter('#lastName')->siblings()->first()->filter('.error_list')->count() == 1);
// Error email field
$this->assertTrue($crawler->filter('#email')->siblings()->first()->filter('.error_list')->count() == 1);
}
I have this problem
InvalidArgumentException: The current node list is empty .
this is my Controller
/**
* #Route("/add", name="addPage")
*/
public function AddAction(Request $request)
{
$user = new User();
$form = $this->createFormBuilder($user)
->add('email', TextType::class)
->add('firstName', TextType::class)
->add('lastName', TextType::class)
->add('save', SubmitType::class, array('label' => 'Add'))
->getForm();
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$firstName = $form['firstName']->getData();
$lastName = $form['lastName']->getData();
$email = $form['email']->getData();
$user->setFirstName($firstName);
$user->setLastName($lastName);
$user->setEmail($email);
$em = $this->getDoctrine()->getManager();
$em->persist($user);
$em->flush();
$this->addFlash('notice','user added' );
return $this->redirectToRoute('listPage');
}

I think it's your selectButton('save') what gives you the error. Try it with your button label Add instead of save

Related

Can't add data via postman

I want to make crud in postman with codeigniter4. But I have a problem when I will add data with post function an error occurs in postman like the picture I sent.
This is a picture of the Error section
controller Konven.php
class Konven extends BaseController
{
/**
* Get all Konven
* #return Response
*/
public function index()
{
$model = new KonvenModel();
return $this->getResponse(
[
'message' => 'Product Convent retrieved',
'produk_konven' => $model->findAll()
]
);
}
/**
* Create a new KOnven
*/
public function store()
{
$rules = [
'nama_menu' => 'required',
'tipe_id' => 'required'
];
$input = $this->getRequestInput($this->request);
if (!$this->validateRequest($input, $rules)) {
return $this->getResponse(
$this->validator->getErrors(),
ResponseInterface::HTTP_BAD_REQUEST
);
}
$konvenEmail = $input['nama_menu'];
$model = new KonvenModel();
$model->save($input);
$produk_konven = $model->where('nama_menu', $konvenEmail)->first();
return $this->getResponse(
[
'message' => 'Product Convent added successfully',
'produk_konven' => $produk_konven
]
);
}
/**
* Get a single client by ID
*/
public function show($produk_konven_id)
{
try {
$model = new KonvenModel();
$produk_konven = $model->findKonvenById($produk_konven_id);
return $this->getResponse(
[
'message' => 'Product Convent retrieved successfully',
'produk_konven' => $produk_konven
]
);
} catch (Exception $e) {
return $this->getResponse(
[
'message' => 'Could not find product convent for specified ID'
],
ResponseInterface::HTTP_NOT_FOUND
);
}
}
model KonvenModel.php
class KonvenModel extends Model
{
protected $table = 'produk_konven';
protected $allowedFields = [
'nama_menu',
'status',
'tipe_id'
];
protected $updatedField = 'updated_at';
public function findKonvenById($produk_konven_id)
{
$produk_konven = $this
->asArray()
->where(['produk_konven_id' => $produk_konven_id])
->first();
$produk_konven->join('tipe', 'produk_konven.tipe_id = tipe.tipe_id');
$produk_konven->orderBy("produk_konven_id", "asc");
if (!$produk_konven) throw new Exception('Could not find client for specified ID');
return $produk_konven;
}
}
Can friends help me overcome the obstacles that I am experiencing?

Symfony: update many-to-many

I have Post and Tag entities with many-to-many relationship. In Post create and edit form there is a textbox where I can enter tags separated by comma relevant to that post. For example, when I enter tag1, tag2, tag3 for post with title 'Post1', the form will create post and tag entities and add these tags to tags list of that post.I use data transformer to create tag entities.
class Post{
/**
* #ORM\ManyToMany(targetEntity="Tag", mappedBy="posts",cascade={"all"})
*/
protected $tags;
public function __construct() {
$this->tags = new ArrayCollection();
}
/**
* #return ArrayCollection
*/
public function getTags()
{
return $this->tags;
}
/**
* #param Tag $tag
*/
public function addTag(Tag $tag)
{
$tag->addPost($this);
$this->tags->add($tag);
}
/**
* #param Tag $tag
*/
public function removeTag(Tag $tag)
{
$this->tags->removeElement($tag);
}
}
PostType
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('title', TextType::class, array('label' => 'Title'))
->add('tags', TextType::class, array('label' => 'Tags'))
;
$builder->get('tags')
->addModelTransformer(new TagViewTransformer($this->manager));
}
TagViewTransformer
class TagViewTransformer implements DataTransformerInterface
{
public function transform($value)
{
/...
}
public function reverseTransform($value)
{
$tags = array();
if ( $value )
{
if( strpos($value, ',') !== false )
{
$list = array_unique(explode(",", $value));
}
else
{
$list = array(trim($value));
}
foreach ( $list AS $tagName )
{
$tag = $this->em
->getRepository('CoreBundle:Tag')
->findOneBy(array('name' => trim($tagName)));
if( !$tag )
{
$tag = new Tag();
$tag->setName(trim($tagName));
$this->em->persist($tag);
}
$tags[] = $tag;
}
}
return $tags;
}
}
This works fine when I try to create Post, all tags are transformed to entities and are added to Post's tags list. but when I try to edit, I start having problems
public function editAction(Request $request, Post $post)
{
$deleteForm = $this->createDeleteForm($post);
$editForm = $this->createForm(PostType::class, $post);
$editForm->handleRequest($request);
$originalTags = $post->getTags();
if ($editForm->isSubmitted() && $editForm->isValid()) {
$em = $this->getDoctrine()->getManager();
$newTags = $editForm->get('tags')->getData();
foreach ($originalTags as $currentTag) {
if (!in_array($currentTag, $newTags)) {
$post->removeTag($currentTag);
}
}
$em->persist($post);
$em->flush();
return $this->redirectToRoute('post_show', array('id' => $post->getId()));
}
return $this->render('AppBundle:Post:edit.html.twig', array(
'entity' => $post,
'form' => $editForm->createView(),
'delete_form' => $deleteForm->createView(),
));
}
Let's say Post has tags: tag1, tag2, tag3, but I want to remove tag3 and add tag4. So I will change tags textbox to tag1, tag2, tag4. However when I submit form, I get tag1, tag2, tag3, tag4. So tag3 is not removed from Post's tag list.
What is wrong with the editAction code?
try with this
public function editAction(Request $request, Post $post)
{
$deleteForm = $this->createDeleteForm($post);
$editForm = $this->createForm(PostType::class, $post);
$editForm->handleRequest($request);
if ($editForm->isSubmitted() && $editForm->isValid()) {
$em = $this->getDoctrine()->getManager();
$em->persist($post);
$em->flush();
return $this->redirectToRoute('post_show', array('id' => $post->getId()));
}
return $this->render('AppBundle:Post:edit.html.twig', array(
'entity' => $post,
'form' => $editForm->createView(),
'delete_form' => $deleteForm->createView(),
));
}
Use Orphan removal to do this :
class Post
{
/**
* #ORM\ManyToMany(targetEntity="Tag", mappedBy="posts", cascade={"all"}, orphanRemoval=true)
*/
protected $tags;
/* Rest of your class */
}
Do some tests, maybe cascade all is not necessary here.

Doctrine - How to extract results and their relationships as array

I have an entity, call it Stones and Stones has a ManyToMany relationship with Attributes.
So I query the entity to get the Stones and then I hydrate this to convert it into an array.
$result = $this->stoneRepository->find($stone_id);
if ( ! $result )
{
return false;
}
$resultArray = $this->doctrineHydrator->extract($result);
This works fine for the Stone entity however I noticed that the join (Attributes) remain as objects.
array (size=12)
'id' => int 1
'name' => string 'Agate' (length=5)
'title' => string 'Title' (length=5)
'attribute' =>
array (size=5)
0 =>
object(Stone\Entity\StAttribute)[1935]
private 'id' => int 2
private 'name' => string 'Hay fevor' (length=9)
private 'state' => boolean true
private 'created' => null
private 'modified' => null
1 =>
object(Stone\Entity\StAttribute)[1936]
private 'id' => int 15
private 'name' => string 'Libra' (length=5)
private 'state' => boolean true
private 'created' => null
private 'modified' => null
2 =>
etc.
What is the process to hydrate the Attribute objects?
Hydration is populating an object (entity) using an array which is opposite of the extraction.
Since you want the resultset in array format, you should prevent unnecessary hydration and extraction process which already occurs in the ORM level under the hood.
Try to use Query Builder Api instead of built-in find() method of the entity repository. This is not a single-line but really straightforward and faster solution, it should work:
$qb = $this->stoneRepository->createQueryBuilder('S');
$query = $qb->addSelect('A')
->leftJoin('S.attribute', 'A')
->where('S.id = :sid')
->setParameter('sid', (int) $stone_id)
->getQuery();
$resultArray = $query->getOneOrNullResult(\Doctrine\ORM\Query::HYDRATE_ARRAY);
This way, you will also prevent running additional SQL queries against database to fetch associated entities. (StAttribute in your case)
I thought I would follow up on this to show how this can be resolved using a CustomStrategy.
By far the easiest and fastest method was suggested by foozy. What I like about the solution is that when I use hydration in ApiGility for instance I can build custom queries which will produce the desired result in a very few lines of code.
The other solution I was working on was to add a custom strategy:
<?php
namespace Api\V1\Rest\Stone;
use DoctrineModule\Stdlib\Hydrator\Strategy\AbstractCollectionStrategy;
use Zend\Stdlib\Hydrator\Strategy\StrategyInterface;
class CustomStrategy extends AbstractCollectionStrategy
{
public function __construct($hydrator)
{
$this->hydrator = $hydrator;
}
/**
* #param mixed $values
* #return array|mixed
*/
public function extract($values)
{
$returnArray = [];
foreach ($values AS $value)
{
$returnArray[] = $this->hydrator->extract($value);
}
return $returnArray;
}
/**
* #param mixed $values
* #return mixed
*/
public function hydrate($values)
{
$returnArray = [];
foreach ($values AS $value )
{
$returnArray[] = $this->hydrator->hydrate($value);
}
return $returnArray;
}
}
Then from the service side I add various strategies to the hydrator like so:
$result = $this->stoneRepository->find($stone_id);
$this->doctrineHydrator->addStrategy("product", new CustomStrategy( $this->doctrineHydrator ) );
$this->doctrineHydrator->addStrategy("attribute", new CustomStrategy( $this->doctrineHydrator ) );
$this->doctrineHydrator->addStrategy("image", new CustomStrategy( $this->doctrineHydrator ) );
$this->doctrineHydrator->addStrategy("related", new CustomStrategy( $this->doctrineHydrator ) );
$resultArray = $this->doctrineHydrator->extract($result);
After which I created a custom entity:
<?php
namespace Api\V1\Rest\Stone;
class StoneEntity
{
public $id;
public $name;
public $description;
public $code;
public $attribute;
public $product;
public $image;
public function getArrayCopy()
{
return array(
'id' => $this->id,
'name' => $this->name,
'description' => $this->description,
'code' => $this->code,
'attribute' => $this->attribute,
'product' => $this->product,
'image' => $this->image
);
}
public function exchangeArray(array $array)
{
$this->id = $array['id'];
$this->name = $array['name'];
$this->description = $array['description'];
$this->code = $array['code'];
$this->attribute = $array['attribute'];
$this->product = $array['product'];
$this->image = $array['image'];
}
}
And the final part is to exchange the returned data with the custom entity:
$entity = new StoneEntity();
$entity->exchangeArray($resultArray);
And finally to return the result:
return $entity;
To be honest, the above is just too long winded and my final solution as per the suggestion by foozy was this:
public function fetchOne($stone_id)
{
$qb = $this->stoneRepository->createQueryBuilder('S');
$query = $qb->addSelect('A','P','I','C')
->leftJoin('S.attribute', 'A')
->innerJoin('A.category', 'C')
->innerJoin('S.product' , 'P')
->innerJoin('S.image' , 'I')
->where('S.id = :sid')
->setParameter('sid', (int) $stone_id)
->getQuery();
$resultArray = $query->getOneOrNullResult(\Doctrine\ORM\Query::HYDRATE_ARRAY);
if ( ! $resultArray )
{
return false;
}
return $resultArray;
}

CakePHP 2.4 mock a method in a model

I want to test a model and for one of those tests I want to mock a method of the model I am testing. So I don't test a controller and I don't want to replace a whole model, just one method of the same model I test.
Reason is that this model method calls a file upload handler. This feature is already tested elsewhere.
What I am doing now is:
I test the model 'Content'. There I test it's method 'addTeaser', which calls 'sendTeaser'.
SO I want to mock sendTeaser and fake a successful answer of the method sendTeaser, while testing addTeaser.
That looks like this:
$model = $this->getMock('Content', array('sendTeaser'));
$model->expects($this->any())
->method('sendTeaser')
->will($this->returnValue(array('ver' => ROOT.DS.APP_DIR.DS.'webroot/img/teaser/5/555_ver.jpg')));
$data = array(
'Content' => array(
'objnbr' => '555',
'name' => '',
...
)
)
);
$result = $model->addTeaser($data);
$expected = true;
$this->assertEquals($expected, $result);
When I let my test run, I get an error that a model within the method 'sendTeaser' is not called properly. Hey! It shouldn't be called! I mocked the method!
..... or not?
What would be the proper syntax for mocking the method?
Thanks a lot as always for help!
Calamity Jane
Edit:
Here is the relevant code for my model:
App::uses('AppModel', 'Model');
/**
* Content Model
*
* #property Category $Category
*/
class Content extends AppModel {
public $dateipfad = '';
public $fileName = '';
public $errormessage = '';
public $types = array(
'sqr' => 'square - more or less squarish',
'hor' => 'horizontal - clearly wider than high',
'lnd' => 'landscape - low but very wide',
'ver' => 'column - clearly higher than wide',
);
public $order = "Content.id DESC";
public $actsAs = array('Containable');
public $validateFile = array(
'size' => 307200,
'type' => array('jpeg', 'jpg'),
);
//The Associations below have been created with all possible keys, those that are not needed can be removed
public $hasMany = array(
'CategoriesContent' => array(
'className' => 'CategoriesContent',
),
'ContentsTag' => array(
'className' => 'ContentsTag',
),
'Description' => array(
'className' => 'Description',
)
);
/**
* Saves the teaser images of all formats.
*
* #param array $data
*
* #return Ambigous <Ambigous, string, boolean>
*/
public function addTeaser($data)
{
$objnbr = $data['Content']['objnbr'];
$type = $data['Content']['teaser-type'];
if (!empty($data['Content']['teaser-img']['tmp_name'])) {
$mFileNames = $this->sendTeaser($data, $objnbr, $type);
}
if (!is_array($mFileNames)) {
$error = $mFileNames;
//Something failed. Remove the image uploaded if any.
$this->deleteMovedFile(WWW_ROOT.IMAGES_URL.$mFileNames);
return $error;
}
return true;
}
/**
* Define imagename and save the file under this name.
*
* Since we use Imagechache, we don't create a small version anymore.
*
* #param integer $objnbr
* #param string $teasername
*
* #return multitype:Ambigous <string, boolean> |Ambigous <boolean, string>
*/
public function sendTeaser($data, $objnbr, $type)
{
//$path = str_replace('htdocs','tmp',$_SERVER['DOCUMENT_ROOT']);
$this->fileName = $this->getImageName($objnbr, $type);
$oUH = $this->getUploadHandler($data['Content']['teaser-img']);
debug($oUH);
exit;
$error = $oUH->handleFileUpload();
if (empty($type))
$type = 0;
if ($error === 'none'){
// Send to ImageChacheServer
$oICC = $this->getImagecacheConnector();
$sCacheUrl = $oICC->uploadFile($objnbr, $type, $this->fileName);
debug($sCacheUrl);
return array($type => $this->fileName);
}
return $error;
}
public function getUploadHandler($imgdata)
{
App::uses('UploadHandler', 'Lib');
$oUH = new UploadHandler($this, $imgdata);
return $oUH;
}
}
Changing getMock to getMockForModel didn't change the output though.
I'd like to emphasize the answer from #ndm using Cake test helper class CakeTestCase::getMockForModel()
$theModel = CakeTestCase::getMockForModel('Modelname', ['theMethodToMock']);
$theModel->expects($this->once())
->method('theMethodToMock')
->will($this->returnValue('valueToReturn'));
$this->getMock is not the way to mock. You should use $this->generate
I would reccomend you to read a book about CakePHP unti testing, like this: https://leanpub.com/cakephpunittesting

Symfony2 - Tests with FOSUserBundle

i would write a test for Symfony2 with FOSUserBundle.
At the moment i tried some ways and no one works.
I need a function like "createAuthClient".
Here is my basic class.
I post it because you could understand my problem better.
<?php
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Component\BrowserKit\Cookie;
class WebTestMain extends WebTestCase
{
protected static $container;
static protected function createClient(array $options = array(), array $server = array())
{
$client = parent::createClient($options, $server);
self::$container = self::$kernel->getContainer();
return $client;
}
static function createAuthClient(array $options = array(), array $server = array())
{
// see lines below with my tries
}
}
First try:
if(static::$kernel === null)
{
static::$kernel = static::createKernel($options);
static::$kernel->boot();
}
if(static::$container === null)
{
self::$container = self::$kernel->getContainer();
}
$parameters = self::$container->getParameter('test');
$server = array_merge(array('HTTP_HOST' => $parameters['host'], 'HTTP_USER_AGENT' => $parameters['useragent']), $server);
$client = self::createClient($options, $server);
$userProvider = self::$container->get('fos_user.user_manager');
$user = $userProvider->findUserBy(array('id' => 1));
$client->getCookieJar()->set(new Cookie('MOCKSESSID', true));
$session = self::$kernel->getContainer()->get('session');
$token = new UsernamePasswordToken($user, null, 'main', $user->getRoles());
$client->getContainer()->get('security.context')->setToken($token);
$session->set('_security_main', serialize($token));
return $client;
Then i searched and searched...
My second try.
if(static::$kernel === null)
{
static::$kernel = static::createKernel($options);
static::$kernel->boot();
}
if(static::$container === null)
{
self::$container = self::$kernel->getContainer();
}
$parameters = self::$container->getParameter('test');
$server = array_merge(
array(
'HTTP_HOST' => $parameters['host'],
'HTTP_USER_AGENT' => $parameters['useragent'],
'PHP_AUTH_USER' => 'admin',
'PHP_AUTH_PW' => 'admin'
),
$server
);
$client = static::createClient(array(), array());
$client->followRedirects();
return $client;
And here is my last try before i post this question...
$client = self::createClient($options, $server);
$parameters = self::$container->getParameter('test');
$server = array_merge(
array(
'HTTP_HOST' => $parameters['host'],
'HTTP_USER_AGENT' => $parameters['useragent']
),
$server
);
$client->setServerParameters($server);
$usermanager = self::$container->get('fos_user.user_manager');
$testuser = $usermanager->createUser();
$testuser->setUsername('test');
$testuser->setEmail('test#mail.org');
$testuser->setPlainPassword('test');
$usermanager->updateUser($testuser);
return $client;
Thank you in Advance.
The best way I have found to test with an authenticated user is to just visit your login page and submit the form with user name and password you have loaded from a fixture. This may seem slow and cumbersome but will test what the user will actually do. You can even create your own method to make using it quick and easy.
public function doLogin($username, $password) {
$crawler = $this->client->request('GET', '/login');
$form = $crawler->selectButton('_submit')->form(array(
'_username' => $username,
'_password' => $password,
));
$this->client->submit($form);
$this->assertTrue($this->client->getResponse()->isRedirect());
$crawler = $this->client->followRedirect();
}
Create an AbstractControllerTest and create an authorized client on setUp() as follow:
<?php
// ...
use Symfony\Component\BrowserKit\Cookie;
abstract class AbstractControllerTest extends WebTestCase
{
/**
* #var Client
*/
protected $client = null;
public function setUp()
{
$this->client = $this->createAuthorizedClient();
}
/**
* #return Client
*/
protected function createAuthorizedClient()
{
$client = static::createClient();
$container = $client->getContainer();
$session = $container->get('session');
/** #var $userManager \FOS\UserBundle\Doctrine\UserManager */
$userManager = $container->get('fos_user.user_manager');
/** #var $loginManager \FOS\UserBundle\Security\LoginManager */
$loginManager = $container->get('fos_user.security.login_manager');
$firewallName = $container->getParameter('fos_user.firewall_name');
$user = $userManager->findUserBy(array('username' => 'REPLACE_WITH_YOUR_TEST_USERNAME'));
$loginManager->loginUser($firewallName, $user);
// save the login token into the session and put it in a cookie
$container->get('session')->set('_security_' . $firewallName,
serialize($container->get('security.context')->getToken()));
$container->get('session')->save();
$client->getCookieJar()->set(new Cookie($session->getName(), $session->getId()));
return $client;
}
}
NOTE: Please, replace the username with your test username.
Then, extends the AbstractControllerTest and use the global $client to make requests as follow:
class ControllerTest extends AbstractControllerTest
{
public function testIndexAction()
{
$crawler = $this->client->request('GET', '/admin/');
$this->assertEquals(
Response::HTTP_OK,
$this->client->getResponse()->getStatusCode()
);
}
}
This method tested and works fine