How to create a phpUnit test for images? - php

I have a simple fileupload (image upload) which resizes this image to a smaller one. To create a phpUnit test for this resize function I need a mock object which represents an image.
Is the best practice to use a 'testimage' or is there a better way??

<?php
namespace tests\control;
use SebastianBergmann\CodeCoverage\TestCase;
class FileHandlerTest extends TestCase
{
/**
* #var array
*/
private $files = [];
/**
* Put your temp image into your filesystem.
* (Not good as unit tests must work with any system,
* but my research about mocking resources gave me nothing)
*/
protected function setUp()
{
if (file_exists(dirname(__FILE__) . '/testImage.jpg')) {
try {
rmdir(dirname(__FILE__) . '/testImage.jpg');
} catch (\Exception $e) {
throw new \Exception('You have no permission to delete files in this directory:' . $e);
}
} else {
$image = imagecreate(500, 500);
try {
imagejpeg($image, dirname(__FILE__) . '/testImage.jpg');
} catch (\Exception $e) {
throw new \Exception('You have no permission to create files in this directory:' . $e);
}
}
$this->files[] = '/testImage.jpg';
}
public function testOperation()
{
$example = new \FileHandler($this->files, dirname(__FILE__));
/**
* Set here all the variable inside your file,
* your code isn't working at this moment.
* Properties to set: uploadFolder, files
*/
$example->process();
/**
* Get you new image from test dir here.
* It's yours TODO :p
*/
/** #var resource $newPicture */
$size = getimagesize($newPicture);
/**
* also count it yourself, as I can't reproduce your code
*/
$this->assertEquals('200', $size[0]);
$this->assertEquals('200', $size[1]);
}
/**
* Deleting of test images. No try/catch here as it would fire on setup
*/
protected function tearDown()
{
if (file_exists(dirname(__FILE__) . '/testImage.jpeg')) {
rmdir(dirname(__FILE__) . '/testImage.jpeg');
}
if (file_exists(dirname(__FILE__) . '/newImage.jpeg')) {
rmdir(dirname(__FILE__) . '/newImage.jpeg');
}
}
}
You still have to make some work about it. And I'd prefer not to test such things. SetUp-method's comment is about it.

Related

Possibility to return child method from parent class method?

As I am pretty new to Laravel I am currently using the queue and have the following issue with a job.
Because the API I am calling can throttle, I need to have that logic for every method I call, so I created a parent (base) class. (not sure if this is the right way, please correct me if wrong here as well)
So I have the JobClass that extends the BaseJobClass, which should handle creation of the API client.
BaseJob
class BaseJob implements ShouldQueue
{
protected function performActionOrThrottle($method, $parameters, Customer $customer, Marketplace $marketplace) {
$client = $this->createClient($customer, $marketplace);
if (!method_exists($client, $method)) {
return $this->fail(new \Exception("Method {$method} does not exist in " . get_class($client)));
}
try {
$result = $client->{$method}($parameters);
} catch (\Exception $exception)
{
echo $exception->getMessage().PHP_EOL;
return $this->release(static::THROTTLE_LIMIT);
}
return $result;
}
}
Job
class Job extends BaseJob
{
CONST THROTTLE_LIMIT = 60;
CONST RETRY_DELAY = 10;
private $customer;
private $requestId;
/**
* Create a new job instance.
*
* #return void
*/
public function __construct(Customer $customer, $requestId = null)
{
$this->customer = $customer;
$this->requestId = $requestId;
echo "Updating Inventory for {$customer->name}\n";
}
/**
* Execute the job.
*
* #return void
*/
public function handle()
{
$marketplace = Marketplace::findOrFail(5);
if (!$report = $this->performActionOrThrottle('GetReport', $this->requestId, $this->customer, $marketplace)) {
echo "Report not available, trying again in " . self::RETRY_DELAY;
return $this->release(self::RETRY_DELAY);
}
...
handle Data
...
return;
}
}
In order do end the job, I need to return the handle() method. How can I return the handle method from the parent method without having to check for the return value and implementing to return it? If I would to so, there is no point for having that parent that should contain all the logic I need for several jobs.

Testing your code for multiple browsers using phpunit & php-webdrivers by facebook

I am working with php-webdrivers by facebook for selenium to implement integration testing for my site. I am using phphunit to run my tests and have wrapped my code inside the phpunitframework_testcase extended class:
class WebTest extends PHPUnit_Framework_TestCase
{
/**
* #var \RemoteWebDriver
*/
protected $driver;
protected $url='http://example.com';
protected $screenShotDirectoryPath = '/screenshots/';
/**
* #BeforeMethod
*/
protected function setUp()
{
try{
$capabilities = array( WebDriverCapabilityType::BROWSER_NAME=>WebDriverBrowserType::FIREFOX );
$this->driver = RemoteWebDriver::create('http://localhost:4444/wd/hub', $capabilities, 5000);
} catch (Exception $e){
echo $e->getMessage();
}
}
/**
* #AfterMethod
*/
public function tearDown()
{
$this->driver->close();
}
public function testImageUpload()
{
$errorSnaps='';
$myBrowserDriver = $this->driver;
//open the url
$myBrowserDriver->get($this->url);
try{
//get email field in login page
$emailField = $myBrowserDriver->findElement(WebDriverBy::id('email'));
//check if the field is displayed
if(!$emailField->isDisplayed()) {
try {
$errorSnaps = $this->takeScreenshot();
$this->errorLogs[] = "The email input Element is not present, a screen-shot of the error has been placed here --> " . $errorSnaps;
} catch (Exception $e){
$this->errorLogs[] = $e->getMessage();
}
}
} catch (NoSuchElementException $nse){
try {
$errorSnaps = $this->TakeScreenshot();
$this->errorLogs[] = "The email field on ".$this->driver->getCurrentURL()." not found , a screen-shot for the error has been placed here -->" . $errorSnaps;
}catch (Exception $e){
$this->errorLogs[]=$e->getMessage();
}
}
}
php-webdrivers documentation recommends this way to initialize the browser drivers
$capabilities=array(
\WebDriverCapabilityType::BROWSER_NAME=>WebDriverBrowserType::FIREFOX
);
$this->driver = RemoteWebDriver::create('http://localhost:4444/wd/hub', $capabilities, 5000);
but does not provide a mechanism to init multiple browser drivers to run my tests with only single test file means considering the code above I have to make different copies for all those browsers and with only one line code difference means if i want to run the test above for chrome then i have to change the line from
$capabilities=array(\WebDriverCapabilityType::BROWSER_NAME=>WebDriverBrowserType::FIREFOX);
to
$capabilities=array(\WebDriverCapabilityType::BROWSER_NAME=>WebDriverBrowserType::CHROME);
and save that code with rest all the same code as above in a different file and run my test suit. As you can see this is not an optimal way for implementing my tests and for the sake of code re-usability.
I came across 2 options:
Pass argument from the terminal sending the browser name with parameter like phpunit brName=chrome and getting it via $_SERVER['brName']. I would still have to type in each time I want to run tests for any other browser.
I came across the TestDecorator class below on phpunit site which looked like a more conventional way to achieve what I am doing but could not figure out how would I use it to run my tests.
Where should I put my code so that it detects and runs it? Every time I try to run the below sample code it says no tests were executed. If i have sample test function below how could I run it 4 times using the testdecorator as base class?
Sample Test:
public function sampleTest(){
$this->assertTrue(TRUE);
}
Test Decorator class:
require_once 'PHPUnit/Extensions/TestDecorator.php';
class PHPUnit_Extensions_RepeatedTest extends PHPUnit_Extensions_TestDecorator
{
private $timesRepeat = 1;
public function __construct(PHPUnit_Framework_Test $test, $timesRepeat = 1)
{
parent::__construct($test);
if (is_integer($timesRepeat) &&
$timesRepeat >= 0) {
$this->timesRepeat = $timesRepeat;
}
}
public function count()
{
return $this->timesRepeat * $this->test->count();
}
public function run(PHPUnit_Framework_TestResult $result = NULL)
{
if ($result === NULL) {
$result = $this->createResult();
}
for ($i = 0; $i < $this->timesRepeat && !$result->shouldStop(); $i++) {
$this->test->run($result);
}
return $result;
}
}
I was not going in the right direction to implement this scenario I should not use the testdecorator class but a more good and easy approach would be.
Setting an environment variable via cmd-line/terminal like
export BROWSER_REQUESTED=chrome for (unix) and set BROWSER_REQUESTED=chrome for (windows),or you can create a .sh file with the following code
export BROWSER_REQUESTED=chrome && phpunit and run it via cmd-line.
Create a BrowserFatory class which listens to the environment variable and initiates the drivers for the browser type requested.
create as much .sh files as much browsers you need to run the test for, i have added the code below to be more descriptive.
WebTest.php
class WebTest extends PHPUnit_Framework_TestCase
{
/**
* #var \RemoteWebDriver
*/
protected $driver;
protected $url='http://example.com';
protected $screenShotDirectoryPath = '/screenshots/';
/**
* #BeforeMethod
*/
protected function setUp()
{
try{
$this->driver = BrowserFactory::drivers();
} catch (Exception $e){
echo $e->getMessage();
}
}
}
BrowserFactory.php
class BrowserFactory
{
public static function drivers()
{
switch ($_SERVER['BROWSER_REQUESTED']) {
case 'chrome':
return self::createChrome();
break;
case "ie":
throw new Exception('Not implemented');
break;
case 'firefox':
default:
return self::createFirefox();
break;
}
}
public static function createChrome()
{
putenv("webdriver.chrome.driver=/path/to/chromedriver");
$service = ChromeDriverService::createDefaultService();
$service->start();
return ChromeDriver::start(DesiredCapabilities::chrome(), $service);
}
public static function createFirefox()
{
// these are just constants defined in bootstrap.php
$seleniumUrl = isset($_SERVER['JENKINS_URL']) ? TEST_ENV_SELENIUM_SERVER : LOCAL_ENV_SELENIUM_SERVER;
return RemoteWebDriver::create(
$seleniumUrl, DesiredCapabilities::firefox()
);
}
}
command-line.sh
export BROWSER_REQUESTED='chrome' && phpunit

spl_autoload with namespace php

I am makeing a small framework for scheduled job that are run by a nodejs external process. I would like to use an auto loader but for some reason data is not getting to it. I am also using namespaces. Here is what my folder structure looks like:
Library
|_ Core
|_ JobConfig.php
|_ Utilities
|_ Loader.php
|_ Scheduled
|_ SomeJob.php
|_ Config.php
My Config.php just has some definitions and an instance of my Loader.php.
Loader.php looks like:
public function __constructor()
{
spl_autoload_register(array($this, 'coreLoader'));
spl_autoload_register(array($this, 'utilitiesLoader'));
}
private function coreLoader($class)
{
echo $class;
return true;
}
private function utilitiesLoader($lass)
{
echo $class;
return true;
}
So for my SomeJob.php I am including Config.php and then try to instantiate JobConfig.php when it fails. My namespaces look like Library\Core\JobConfig and so on. I am not sure if this is the best approach without the ability to bootsrapt things. But I am not seeing the echo's from the loader before it fails.
Edit:
I tried the sugestion by #Layne but did not work. I am still getting a class not found and seems like the class in not getting in the spl stack. Here is a link to the code
If you actually use namespaces in the same way you're using your directory structure, this should be rather easy.
<?php
namespace Library {
spl_autoload_register('\Library\Autoloader::default_autoloader');
class Autoloader {
public static function default_autoloader($class) {
$class = ltrim($class, '\\');
$file = __DIR__ . '/../';
if ($lastNsPos = strrpos($class, '\\')) {
$namespace = substr($class, 0, $lastNsPos);
$class = substr($class, $lastNsPos + 1);
$file .= str_replace('\\', '/', $namespace) . '/';
}
$file .= $class . '.php';
include $file;
}
}
}
Put that into your Library directory and require it on a higher level. Hopefully I didn't mess that one up, didn't test it.
EDIT: Fixed path.
use this class to index of your project, just replace WP_CONTENT_DIR with another directory level to scan php files, this class automatically include files:
<?php
class autoloader
{
public static function instance()
{
static $instance = false;
if( $instance === false )
{
// Late static binding
$instance = new static();
}
return $instance;
}
/**
* #param $dir_level directory level is for file searching
* #param $php_files_json_name name of the file who all the PHP files will be stored inside it
*/
private function export_php_files($dir_level, $php_files_json_name)
{
$filePaths = array(mktime());
/**Get all files and directories using iterator.*/
$iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir_level));
foreach ($iterator as $path) {
if (is_string(strval($path)) and pathinfo($path, PATHINFO_EXTENSION) == 'php') {
$filePaths[] = strval($path);
}
}
/**Encode and save php files dir in a local json file */
$fileOpen = fopen($dir_level . DIRECTORY_SEPARATOR . $php_files_json_name, 'w');
fwrite($fileOpen, json_encode($filePaths));
fclose($fileOpen);
}
/**
* #param $php_files_json_address json file contains all the PHP files inside it
* #param $class_file_name name of the class that was taken from #spl_autoload_register_register plus .php extension
* #return bool Succeeding end of work
*/
private function include_matching_files($php_files_json_address, $class_file_name)
{
static $files;
$inc_is_done = false;
if ($files == null) {
$files = json_decode(file_get_contents($php_files_json_address), false);
}
/**Include matching files here.*/
foreach ($files as $path) {
if (stripos($path, $class_file_name) !== false) {
require_once $path;
$inc_is_done = true;
}
}
return $inc_is_done;
}
/**
* #param $dir_level directory level is for file searching
* #param $class_name name of the class that was taken from #spl_autoload_register
* #param bool $try_for_new_files Try again to include new files, that this feature is #true in development mode
* it will renew including file each time after every 30 seconds #see $refresh_time.
* #return bool Succeeding end of work
*/
public function request_system_files($dir_level, $class_name, $try_for_new_files = false)
{
$php_files_json = 'phpfiles.json';
$php_files_json_address = $dir_level . DIRECTORY_SEPARATOR . $php_files_json;
$class_file_name = $class_name . '.php';
$files_refresh_time = 30;
/**Include required php files.*/
if (is_file($php_files_json_address)) {
$last_update = json_decode(file_get_contents($php_files_json_address), false)[0];
if ((mktime() - intval($last_update)) < $files_refresh_time || !$try_for_new_files) {
return $this->include_matching_files($php_files_json_address, $class_file_name);
}
}
$this->export_php_files($dir_level, $php_files_json);
return $this->include_matching_files($php_files_json_address, $class_file_name);
}
/**
* Make constructor private, so nobody can call "new Class".
*/
private function __construct()
{
}
/**
* Make clone magic method private, so nobody can clone instance.
*/
private function __clone()
{
}
/**
* Make sleep magic method private, so nobody can serialize instance.
*/
private function __sleep()
{
}
/**
* Make wakeup magic method private, so nobody can unserialize instance.
*/
private function __wakeup()
{
}
}
/**
* Register autoloader.
*/
try {
spl_autoload_register(function ($className) {
$autoloader = autoloader::instance();
return $autoloader->request_system_files(WP_CONTENT_DIR, $className, true);
});
} catch (Exception $e) {
var_dump($e);
die;
}

Unable to catch a global exception

Monolog's StreamHandler throws a \UnexpectedValueException if there's a problem with the specified log file.
In my code I'm trying to catch \UnexpectedValueException but I've been not able to.
My code is:
<?php
namespace myNamespace;
use Monolog\Logger as Monolog;
use Monolog\Handler\StreamHandler;
class Logger {
private static $instance;
private static function init()
{
if (!self::$instance)
{
$logger = new Monolog(MY_LOG_CHANNEL);
try
{
$logger->pushHandler(new StreamHandler(MY_LOG_PATH . "/" .
MY_LOG_NAME, Monolog::NOTICE));
}
catch (\UnexpectedValueException $e)
{
writeln("Error starting logger" . $e->getMessage());
die;
}
// and so on
It doesn't work, I get this:
Fatal error: Uncaught exception 'UnexpectedValueException' with message 'The stream or
file [somefile.log] could not be opened: failed to open stream: No such file or
directory' in [path to src/Monolog/Handler/StreamHandler.php ]
As I understand it they have escaped to the global namespace so I should be able to pick it up there. Why can't I? I've tried all combinations of namespace \myNamesace\UnexpectedValueException even Monolog\UnexpectedValueException global or local, to no avail.
Clearly I'm missing something, what is it please?
Edit:
In another class I'm doing an if (file_exists($fileName)) /** do file_get_contents() etc */ else Logger::error($filename . "does not exist")
The error is being triggered within Logger::error() when I call self::init()
The error is caused because I have (purposely) munged the log file path, if it's a valid log file path then the code runs fine. Clearly I want to catch that error, hence the try/catch.
The next line in the trace is the line in the code above: $logger->pushHandler(new StreamHandler(MY_LOG_PATH . "/" . MY_LOG_NAME, Monolog::NOTICE));
Interestingly, the only other place I'm catching an exception (this is just scaffolding code at the moment, no business logic yet) is within the /** do file_get_contents() etc */ bit if I purposely mis-spell the filename var, file_get_contents barfs and a standard
catch (Exception $e) works as I'd expect around the file_get_contents()
This is a pretty old question which probably got resolved in the meanwhile. However, for the sake of future visitors, I would like to point out that the exception is thrown when a log entry will be written to the given file, not at construction time.
The way you have the try { } catch () {} setup, the exception is expected at the creation of StreamHandler. However, the real exception happens whenever you try to send to the log via call like $logger->error("Error message") so you should be catching exceptions there.
On another note, I think throwing exceptions from a logging library is one of the silliest things that one could do. Logging should be idempotent and not affect the state of the running application.
In an old silex application, I also ran into this issue. I want to log to a logstash instance, yet it should not break if logstash isn't available.
Luckily, my application used the logger only typed against the Psr\Log\LoggerInterface, so I could write a decorator preventing the exceptions to break the app at one place, instead of adding a try and catch call on every call within the codebase.
It looks like this:
<?php
namespace Dreamlines\DirectBookingForm\ServiceProvider;
use Psr\Log\LoggerInterface;
/**
* DontThrowLoggerDecorator
*
* Monolog will break on info, error, etc. calls
* This decorator wrap the LoggerInterface and ensures that failure to log won't break the app
**/
class DontThrowLoggerDecorator implements LoggerInterface
{
/**
* #var LoggerInterface
*/
private $logger;
public function __construct(LoggerInterface $logger)
{
$this->logger = $logger;
}
/**
* #inheritDoc
*/
public function emergency($message, array $context = array())
{
try {
$this->logger->emergency($message, $context);
} catch (\Exception $e) {
}
}
/**
* #inheritDoc
*/
public function alert($message, array $context = array())
{
try {
$this->logger->alert($message, $context);
} catch (\Exception $e) {
}
}
/**
* #inheritDoc
*/
public function critical($message, array $context = array())
{
try {
$this->logger->critical($message, $context);
} catch (\Exception $e) {
}
}
/**
* #inheritDoc
*/
public function error($message, array $context = array())
{
try {
$this->logger->error($message, $context);
} catch (\Exception $e) {
}
}
/**
* #inheritDoc
*/
public function warning($message, array $context = array())
{
try {
$this->logger->warning($message, $context);
} catch (\Exception $e) {
}
}
/**
* #inheritDoc
*/
public function notice($message, array $context = array())
{
try {
$this->logger->notice($message, $context);
} catch (\Exception $e) {
}
}
/**
* #inheritDoc
*/
public function info($message, array $context = array())
{
try {
$this->logger->info($message, $context);
} catch (\Exception $e) {
}
}
/**
* #inheritDoc
*/
public function debug($message, array $context = array())
{
try {
$this->logger->debug($message, $context);
} catch (\Exception $e) {
}
}
/**
* #inheritDoc
*/
public function log($level, $message, array $context = array())
{
try {
$this->logger->log($level, $message, $context);
} catch (\Exception $e) {
}
}
}
and I just wrap my logger instance around this once, in my silex application I am doing it like this:
$app['NOT_BREAKING_LOGGER'] = $app->share(
function () use ($app) {
return new DontThrowLoggerDecorator($app['monolog']);
}
);
and I am injecting the NOT_BREAKING_LOGGER in each of my services instead of the monolog one.

Php empty file output in flat file ORM class

Background :
I am trying to create a flat file key value store. When i use the code below the array that has been written to the file gets deleted and i end up with an empty file. On the next request the file gets populated with the array. This process loops itself on every page request.
What i have tried :
Various methods with "json_encode()"/"json_decode()".
Also tried to do the same with "serialize()"/"unserialize()".
Tried various coding structures with "array_merge()" and "array_merge_recursive()".
Nothing seems to work! I end up with the same loop of an empty file -> file with array and it continues
Question :
Can someone more experienced tell me what i am doing wrong?
code :
/**
* Class Orm
*/
class Orm
{
/**
* #var string The Root database directory with a trailing slash. default is "./database/".
*/
static $dir = "./database/";
/**
* #var array
*/
protected $data;
/**
* #var string
*/
protected $file = "";
/**
* #param $file
* #return string
*/
public function load_table($file)
{
try {
if (file_exists(self::$dir . $file)) {
return $this->file = $file;
} else {
throw new Exception("The file " . $file . " cannot be created, because it already exists");
}
} catch (Exception $error) {
return $error->getMessage();
}
}
/**
* #param String
* #param array $values An associative array of values to store.
* #return array
*/
public function set($key, $values = array())
{
try{
if (!empty($key) && !empty($values)){
return $this->data[$key] = $values;
} else {
throw new Exception();
}
} catch (Exception $error){
return $error->getMessage();
}
}
public function save()
{
try{
if (file_exists(self::$dir . $this->file)) {
if (filesize(self::$dir . $this->file) == 0)
{
file_put_contents(self::$dir . $this->file, print_r($this->data, TRUE));
}else{
$tmp = file_get_contents(self::$dir . $this->file);
$content = array_merge($tmp, $this->data);
file_put_contents(self::$dir . $this->file, print_r($content, TRUE));
}
} else {
throw new Exception();
}
} catch(Exception $error){
return $error->getMessage();
}
}
}
$user = new Orm();
$user->load_table("users");
$user->set("Tito",array("age" => "32", "occupation" => "cont"));
$user->save();
PS : I thought this would be a nice project to familiarize myself with Php. So please do not advise to use SQL as this is for learning and understanding Php only.
I can not say why your code fails to do that or not. However I would create another object as well that takes care of loading and saving a string to disk. Nothing more and nothing less:
class StringStore
{
private $path;
public function __construct($path) {
$this->path = $path;
}
/**
* #return string
*/
public function load() {
... move your load code in here
return $buffer;
}
/**
* #param string $buffer
*/
public function save($buffer) {
... move your save code in here
}
}
That might look a bit less, however you can move a larger part of code out of the ORM class. If the problem is with storing to disk (e.g. maybe some mistake made?) you can fix it in the store class then.
If the mistake has been made in the string processing, then you know you need to look in the ORM class instead to continue fixing.

Categories