I need help with PHPUnit and some methods. How should you guys write tests in PHPUnit to reach a high code coverage for the following properties and methods?
I'm pretty new to PHPUnit and could need some help. I've just write some test cases for more basic code. This class generates flash messages for the end user, and store it in a session.
Extremely grateful for some help. Any ideas?
private $sessionKey = 'statusMessage';
private $messageTypes = ['info', 'error', 'success', 'warning']; // Message types.
private $session = null;
private $all = null;
public function __construct() {
if(isset($_SESSION[$this->sessionKey])) {
$this->fetch();
}
}
public function fetch() {
$this->all = $_SESSION[$this->sessionKey];
}
public function add($type = 'debug', $message) {
$statusMessage = ['type' => $type, 'message' => $message];
if (is_null($this->all)) {
$this->all = array();
}
array_push($this->all, $statusMessage);
$_SESSION[$this->sessionKey] = $this->all;
}
public function clear() {
$_SESSION[$this->sessionKey] = null;
$this->all = null;
}
public function html() {
$html = null;
if(is_null($this->all))
return $html;
foreach ($this->all as $message) {
$type = $message['type'];
$message = $message['message'];
$html .= "<div class='message-" . $type . "'>" . $message . "</div>";
}
$this->clear();
return $html;
}
I have setup an setup-case, like this:
protected function setUp() {
$this->flash = new ClassName();
}
Also tried one test case:
public function testFetch() {
$this->assertEquals($this->flash->fetch(), "statusMessage", "Wrong session key.");
}
But gets an error message telling me: "Undefined variable: _SESSION"
If I then try:
public function testFetch() {
$_SESSION = array();
$this->assertEquals($this->flash->fetch(), "statusMessage", "Wrong session key.");
}
I get another error message telling: "Undefined index: statusMessage"
Try something like this:
function testWithoutSessionKey() {
$_SESSION = array();
$yourClass = new YourclassName();
$this->assertNull($yourClass->html()); }
function testWithSomeSessionKey() {
$_SESSION = array( 'statusMessage' => array(...));
$yourClass = new YourclassName();
$this->assertSame($expect, $yourClass->html());
}
You can't instantiate your class in the setup because your constructor need that the SESSION variable may exist(so you can test that can have some value inside).
You can evalutate (assert) only the ouptput of a method, so you can't assert that the message of the return of the method fetch.
In your method testFecth you have found a bug! Thanks to the test for this. Try fixing it with checking as you do in the construct :
public function fetch() {
if (isset($_SESSION[$this->sessionKey]))
$this->all = $_SESSION[$this->sessionKey];
}
Hope this help
Related
I am using php 7.4.9 and have a class which reads information from a file. These informations should be all the time availabe from outside the class and it also should possible to modify that array, so that this class can write back these information on request.
I have looked for a while but could not fined a useful solution.
I got the functions working, but the array loose the values from call to call.
Edit 2020/12/12
This is the uses structure of my code
<?php
.......
function show(){
$id3 = ID3::create();
$mp3 = &ID3::$mp3Array;
if($mode == "manual"){
if($file == ""){
return "";
}
$fName = $dir . "/" . $file;
$id3->open($fName);
.......
}else if($mode == "save"){
$fName = $dir . "/" . $file;
$id3->save($fName);
return "Save done!";
}
} // end of show
class ID3{
public static $mp3Array = array();
public static function create(): self {
static $object;
$object = $object ?? new self();
return $object;
}
function open($fName){
$mp3 = self::$mp3Array;
. // $mp3 will be filled
.........
}
function save($fName) {
$mp3 = &ID3::§mp3Array;
error_log("TagSave: ".var_export($mp3, true),0); // is always empty
foreach($mp3 as $key => $value){
........
}
}
} //end of class>
?>
If I try to save the modified array, it is always empty, if show is called again!
I have also implemented the #Logifire proposal 'create'. I got a valid pointer but the arrayis still empty.
Maybe I should point out, that it is web page. The html code sends information (form) back to the php program.
I figured out, that use of global $id3 = NULL; does not work, because the php grogramm will be always called and set the variable again to NULL each time.
I have also implemented the following code on the beginning
<?php
error_log("PHP call",0);
$id3count = 0;
if(array_key_exists("Test_id3",$GLOBALS)){
error_log("GLOBALS[Test_id3] exist!",0);
}else{
error_log("GLOBALS[Test_id3] does not exist!",0);
$GLOBALS['Test_id3'] = "NEW";
}
The $GLOBAL['Test_id3'] never exist, if the programm will be called!
I got the functions working, but the array loose the values from call to call.
As I understand you, your setup is not a long running app, you can not keep state between requests (calls).
But if you are aware of that, the issue may be you have a new instance of the class each time you call it within the same request flow, you may use a singleton if this is the case. I suggest using accessors in your class.
class MyDataList {
private array $my_array = [];
private function __construct()
{
}
public static function create(): self {
static $object;
$object = $object ?? new self();
return $object;
}
public function setArray(array $new_array): void {
$this->my_array = $new_array;
// open, write, close file..
}
public function getArray(): array {
return $this->my_array;
}
}
$my_data_list = MyDataList::create();
Based on your edited question (2020/12/12), I extended the example code:
class MyDataList {
private array $my_array = [];
private $file_path = '';
private function __construct()
{
}
public static function create(string $file_path): self {
static $object;
if ($object === null) {
$object = new self();
$stringified = file_get_contents($file_path) ?: '';
$array = json_decode($stringified, true) ?: [];
$object->file_path = $file_path;
$object->my_array = $array;
}
return $object;
}
public function setArray(array $new_array): void {
$this->my_array = $new_array;
$stringified = json_encode($new_array);
file_put_contents($this->file_path, $stringified);
}
public function getArray(): array {
return $this->my_array;
}
}
$my_data_list = MyDataList::create('/path/to/file');
Note: Be aware, you need to apply error handling
Comment answers:
Is the filepath connected to the array?
Well, you will write your data as JSON to a file each time you "modify" the array via the setArray()
Does it means, that the array is stored into a file and read out each time I try to connect again?
For each request you call create() it will instantiate the internal state of the array based on the stored data in the file. ATM. The file_get_contents call may have been wrapped and only called if the $object was not instantiated. (Now updated in the example)
So I have to call setArray($array); to save the data. I was looking for a soluting to keep the data without an management to save and read the array. Is this not possible with PHP?
Maybe you want to use a session variable to store your data? But it is individual per user and not long lived data - Link: https://www.php.net/manual/en/reserved.variables.session.php
In a standard PHP setup you can not have data/state between requests, but there are solution like Swoole which makes PHP a long running app: https://www.php.net/manual/en/book.swoole.php
I need a possibility to modify the array directly.
Is it a reference to the array you want? https://3v4l.org/OsBC6
class MyDataList {
private array $my_array = [];
private function __construct()
{
}
public static function create(): self {
static $object;
$object = $object ?? new self();
return $object;
}
public function setArray(array &$new_array): void {
$this->my_array = &$new_array;
}
public function getArray(): array {
return $this->my_array;
}
}
There is no easy way to do with PHP!
Finally I use the proposal from Logifire, but had to modified it to fullfill my requirements.
I needed more than 1 array.
One array can ibclude binary data values, which json can't handle. So I have to use base64 for the binary data values.
Here my code:
public array $mp3Array = array();
public array $findArray = array();
private $file_dir = "";
public static function create(string $fileDir): self {
static $object;
if ($object === null) {
$object = new self();
$stringified1 = file_get_contents($fileDir."/mp3Array.obj") ?: '';
$array1 = json_decode($stringified1, true) ?: [];
$stringified2 = file_get_contents($fileDir."/findArray.obj") ?: '';
$array2 = json_decode($stringified2, true) ?: [];
$object->file_dir = $fileDir;
$object->mp3Array = $object->arrayDecode($array1);
$object->findArray = $array2;
}
return $object;
}
private function arrayEncode($arr){
$tmp = [];
foreach($arr as $key => $val){
if(is_array($val)){
$tmp[$key] = $this->arrayEncode($val);
}else if ($key == "data"){
$tmp[$key] = base64_encode($val);
}else{
$tmp[$key] = $val;
}
}
return $tmp;
}
private function arrayDecode($arr){
$tmp = [];
foreach($arr as $key => $val){
if(is_array($val)){
$tmp[$key] = $this->arrayDecode($val);
}else if ($key == "data"){
$tmp[$key] = base64_decode($val);
}else{
$tmp[$key] = $val;
}
}
return $tmp;
}
public function setMp3(array $new_array): void {
$this->mp3Array = $new_array;
$stringified = json_encode($new_array);
file_put_contents($this->file_dir."/mp3Array.obj", $stringified);
}
public function saveMp3(): void {
$base64 = $this->arrayEncode($this->mp3Array);
$stringified = json_encode($base64);
file_put_contents($this->file_dir."/mp3Array.obj", $stringified);
}
public function setFind(array $new_array): void {
$this->findArray = $new_array;
$stringified = json_encode($new_array);
file_put_contents($this->file_dir."/findArray.obj", $stringified);
}
public function saveFind(): void {
$stringified = json_encode( $this->findArray);
file_put_contents($this->file_dir."/findArray.obj", $stringified);
}
I know this question is asked like a hundred times in hundred different ways. But I just can't get this particular one to work. I am really confused.
This is the very beginning of my script:
<?php
class API {
protected static $message, $data, $status, $session, $token, $dbo, $app;
const FAIL = 0;
const SUCCESS = 1;
const INCOMPLETE_ARGS = 2;
const GROUP_ALLOW = array(5);
public static function Init() {
global $app;
self::$status = false;
self::$message = "Invalid request.";
self::$data = "";
}
public static function Render() {
echo preg_replace("/\\\\u([0-9abcdef]{4})/", "&#x$1;", json_encode(array(
"status" => self::$status,
"message" => self::$message,
"data" => self::$data))
);
}
Then there is this function:
public static function Login() {
$email = (isset($_REQUEST['email'])) ? $_REQUEST['email'] : "";
$password = (isset($_REQUEST['password'])) ? $_REQUEST['password'] : "";
require_once ("../app/Mage.php");
umask(0);
ob_start();
session_start();
Mage::app('default');
Mage::getSingleton("core/session", array("name" => "frontend"));
$b2bwebsiteid = Mage::getStoreConfig('base/general/b2bwebsite');
//$websiteId = Mage::app()->getWebsite()->getId();
//$store = Mage::app()->getStore();
$customer = Mage::getModel("customer/customer");
$customer->website_id = $b2bwebsiteid;
$customer->loadByEmail($email);
$countryCodeBE = $customer->getDefaultBillingAddress()->getCountry();
$countrybe = Mage::getModel('directory/country')->loadByCode($countryCodeBE);
$countryid = $countrybe->getCountryId();
.....
.....
And in between there is a lot more code and functions.
Now a bit further, there is another function:
public static function ProductInfoNew() {
And now the thing I want, is to load the variable $countryid from that first function in the ProductInfoNew function so that I can do something like this:
if ($countryid == "BE") {
$groupid = 6;
}
else {
$groupid = 4;
}
But the output always seems to be NULL or Empty.
I tried setting the $countryid variable as global, static, tried to load the variable with self:: and I tried so many things but I can't get it to work.
Does anyone know the correct solution to do this?
Thanks a lot in advance.
$countryid should be a field inside of the class you are using.
class API {
private static $country_id;
public static function ProductInfoNew() {
if (self::$country_id == "BE") {
$groupid = 6;
} else {
$groupid = 4;
}
}
}
New to OOP, figured I'd practice a bit by sending back data from PHP via ajax. What am I doing wrong here? It works if I change the code to procedural. Here's the OOP:
if (isset($_POST['fruity'])) {
$start_fruity = new Fruity_draft();
$start_fruity->send_json();
}
class Fruity_draft {
public $banned = $_POST['banned'];
public $players = $_POST['players'];
public $random_civs = $_POST['random_civs'];
public $array_list = [];
public $send_json['banned'] = $banned;
function __construct($send_json) {
$this->send_json = $send_json;
}
function send_json() {
echo json_encode($this->send_json);
}
}
First of all, you forgot about passing a parameter to the constructor, it expects an array.
function __construct($send_json) {
In your call, you don't send anything
$start_fruity = new Fruity_draft();
This throws a warning, Warning: Missing Argument 1
and a notice, Notice: Undefined variable: send_json
Second, you should move the initialization of the class variables in the constructor.
class Fruity_draft {
public $banned;
public $players;
public $random_civs;
public $array_list;
public $send_json;
function __construct($send_json) {
$this->banned = 'banned';
$this->players = 'players';
$this->random_civs = 'random_civs';
$this->send_json = $send_json;
$this->send_json['banned'] = $this->banned;
}
...
}
That's not really OOP :). You should return something from the class, not echo.
Also, you should send data from other function to the class.. in the constructor or with a method set_post_data() or something...
Simple:
if (isset($_POST['fruity'])) {
$start_fruity = new Fruity_draft($_POST);
echo $start_fruity->get_json_response();
}
class Fruity_draft {
private $postData;
function __construct($postData) {
$this->postData = $postData;
}
function get_json_response() {
return json_encode($this->postData['banned']);
}
}
I don't even know if this is possible but I'm trying to set an optional value to an existing object.
Here is a simplified version of the code I'm trying.
<?php
class configObject {
private $dataContainer = array();
public function set($dataKey, $dataValue) {
$this->dataContainer[$dataKey] = $dataValue;
return TRUE;
}
public function get($dataKey) {
return $this->dataContainer($dataKey);
}
$this->set('someValue', 'foobar');
} //End configObject Class
function getPaginationHTML($c = &$_config) {
$someOption = $c->get('someValue');
// Do other stuff
return $html;
}
$_config = new configObject();
$html = getPaginationHTML();
?>
I'm getting the error:
syntax error, unexpected '&' in
Any help is appreciated, again I'm not sure if it's even possible to do what I'm trying to do so sorry for being a noob.
Thanks
example with the decorator pattern:
class ConfigObject {
private $dataContainer = array();
public function set($dataKey, $dataValue) {
$this->dataContainer[$dataKey] = $dataValue;
return true;
}
public function get($dataKey) {
return $this->dataContainer[$dataKey];
}
}
class ConfigObjectDecorator {
private $_decorated;
public function __construct($pDecorated) {
$this->_decorated = $pDecorated;
}
public function getPaginationHTML($dataKey) {
$someOption = $this->get($dataKey);
// Do other stuff
$html = '<p>' . $someOption . '</p>';
return $html;
}
public function set($dataKey, $dataValue) {
return $this->_decorated->set($dataKey, $dataValue);
}
public function get($dataKey) {
return $this->_decorated->get($dataKey);
}
}
class ConfigFactory {
public static function create () {
$config = new ConfigObject();
return new ConfigObjectDecorator($config);
}
}
$config = ConfigFactory::create();
if ($config->set('mykey', 'myvalue'))
echo $config->getPaginationHTML('mykey');
Note that can easily rewrite ConfigFactory::create() to add a parameter to deals with other types of decoration (or none).
EDIT I've updated the question with actual code. Turns out it was not a scope issue but a stupid mistake on my part. While testing that all value were good I was really setting them to empty.
After reading the answer below I realized I have the scope figured out but had a typo in the code.
Sorry
<?php
abstract class PHPFoo_XYZ
{
protected $_postData = array();
public function processXYZ(array $postData)
{
$this->_postData = $postData;
}
protected function _checkProcessId()
{
// doing nothing
}
}
?>
<?php
require_once dirname(__FILE__) . '/../PHPFoo/XYZ.php';
class App_XYZ extends PHPFoo_XYZ
{
protected $_UserData = array();
protected $_UserId = 'notset';
protected $_UserName = '';
public $_msg = '';
public function processXYZ(array $postData)
{
$this->_postData = $postData;
$this->_getUserData();
$this->_checkProcessId();
}
protected function _checkProcessId()
{
$this->_writeLog("User Name ".$this->_UserName);
$this->_writeLog("User Id ".$this->_UserId);
// These show empty
}
public function _getUserData() {
$UserData = array();
$UserId = array();
$User_Name = array();
$msg = '';
// Get data from database
$this->_UserId = $UserId[0]['item_id'];
// Get data from database
$this->_UserName = $User_Name[0]['title'];
// Get full data
// $results = Array of values from database
foreach ($results as $key => $value) {
$UserData[$results[$key]['fielddef_id']] = $results[$key]['value'];
}
$this->_UserData = $UserData;
$this->_writeLog("USER DATA FULL");
$this->_writeLog("User Name ".$this->_UserName);
$this->_writeLog("User Id ".$this->_UserId);
$msg = '';
foreach ($this->_UserData as $k => $v) {
$msg .= "\n".$k." == ".$v;
}
$this->_writeLog("User Data\n".$msg);
// The above output is good
if($this->_UserData = '' || $this->_UserId = '' || $his->_UserName = '') {
$this->_writeLog("There was an error getting User Data.");
return false;
}else{
return true;
}
}
}
There is something wrong from beginning, you should write "public function" when you declare a function, not "public functions", and there must be the word "function" declaring a method, not just the name.
Also you are calling a method myfunc1, when it doesn't exists and you have made another mistake when you call func2 (you wrote fucn2).
So, if you fix your code, it works as you want.
Here I fixed it for you:
<?php
abstract class foo {
protected $_var1 = '';
protected $_var2 = '';
public function func1() {
#code...
}
public function func2() {
#code..
}
}
class bar extends foo {
protected $myvar1 = '';
protected $myvar2 = '';
public function myfunc() {
// do some code to fill myvar1 to be used in other functions
$this->myvar1 = 'some data';
echo "my var " . $this->myvar1;
}
public function func2() {
// do some code that uses myvar1 data
// but $this->myvarf1 is empty here why?
echo $this->myvar1;
}
public function runit() {
$this->myfunc();
$this->func2();
}
}
//requre file
$callclass = new bar;
$callclass->runit();
?>
So please be careful before asking and if you can/want use an ide like netbeans for php to avoid this mistakes.
Have a good night.