With PHPUnit 6.5.14, I am trying to test a method. To do this, one of its dependencies needs to be mocked; however I can't get it working. Here is a stripped down version:
class Order {
public function create() {
$CCP = new CreditCardProcessor();
$success = $CCP->chargeCreditCard();
return $success;
}
}
class CreditCardProcessor {
public function chargeCreditCard() {
return false;
}
}
class OrderTest extends TestCase {
public function testCreate() {
$mockCCP = $this->getMockBuilder(CreditCardProcessor::class)
->setMethods(['chargeCreditCard'])
->getMock();
$mockCCP
->method('chargeCreditCard')
->willReturn(true);
$O = new Order();
$success = $O->create();
$this->assertTrue($success, 'Was not able to create order.');
}
}
I've read the docs and gone over some examples but can't figure it out. Any ideas what I am doing wrong? Thanks.
After finding more examples, I believe the solution is to pass in the dependency to create as an argument:
class Order {
public function create($CCP) {
$success = $CCP->chargeCreditCard();
return $success;
}
}
Then I can do the same in my test:
class OrderTest extends TestCase {
public function testCreate() {
$mockCCP = $this->getMockBuilder(CreditCardProcessor::class)
->setMethods(['chargeCreditCard'])
->getMock();
$mockCCP
->method('chargeCreditCard')
->willReturn(true);
$O = new Order();
$success = $O->create($mockCCP);
$this->assertTrue($success, 'Was not able to create order.');
}
}
I haven't tried yet, but that should do it.
I don't like the idea of changing code to satisfy tests, but it probably is also an indication I need to restructure my code.
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
I have two classes, ClassA, ClassB. In ClassA I have Method which call method from ClassB.
For Example:
function functionInClassB($state)
{
if ($state) {
return true;
} else {
return false;
}
}
function functionInClassA ()
{
if (functionInClassB(1)) {
return "Anything";
}
}
Now, I want test functionInClassB with PhpUnit and I dont want functionInClassB ran. I want return value which I want.
Sorry for my English, please help!!!
Old question, but I think you're referring to stubbing / mocking.
How easy /sensible it is depends. If you want to call functionInClassB statically then you're out of luck; static stuff doesn't work well with testing. But if you're calling the method on an instance then the following should work:
class A {
protected $instanceOfB;
public function functionInClassA(B $injectedInstanceOfB) {
if ($injectedInstanceOfB->functionInClassB(1)) return 'anything';
}
}
class B {
public function functionInClassB($state) {
if ($state) {
return true;
} else {
return false;
}
}
}
class yourPHPUnitTest extends \PHPUnit\Framework\TestCase {
public function testA() {
$stubOfB = $this->createStub(\B::class);
// Optionally...
$stubOfB->method('functionInClassB')->willReturn('something');
// Thing you want to test
$a = new A();
$result = $a->functionInClassA($stubOfB);
$this->assertEqual('something', $result);
}
}
What is the best way to call Method heDidIt() from child class Make?
I was thinking about events but couldnt find a good non global solution.
$control = new Control();
$maker = $control->createMaker();
$maker->doIt();
class Control
{
private $_make;
public function createMaker()
{
$this->_make = new Make();
return $this->_make;
}
private function heDidIt()
{
//Call me if the Maker did something.
}
}
class Make
{
public function doIt()
{
//hey im doing something, better tell my Controller
}
}
Just tell Make who's its boss so it can inform him:
$control = new Control();
$maker = $control->createMaker();
$maker->doIt();
class Control
{
private $_make;
public function createMaker()
{
$this->_make = new Make($this);
return $this->_make;
}
private function heDidIt()
{
//Call me if the Maker did something.
}
public function inform($sampleParam) {
var_dump($sampleParam);
$this->heDidIt();
}
}
class Make
{
protected $control;
public function __construct(Control $control) {
$this->control = $control;
}
public function doIt()
{
//hey im doing something, better tell my Controller
$control->inform('called in Make::doIt()');
}
}
$control = new Control();
$maker = $control->createMaker();
$maker->doIt();
class Control
{
private $_make;
public function createMaker()
{
$this->_make = new Make();
return $this->_make;
}
**protected** function heDidIt()
{
//Call me if the Maker did something.
}
}
class Make **extends Control**
{
public function doIt()
{
**$this -> heDidIt();**
//hey im doing something, better tell my Controller
}
}
Although this seems extremely pointless, so maybe providing your actual code and requirements would let us help you better.
In a lot of my PHP classes, I have this code:
private $strError = "";
private $intErrorCode = NULL;
private $blnError = FALSE;
public function isError() {
return $this->blnError;
}
public function getErrorCode() {
return $this->intErrorCode;
}
private function setError( $strError, $intErrorCode = NULL ) {
$this->blnError = TRUE;
$this->intErrorCode = $intErrorCode;
$this->strError = $strError;
}
The point is so that outside code can know if an object has an error state, what the string of the error is, etc. But to have this exact code in a bunch of different classes is repetitious!
I'd love to have a dual-extension where I could do
class childClass extends parentClass, error {
...
}
And have those properties and methods inborn, But PHP doesn't support multiple inheritances. What I'm thinking about doing is creating an error class that exists inside each class. If I make it public, I can call it directly through the object
if ( $myObject->error->isError() ) {...}
but wouldn't that also make its error status settable from outside the containing class,
$myObject->error->setError("I shouldn't be doing this here");
which I would rather avoid?
Or I could write 'gateway' functions in the containing class, which do the appropriate calls on the error object, and prevent setting the error status from outside,
class childClass extends parentClass {
private $error;
public function __construct(...) {
...
$error = & new error();
...
}
public function isError() {...}
public function getError() {...}
public function getErrorCode() {...}
private function setError() {...}
...
}
but that leads to (some of) the code duplication that I'm trying to avoid.
What's the optimal solution here? I'm trying to have functionality for error statuses for a number of objects, so that the outside world can see their error state, with minimal repetition.
Use composition instead of inheritance.
class Errors {
private $strError = "";
private $intErrorCode = NULL;
private $blnError = FALSE;
public function isError() {
return $this->blnError;
}
public function getErrorCode() {
return $this->intErrorCode;
}
private function setError( $strError, $intErrorCode = NULL ) {
$this->blnError = TRUE;
$this->intErrorCode = $intErrorCode;
$this->strError = $strError;
}
}
And now use a private instance variable to refer to it:
class childClass extends parentClass {
private $errors = new Errors();
...
}
The private visibility prevents you from referencing $errors outside of the class.
There's also no need to create isError(), getError(), etc. inside childClass (and therefore no need to worry about code duplication). Simply call $this->errors->isError(), $this->errors->getError(), etc. If you still wanted to require those methods to be implemented though, as suggested below, you could specify an interface.
You could also abuse the __call magic method to do the same thing:
public function __call($name, array $arguments) {
$name = strtolower($name);
if (isset($this->methods[$name])) {
array_unshift($arguments, $this);
return call_user_func_array($this->methods[$name], $arguments);
}
throw new BadMethodCallException('Method does not exist');
}
Note that I said abuse... Ideally, I'd think of a different architecture rather than having all these "common methods" everywhere. Why not use an exception instead of checking $foo->isError? If that's not appropriate, why not decorate a class?
class Errors
protected $object = null;
public function __construct($object) {
$this->object = $object;
}
public function __call($method, array $arguments) {
$callback = array($this->object, $method);
if (is_callable($callback)) {
return call_user_func_array($callback, $arguments);
}
throw new BadMethodCallException('Method does not exist');
}
public function __get($name) { return $this->object->$name; }
public function __set($name, $value) { $this->object->$name = $value; }
// Your methods here
public function isInstance($name) { return $this->object instanceof $name; }
}
Then just "wrap" your existing object in that class:
$obj = new Errors($obj);
$obj->foo();
As of PHP 5.4, you can use Traits.
For example you could make Trait called ErrorTrait like this:
trait ErrorTrait {
private $strError = "";
private $intErrorCode = NULL;
private $blnError = FALSE;
public function isError() {
return $this->blnError;
}
public function getErrorCode() {
return $this->intErrorCode;
}
private function setError( $strError, $intErrorCode = NULL ) {
$this->blnError = TRUE;
$this->intErrorCode = $intErrorCode;
$this->strError = $strError;
}
}
Then you would define your child class like this:
class childClass extends parentClass {
use ErrorTrait;
...
}
Traits work basically like copy/paste so all of the code in the trait would be available within the class (without the code duplication).
Hey there I'm wondering how this is done as when I try the following code inside a function of a class it produces some php error which I can't catch
public $tasks;
$this->tasks = new tasks($this);
$this->tasks->test();
I don't know why the initiation of the class requires $this as a parameter either :S
thanks
class admin
{
function validate()
{
if(!$_SESSION['level']==7){
barMsg('YOU\'RE NOT ADMIN', 0);
return FALSE;
}else{
**public $tasks;** // The line causing the problem
$this->tasks = new tasks(); // Get rid of $this->
$this->tasks->test(); // Get rid of $this->
$this->showPanel();
}
}
}
class tasks
{
function test()
{
echo 'test';
}
}
$admin = new admin();
$admin->validate();
You can't declare the public $tasks inside your class's method (function.) If you don't need to use the tasks object outside of that method, you can just do:
$tasks = new Tasks($this);
$tasks->test();
You only need to use the "$this->" when your using a variable that you want to be available throughout the class.
Your two options:
class Foo
{
public $tasks;
function doStuff()
{
$this->tasks = new Tasks();
$this->tasks->test();
}
function doSomethingElse()
{
// you'd have to check that the method above ran and instantiated this
// and that $this->tasks is a tasks object
$this->tasks->blah();
}
}
or
class Foo
{
function doStuff()
{
$tasks = new tasks();
$tasks->test();
}
}
with your code:
class Admin
{
function validate()
{
// added this so it will execute
$_SESSION['level'] = 7;
if (! $_SESSION['level'] == 7) {
// barMsg('YOU\'RE NOT ADMIN', 0);
return FALSE;
} else {
$tasks = new Tasks();
$tasks->test();
$this->showPanel();
}
}
function showPanel()
{
// added this for test
}
}
class Tasks
{
function test()
{
echo 'test';
}
}
$admin = new Admin();
$admin->validate();
You're problem is with this line of code:
public $tasks;
$this->tasks = new tasks();
$this->tasks->test();
$this->showPanel();
The public keyword is used in the definition of the class, not in a method of the class. In php, you don't even need to declare the member variable in the class, you can just do $this->tasks=new tasks() and it gets added for you.