I wrote a DataTest case following the example 4.5 of PHPUnit manual, the url is: http://www.phpunit.de/manual/3.6/en/writing-tests-for-phpunit.html#writing-tests-for-phpunit.data-providers. But I came across with an error:
The data provider specified for DataTest::testAdd is invalid.
Data set #0 is invalid.
I thought it maybe that I edit the data.csv file in a wrong way, then I used php function fputcsv() to create data.csv file, but it also didn't work, I want to know why, and how to resolve this problem. Thanks!
P. S.: the data in data.csv is:
0,0,0
0,1,1
The codes are show as follows:
DataTest.php
require 'CsvFileIterator.php';
class DataTest extends PHPUnit_Framework_TestCase
{
public function provider()
{
return new CsvFileIterator('data.csv');
}
/**
* #dataProvider provider
*/
public function testAdd($a, $b, $c)
{
$this->assertEquals($c, $a + $b);
}
}
CsvFileIterator.php
class CsvFileIterator implements Iterator
{
protected $file;
protected $key = 0;
protected $current;
public function __construct($file)
{
$this->file = fopen($file, 'r');
}
public function __destruct()
{
fclose($this->file);
}
public function rewind()
{
rewind($this->file);
$this->current = fgetcsv($this->file);
$this->key = 0;
}
public function valid()
{
return !feof($this->file);
}
public function key()
{
return $this->key;
}
public function current()
{
return $this->current;
}
public function next()
{
$this->current = fgetcsv($this->file);
$this->key++;
}
}
The data.csv file is create by function fputcsv():
$data = array(
array(0, 0, 0),
array(0, 1, 1)
);
$fp = fopen('data.csv', 'w');
foreach($data as $v)
{
fputcsv($fp, $v);
}
fclose($fp);
Example :-)
/**
* #dataProvider provider
* #group csv
*/
public function testAdd($a, $b, $c)
{
$this->assertEquals($c, $a + $b);
}
/**
* #return array
*/
public function provider()
{
$file = file_get_contents("/Volumes/htdocs/contacts.csv","r");
foreach ( explode("\n", $file, -1) as $line )
{
$data[] = explode(',', $line);
}
return $data;
}
/*
* CREATE TO CSV FILE DATAPROVIDER
* don't create this file in your test case
*/
public function saveToCsv()
{
$list = array(
array(0,0,0),
array(0,1,1)
);
$file = fopen("/Volumes/htdocs/contacts.csv","w");
foreach ($list as $line)
{
fputcsv($file,$line);
}
fclose($file);
}
#ZongshuLin Similar issue here. Possible Solutions:
Check this data.csv. Basically I had to add a row after the last line.
You can also check my approach on GitHub when specifying the data.csv location
Use DIRECTORY_SEPARATOR constant so the script may run on any OS--Windows uses backslashes while Linux slashes.
/**
* #return CsvFileIterator
*/
public function additionProvider()
{
return new CsvFileIterator(__DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'storage/data.csv');
}
Related
For some logging/monitoring i would like to get the current route path including placeholders.
If my route is /blog/{slug} and the request is to http://localhost/blog/foobar what i need is "/blog/{slug}"
In request listeners this value seem to not be inside the request object. I only find the resolved path which i am not interested in.
In Compiler passes I have the issue that any Router related service I try to get from the ContainerBuilder returns an exception. If i had the
What is a clean way to obtain this?
For routes with one param:
$routeWithoutParam = substr($request->getRequestUri(),0,strrpos($request->getRequestUri(),'/'));
$routeParams = $request->attributes->get('_route_params');
$routeDefinition = $routeWithoutParam.'/{' . array_key_first($paramsArray) . '}';
echo $routeDefinition;
For routes with multiple params:
$routeParams = $request->attributes->get('_route_params');
$routeWithoutParam = '';
for ($i = 0; $i < count($routeParams); $i++) {
$routeWithoutParam = $i === 0
? substr($request->getRequestUri(), 0, strrpos($request->getRequestUri(), '/'))
: substr($routeWithoutParam, 0, strrpos($routeWithoutParam, '/'))
;
}
$routeDefinition = $routeWithoutParam.'/';
foreach (array_keys($routeParams) as $key => $param) {
$routeDefinition.= '{' . $param . '}' . ($key < count($routeParams)-1 ? '/' : '');
}
echo $routeDefinition;
You can obtain the Router and RouteCollection, that holds all the routes in your app:
// src/Listener/RouteLoggerListener.php -- namespace and use ommited
class RouteLoggerListener implements EventSubscriberInterface
{
/**
* #var LoggerInterface
*/
private $logger;
/**
* #var RouterInterface
*/
private $router;
public function __construct(LoggerInterface $logger, RouterInterface $router)
{
$this->logger = $logger;
$this->router = $router;
}
public static function getSubscribedEvents()
{
// Trigger after the RouterListener
return [KernelEvents::REQUEST => ['onKernelRequest', 50]];
}
public function onKernelRequest(RequestEvent $event)
{
// This is for sf53 and up, for previous versions, use isMasterRequest()
if (!$event->isMainRequest()) {
return;
}
$request = $event->getRequest();
if (null == $request) {
return;
}
$matchedRoute = $request->attributes->get('_route');
if (null == $matchedRoute) {
// Bail gracefully
return;
}
$routeCollection = $this->router->getRouteCollection();
$route = $routeCollection->get($matchedRoute);
$this->logger->debug('Request route: '.$route->getPath());
}
}
Note: Using RouteCollection at runtime is highly discouraged because it triggers a recompile. As indicated in this GitHub comment, the proper way to do it would be to use ConfigCache while cache warming.
Is there any way of creating testcases runtime by giving paramters?
Functionality of the test cases is same, just name and parameters will differ.
Like here is an example
public function testGetGiftsGivenVideo() {
$fileName = 'get-gifts-given-video.json';
$filePath = $this->path . $fileName;
$this->accessToken = $this->coreAPI->GetOAuthToken($this->loginFilePath);
$this->compareResults($filePath, $this->accessToken, true);
}
public function testGetGiftsReceivedAudio() {
$fileName = 'get-gifts-received-audio.json';
$filePath = $this->path . $fileName;
$this->accessToken = $this->coreAPI->GetOAuthToken($this->loginFilePath);
$this->compareResults($filePath, $this->accessToken, true);
}
public function testGetGiftsReceivedVideo() {
$fileName = 'get-gifts-received-video.json';
$filePath = $this->path . $fileName;
$this->accessToken = $this->coreAPI->GetOAuthToken($this->loginFilePath);
$this->compareResults($filePath, $this->accessToken, true);
}
Now all these functions are doing the same thing.
I see two possible solutions here:
As suggested by axiac in the comments, you can use data providers.
A data provider is a method returning an array of parameters to call a test method multiple times with different parameters. See the PHPUnit documentation about data providers for more information
/**
* #dataProvider provideGetGifts
* #param $filename
*/
public function testGetGifts($fileName) {
$filePath = $this->path . $fileName;
$this->accessToken = $this->coreAPI->GetOAuthToken($this->loginFilePath);
$this->compareResults($filePath, $this->accessToken, true);
}
public function provideGetGifts()
{
return array(
array('get-gifts-given-video.json'),
array('get-gifts-received-audio.json'),
array('get-gifts-received-video.json'),
);
}
The second solution, if you want to have different method names, is to simply use a second method which includes some logic of the test:
protected function compareAccessTokenWithFilePath($fileName) {
$filePath = $this->path . $fileName;
$this->accessToken = $this->coreAPI->GetOAuthToken($this->loginFilePath);
$this->compareResults($filePath, $this->accessToken, true);
}
public function testGetGiftsGivenVideo() {
$this->compareAccessTokenWithFilePath('get-gifts-given-video.json');
}
public function testGetGiftsReceivedAudio() {
$this->compareAccessTokenWithFilePath('get-gifts-given-audio.json');
}
public function testGetGiftsReceivedVideo() {
$this->compareAccessTokenWithFilePath('get-gifts-received-video.json');
}
Personally, I'd prefer the first solution. Its a little bit clearer.
I am getting the following error when trying to upload a file, it's odd because I've used the same code on other projects without any problems/errors.
What am I missing here?
Notice: Undefined property: Acme\DemoBundle\Entity\Article::$file in /var/www/html/InsideFight/src/Acme/DempBundle/Entity/Article.php line 277
The problem line is:
if (null !== $this->file) {
I do not have any file upload code in my controller it's being handled in the entity.
Entity
public $file;
public function getUploadDir()
{
return 'images/';
}
public function getUploadRootDir()
{
return __DIR__ . '/../../../../web/' . $this->getUploadDir();
}
public function getWebPath()
{
return null === $this->image ? null : $this->getUploadDir() . '/' . $this->image;
}
public function getAbsolutePath()
{
return null === $this->image ? null : $this->getUploadRootDir() . '/' . $this->image;
}
/**
* #ORM\PrePersist()
* #ORM\PreUpdate()
*/
public function preUpload()
{
if (null !== $this->file) {
$this->image = uniqid() . '.' . $this->file->guessExtension();
}
}
/**
* #ORM\PostPersist()
* #ORM\PostUpdate()
*/
public function upload()
{
if (null === $this->file) {
return;
}
// If there is an error when moving the file, an exception will
// be automatically thrown by move(). This will properly prevent
// the entity from being persisted to the database on error
$this->file->move($this->getUploadRootDir(), $this->image);
unset($this->file);
}
/**
* #ORM\PostRemove()
*/
public function removeUpload()
{
if ($file = $this->getAbsolutePath()) {
unlink($file);
}
}
Thats because you do unset($this->file);. Change it to $this->file = null.
Include the following namespace.
use Symfony\Component\HttpFoundation\File\UploadedFile;
Make the file variable private and create a temp file variable.
private $file;
private $tempFile
Then create getter and setter methods for $file.
public function getFile()
{
return $this->file;
}
public function setFile(UploadedFile $file = null)
{
$this->file = $file;
if (isset($this->image)) {
// store the old name to delete after the update
$this->tempfile = $this->image;
$this->image = null;
} else {
$this->image = 'initial';
}
}
Then, modify preUpload and upload functions.
public function upload()
{
if (null === $this->getFile()) {
return;
}
// if there is an error when moving the file, an exception will
// be automatically thrown by move(). This will properly prevent
// the entity from being persisted to the database on error
$this->getFile()->move($this->getUploadRootDir(), $this->image);
// check if we have an old image
if (isset($this->tempFile)) {
// delete the old image
unlink($this->getUploadRootDir() . '/' . $this->tempFile);
// clear the temp image path
$this->tempFile = null;
}
$this->file = null;
}
public function preUpload()
{
if (null !== $this->getFile()) {
// generate a unique name
$filename = uniqid();
$this->image = $filename . '.' . $this->getFile()->guessExtension();
}
}
I have a postUpdate listener and I'd like to know what the values were prior to the update and what the values for the DB entry were after the update. Is there a way to do this in Symfony 2.1? I've looked at what's stored in getUnitOfWork() but it's empty since the update has already taken place.
You can use this ansfer Symfony2 - Doctrine - no changeset in post update
/**
* #param LifecycleEventArgs $args
*/
public function postUpdate(LifecycleEventArgs $args)
{
$changeArray = $args->getEntityManager()->getUnitOfWork()->getEntityChangeSet($args->getObject());
}
Found the solution here. What I needed was actually part of preUpdate(). I needed to call getEntityChangeSet() on the LifecycleEventArgs.
My code:
public function preUpdate(Event\LifecycleEventArgs $eventArgs)
{
$changeArray = $eventArgs->getEntityChangeSet();
//do stuff with the change array
}
Your Entitiy:
/**
* Order
*
* #ORM\Table(name="order")
* #ORM\Entity()
* #ORM\EntityListeners(
* {"\EventListeners\OrderListener"}
* )
*/
class Order
{
...
Your listener:
class OrderListener
{
protected $needsFlush = false;
protected $fields = false;
public function preUpdate($entity, LifecycleEventArgs $eventArgs)
{
if (!$this->isCorrectObject($entity)) {
return null;
}
return $this->setFields($entity, $eventArgs);
}
public function postUpdate($entity, LifecycleEventArgs $eventArgs)
{
if (!$this->isCorrectObject($entity)) {
return null;
}
foreach ($this->fields as $field => $detail) {
echo $field. ' was ' . $detail[0]
. ' and is now ' . $detail[1];
//this is where you would save something
}
$eventArgs->getEntityManager()->flush();
return true;
}
public function setFields($entity, LifecycleEventArgs $eventArgs)
{
$this->fields = array_diff_key(
$eventArgs->getEntityChangeSet(),
[ 'modified'=>0 ]
);
return true;
}
public function isCorrectObject($entity)
{
return $entity instanceof Order;
}
}
You can find example in doctrine documentation.
class NeverAliceOnlyBobListener
{
public function preUpdate(PreUpdateEventArgs $eventArgs)
{
if ($eventArgs->getEntity() instanceof User) {
if ($eventArgs->hasChangedField('name') && $eventArgs->getNewValue('name') == 'Alice') {
$eventArgs->setNewValue('name', 'Bob');
}
}
}
}
I have created a file uploads page. In my controller I want to get the uploaded path of the view and add it in the database for a particular id. For that I want the path of the file ans send it to the repository. The problem when I am using in my controller
if ($request->getMethod() == 'POST')
{
$form->bind($request);
$file = $form["file"]->getData();
/* here it is giving the path like /tmp/phpkR4kgD */
$em = $this->getDoctrine()->getManager();
$user->upload();
}
this is my entity
/**
* Sets file.
*
* #param UploadedFile $file
*/
public function setFile(UploadedFile $file = null)
{
$this->file = $file;
}
public function upload()
{
if (null === $this->file)
{
return;
}
$this->file->move($this->getUploadRootDir(), $this->file->getClientOriginalName());
$this->path = $this->file->getClientOriginalName();
$this->file = null;
}
/**
* Get file.
*
* #return UploadedFile
*/
public function getFile()
{
return $this->file;
}
public function getAbsolutePath()
{
return null === $this->path
? null
: $this->getUploadRootDir() . DIRECTORY_SEPARATOR . $this->path;
}
public function getWebPath()
{
return null === $this->path
? null
: $this->getUploadDir() . DIRECTORY_SEPARATOR . $this->path;
}
protected function getUploadRootDir()
{
return __DIR__ . '/../../../../web/'. $this->getUploadDir();
}
protected function getUploadDir()
{
return 'uploads/';
}
I have created my uploads folder in web folder of symfony
while calling upload() method from it takes the temporary path to entity.
In entity it will get the orginal path $this->path = $this->file->getClientOriginalName();
so use return statement which returns the original path to controller from there you can save it in database...