I'm trying to get started with setting up unit tests using a MySQL database and I'm running into this exception:
DBTest::test__getException()
Argument 1 passed to PHPUnit_Extensions_Database_DataSet_DefaultTableIterator::__construct() must be an array, null given.
I don't know what I could be missing
My Unit Test Code:
<?php
class DBTest extends Generic_Tests_DatabaseTestCase {
//...
public function getDataSet() {
$dataSet = $this->createMySQLXMLDataSet(dirname(__FILE__)."/../db/t_enroll_fixtures.xml");
return $dataSet;
}
public function setUp() {
$this->X = $this->getMock('\X\Engine\X');
$this->model = new Model($this->X, 't_users');
$this->className = get_class($this->model);
parent::setUp();
}
public function tearDown() {
$this->X = NULL;
$this->model = NULL;
parent::tearDown();
}
public function testMagicFields() {
$this->getConnection()->addTable('t_enroll');
$this->assertEquals(10, $this->getConnection()->getRowCount('t_enroll'));
}
}
?>
The Generic_Test_DatabaseTestCase Class:
<?php
require_once "PHPUnit/Extensions/Database/TestCase.php";
abstract class Generic_Tests_DatabaseTestCase extends PHPUnit_Extensions_Database_TestCase {
// only instantiate pdo once for test clean-up/fixture load
static private $pdo = null;
// only instantiate PHPUnit_Extensions_Database_DB_IDatabaseConnection once per test
private $conn = null;
final public function getConnection() {
if($this->conn === null) {
if(self::$pdo == null) {
self::$pdo = new PDO($GLOBALS['DB_DSN'], $GLOBALS['DB_USER'], $GLOBALS['DB_PASSWD']);
}
$this->conn = $this->createDefaultDBConnection(self::$pdo, $GLOBALS['DB_DBNAME']);
}
return $this->conn;
}
}
?>
What am I missing?
I had the same problem. The reason was, that the XML fixture was generated by MySQLDump and someone removed the <database name="xyz"> node.
This turned $this->tables in PHPUnit into NULL instead Array
This happened to me after I added a schema location for the mysqldump like
<mysqldump xmlns="mysqldump"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="mysqldump mysqldump.xsd ">
After I removed the namespace, it worked:
<mysqldump xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
Related
i have test:
class ContacsBLOTest extends TestCase
{
public function testsearch()
{
$Ctrl= new ContactsBLO;
$data=['id'=>1,'name'=>'The Manh','phone'=>'123456566','address'=>'180 cao lo','note'=>''];
$data=[(object)$data];
$mock_data=\Mockery::mock('DB');
$mock_data->shouldReceive('all')->andReturn($data);
$mock_ctrl= new ContactsBLO;
$mock_ctrl->select=$mock_data;
$result=$mock_ctrl->search('manh');
$this->assertNotNull($result);
}
and this is ContacsBLO class:
class ContactsBLO
{
public $db,$not_allow,$Validation;
public function __construct(){
$this->db=new DB;
$this->not_allow=['"','\'','%'];
$this->Validation = new ContactValidation;
}
public function search($request=null){
$length=strlen($request);
for ($i=0;$i<$length;$i++) {
$forbidden=$this->not_allow;
if(in_array($request[$i],$forbidden)){
return (['messenger'=>'We are not allow special character in your request','old_input'=>$request]);
}
else{
return $data=$this->db->select('*',$request);
}
}
}
}
DB::class(i define connect to data base and define select method:
class DB
{
public $obj = null;
public $table = 'contacts';
public function __construct(){
$dsn="mysql:host=".HOST."; dbname=".DB_NAME;
$this->obj = new \PDO($dsn, DB_USER, DB_PASS);
$this->obj->query("set names 'utf8' ");
}
public function select($row=null,$query=null) {
$sql='SELECT '.$row.' FROM '.$this->table.' '.$query;
$data = $this->obj->prepare($sql);
$data->execute();
return $data->fetchAll(\PDO::FETCH_CLASS);
}
}
But when i run xdebug and run this test, $forbidden is null,it mean mock method return real data, not mock data. i dont know why.
Anyone can help me! Please!
You never inserted your mock into your class, besides when using the new keyword to create a class instance it's difficult to mock. Your only chance in such cases is to use class alias.
To avoid all this you can pass in the database instance through the ContactsBLO constructor.
class ContacsBLOTest extends TestCase
{
public function testSearch()
{
$data = ['id'=>1,'name'=>'The Manh','phone'=>'123456566','address'=>'180 cao lo','note'=>''];
$data = json_decode(json_encode($data));
$mock_contact = \Mockery::mock(DB::class);
$mock_contact->shouldReceive('select')->andReturn($data);
$Ctrl = new ContactsBLO($mockDB);
$result = $Ctrl->search('manh');
$this->assertNotNull($result);
}
}
class ContactsBLO
{
public $db;
public $not_allow;
public $Validation;
public function __construct(DB $db) {
$this->db = $db;
$this->not_allow = ['"','\'','%'];
$this->Validation = new ContactValidation;
}
public function search($request=null){
$length=strlen($request);
for ($i=0;$i<$length;$i++) {
$forbidden = $this->not_allow;
if(in_array($request[$i],$forbidden)){
return (['messenger'=>'We are not allow special character in your request','old_input'=>$request]);
}
else{
return $data = $this->db->select('*',$request);
}
}
}
}
I tested it with this code and it worked fine. Please check if the DB class is imported at the top of your test file. You also have to append Test to all test file names and classes (see above).
I was change it to:
$mock_data=\Mockery::mock('DB');
$mock_data->shouldReceive('select')->andReturn($data);
$mock_ctrl= new ContactsBLO;
$mock_ctrl->db=$mock_data;
$result=$mock_ctrl->search();
And it is working for me, thank for all help
Singleton Class:
<?php
class db_singleton
{
const ORACLE_HOST = "SOMEIP";
const ORACLE_USER = "validuser";
const ORACLE_PASS = "validpass";
const ORACLE_DB = "SOMEIP/DBNAME";
private static $instance; // stores the oci_* instance
private function __construct() { } // block directly instantiating
private function __clone() { } // block cloning of the object
public static function call()
{
// create the instance if it does not exist
if(!isset(self::$instance))
{
// the ORACLE_* constants should be set to or
// replaced with your db connection details
self::$instance = oci_connect(self::ORACLE_USER, self::ORACLE_PASS, self::ORACLE_DB);
if(self::$instance->connect_error)
{
throw new Exception('Oracle connection failed: ' . self::$instance->connect_error);
}
}
// return the instance
return self::$instance;
}
public function __destruct() {
oci_close($instance);
}
public function queryresult($query)
{
$result_set_array =array();
$this->stmt = oci_parse($this->con, $query);
oci_execute($this->stmt);
while($row=oci_fetch_array($this->stmt,OCI_ASSOC+OCI_RETURN_NULLS))
{
$result_set_array[] = $row;
}
oci_free_statement($this->stmt);
return $result_set_array;
}
}
?>
When I try using singleton class with below code, it works perfect and fetch results.
$conn = db_singleton::call();
$stid = oci_parse($conn, 'SELECT * FROM somevalid_table');
oci_execute($stid);
while($result=oci_fetch_array($stid,OCI_ASSOC+OCI_RETURN_NULLS))
{
$result_set_array[] = $result;
}
Now, when I try extending my class using model, it throws exception
class Myclass Extends db_singleton{
public function someModel()
{
$result = parent::queryresult(" select * from somevalid_table");
return $result;
}
}
Exception:
Fatal error: Call to private db_singleton::__construct() from context 'someController'
I know that class cannot be instantiated having private constructor. __construct() functions are always called when an object is instantiated, so trying to do something like $x = new MyObject() will cause a fatal error with a private construction function.
I am using Singleton classes to prevent direct instantiation of an object. How can I overcome issue ? What would be the best solution ?
Thanks.
$x = new MyObject() will never work if your constructor is private in that class because __construct() is the first method that is invoked on object creation.
Create a public method
/**
* Singleton class
*
*/
final class UserFactory
{
/**
* Call this method to get singleton
*
* #return UserFactory
*/
public static function Instance()
{
static $inst = null;
if ($inst === null) {
$inst = new UserFactory();
}
return $inst;
}
/**
* Private ctor so nobody else can instance it
*
*/
private function __construct()
{
}
}
To use:
$fact = UserFactory::Instance();
$fact2 = UserFactory::Instance();
$fact == $fact2;
But:
$fact = new UserFactory()
PHP Class Script:
class db_singleton
{
const ORACLE_USER = "validuser";
const ORACLE_PASS = "validpass";
const ORACLE_DB = "SOMEIP/DBNAME";
private static $instance = null; // stores the oci_* instance
// private constructor
private function __construct() { } // block directly instantiating
private function __clone() { trigger_error('Clone is not allowed.', E_USER_ERROR); } // block cloning of the object
public static function getInstance()
{
// create the instance if it does not exist
if(!isset(self::$instance))
{
// the ORACLE_* constants should be set to or
// replaced with your db connection details
self::$instance = oci_connect(self::ORACLE_USER, self::ORACLE_PASS, self::ORACLE_DB);
if(self::$instance->connect_error)
{
throw new Exception('Oracle connection failed: ' . self::$instance->connect_error);
}
}
// return the instance
return self::$instance;
}
public static function queryresult($query)
{
$result_set_array =array();
$stmt = oci_parse(db_singleton::getInstance(), $query);
oci_execute($stmt);
while($row=oci_fetch_array($stmt,OCI_ASSOC+OCI_RETURN_NULLS))
{
$result_set_array[] = $row;
}
oci_free_statement($stmt);
return $result_set_array;
}
Now to prevent Fatal error: Call to private db_singleton::__construct(), I have added an empty constructor to my child class which is model class in my case. This will override the parent class constructor which is private.
class Myclass Extends db_singleton{
public function __construct() {}
public function someModel(){
$result = parent::queryresult(" select * from somevalid_table");
return $result;
}
}
Hope it helps someone.
Thanks.
I have the following unit test but I don't get back the needed values. Maybe I don't understand how this works correctly.
class TestClass
{
public function getData()
{
$id = 1123;
return $id;
}
}
class Test_ClassTesting extends PHPUnit_Framework_TestCase
{
public function test_addData()
{
$stub = $this->getMock('TestClass');
$stub
->expects($this->any())
->method('getData')
->will($this->returnValue('what_should_i_put_here_to_get id from TESTCLASS'));
$y = $stub->getData();
}
}
As the commenters have said, you simply return the value desired.
class TestClass
{
public function getData()
{
$id = 1123;
return $id;
}
}
class Test_ClassTesting extends PHPUnit_Framework_TestCase
{
public function test_addData()
{
$stub = $this->getMock('TestClass'); // Original Class is not used now
$stub
->expects($this->any())
->method('getData')
->will($this->returnValue(4444)); // Using different number to show stub works, not actual function
$this->assertEquals(4444, $stub->getData());
}
public function test_addDataWithoutStub()
{
$object = new TestClass();
$this->assertEquals(1123, $object->getData());
}
}
I have something like this:
class MyParent {
protected static $object;
protected static $db_fields;
public function delete() {
// delete stuff
}
public static function find_by_id($id = 0) {
global $database;
$result_array = self::find_by_sql("SELECT * FROM " . static::$table_name . " WHERE id=" . $database -> escape_value($id) . " LIMIT 1");
return !empty($result_array) ? array_shift($result_array) : false;
}
public static function find_by_sql($sql = "") {
global $database;
// Do Query
$result_set = $database -> query($sql);
// Get Results
$object_array = array();
while ($row = $database -> fetch_array($result_set)) {
$object_array[] = self::instantiate($row);
}
return $object_array;
}
private static function instantiate($record) {
$object = self::$object;
foreach ($record as $attribute => $value) {
if (self::has_attribute($attribute)) {
$object -> $attribute = $value;
}
}
return $object;
}
}
class TheChild extends MyParent {
protected static $db_fields = array('id', 'name');
protected static $table_name = "my_table";
function __construct() {
self::$object = new TheChild;
}
}
$child= TheChild::find_by_id($_GET['id']);
$child->delete();
I get this: Call to undefined method stdClass::delete() referring to the last line above. What step am I missing for proper inheritance?
You never actually instanciate the TheChild class, which should be done by
$var = new TheChild();
except in TheChild constructor itself.
So, the static $object field is never affected (at least in your example), so affecting a field to it (the line $object -> $attribute = $value; ) causes the creation of an stdClass object, as demonstrated in this interactive PHP shell session:
php > class Z { public static $object; }
php > Z::$object->toto = 5;
PHP Warning: Creating default object from empty value in php shell code on line 1
php > var_dump(Z::$object);
object(stdClass)#1 (1) {
["toto"]=>
int(5)
}
This object does not have a delete method.
And as said before, actually creating a TheChild instance will result in an infinite recursion.
What you want to do is this, probably:
class TheChild extends MyParent {
protected static $db_fields = array('id', 'name');
protected static $table_name = "my_table";
function __construct() {
self::$object = $this;
}
}
Edit: Your updated code shows a COMPLETE different Example:
class MyParent {
protected static $object;
public function delete() {
// delete stuff
}
}
class TheChild extends MyParent {
function __construct() {
self::$object = new TheChild;
}
}
$child = new TheChild;
$child->delete();
Calling "Child's" Constructor from within "Child's" Constructor will result in an infinite loop:
function __construct() {
self::$object = new TheChild; // will trigger __construct on the child, which in turn will create a new child, and so on.
}
Maybe - i dont know what you try to achieve - you are looking for:
function __construct() {
self::$object = new MyParent;
}
ALSO note, that the :: Notation is not just a different Version for -> - it is completely different. One is a Static access, the other is a access on an actual object instance!
so I am new in the world of object oriented programming and I am currently facing this problem (everything is described in the code):
<?php
class MyClass {
// Nothing important here
}
class MyAnotherClass {
protected $className;
public function __construct($className){
$this->className = $className;
}
public function problematicFunction({$this->className} $object){
// So, here I obligatorily want an $object of
// dynamic type/class "$this->className"
// but it don't works like this...
}
}
$object = new MyClass;
$another_object = new MyAnotherClass('MyClass');
$another_object->problematicFunction($object);
?>
Can anyone help me ?
Thanks, Maxime (from France : sorry for my english)
What you need is
public function problematicFunction($object) {
if ($object instanceof $this->className) {
// Do your stuff
} else {
throw new InvalidArgumentException("YOur error Message");
}
}
Try like this
class MyClass {
// Nothing important here
public function test(){
echo 'Test MyClass';
}
}
class MyAnotherClass {
protected $className;
public function __construct($className){
$this->className = $className;
}
public function problematicFunction($object){
if($object instanceof $this->className)
{
$object->test();
}
}
}
$object = new MyClass;
$another_object = new MyAnotherClass('MyClass');
$another_object->problematicFunction($object);
That's called type hinting and what you want to do is just not supported.
If all those dynamic class names have something in common (e.g., they're different implementations for certain feature) you probably want to define a base (maybe abstract) class or an interface and use that common ancestor as type hint:
<?php
interface iDatabase{
public function __contruct($url, $username, $password);
public function execute($sql, $params);
public function close();
}
class MyClass implements iDatabase{
public function __contruct($url, $username, $password){
}
public function execute($sql, $params){
}
public function close(){
}
}
class MyAnotherClass {
protected $className;
public function __construct($className){
$this->className = $className;
}
public function problematicFunction(iDatabase $object){
}
}
Otherwise, just move the check to within problematicFunction() body, as other answers explain.