I have function test which return the repsonse of static function staticABC that belongs to separate class
function test()
{
return testA::staticABC();
}
Now I want to write PHPUnit cases for function test by mocking staticABC() function.Can any techie have idea about this ?
I don't think there is a way of mocking a function, but what you could do is use some kind of test double:
class SUT{
$staticCreator = array('testA::staticABC'); //Initialized to a default
//for production, would be better if injected somehow before using
function setStaticCreator($staticCreator){
$this->staticCreator=$staticCreator;
}
function test(){
return call_user_func($this->staticCreator);
}
}
and then run your test this way:
class Test extends ...{
function mockStaticABC(){
return "mock_string";
}
test_testfunction(){
$sut = new SUT();
$staticCreator = array($this,'mockStaticABC');
$sut->setStaticCreator($staticCreator);
$mock_return = $sut->test();
$this->assertEquals("mock_string",$mock_return);
}
}
Related
I have a bunch of PHP functions that require unit test classes to be written.
I just want to know is there any way to mock-up lines of a function before use assertions?
<?php
function get_Func($args){
$number = $args[0];
$id = "71".$args[1]."0";
//The Line I need to mockup without touching the code
$result = getViaCurl($number,$id);
return $result;
}
?>
You need to extract the function to a service, so you can mock it easily, but as long as your function is inside a class, because this way you can pass the new service to the class constructor to be able to pass the mock in the test, instead of the original.
class YourClass
{
private NewService $service;
public function __construct($service)
{
$this->service = $service;
}
function get_Func($args)
{
$number = $args[0];
$id = "71".$args[1]."0";
//The Line I need to mockup without touching the code
$result = $this->service->getViaCurl();
return $result;
}
}
class NewService
{
public function getViaCurl($number, $id)
{
return getViaCurl($number,$id);
}
}
I have 3 classes.
class Box{
public $item1;
public $item2;
public function __construct($item1,$item2){
$this->item = $item1;
$this->item2 = $item2;
}
public function getItem1(){
return $this->item1;
}
}
class Details{
public $stuff
public $item1;
public $item2;
public $item3;
public function __construct($stuff){
$this->stuff = $stuff
}
public function setItem1($item){
$this->item1 = $item;
}
public function setItem2($item){
$this->item2 = $item;
}
}
class Crate{
public $box;
private $stuffString = "Stuff";
public function __construct(Box $box){
$this->box = $box;
}
public function getDetails(){
$details = new Details($stuffString);
$details->setItem1($box->item1);
$details->setItem2("Detail");
return $details;
}
}
The Crate->getDetails() method returns a Details object with data from the Box object. I want to write tests for this method.
function test_get_details(){
$box = Mockery::mock(Box::class);
$box->shouldReceive('getItem1')->andReturn("BoxItem");
$crate= new Crate($box);
$details = $crate->getDetails();
$this->assertInstanceOf(Details::class,$details);
}
I create a mock of the Box class and pass it to constructor of Crate. When I call $crate->getDetails(); it should return a Details object with
$item1 = "BoxItem"
$item2 = "Detail"
$item3 = null
I know I can test this by doing for each item $this->assertEquals("BoxItem",$details->item1); etc... but is that the best way to go about it? Is there some PHPUnit tool to build up the desired Detials result and compare it
For Example
$this->assertEquals(MockDetailObject,$details)
or do I have to do a series of asserts to make sure the result is what I expect.
Note*
I know for my example this isn't a huge deal, I built it up quick to explain what I mean. But in the code I'm working on I ran into the same type of problem except the Details Object is more complex than just 3 strings.
TL;DR: create a factory and test this factory 100%.
From what I understood, your Crate class is both an entity and a factory. You could refactor Crate::getDetails by moving this creation responsibility to a factory.
This way you'll be able to unit test the creation logic only by using the "Given, When, Then" structure. Check out this post about clean tests and navigate to the "Tests should be concise and meaningful".
Having this structure will help you telling what are the inputs and outputs.
For example:
CrateDetailsFactoryTest.php
class CrateDetailFactoryTest extends TestCase
{
public function testCreateCrateDetail(): void
{
// Given
$crate = $this->givenThereIsACrate();
$boxes = $this->givenThereAreTwoRedBoxes();
// When
$crateDetail = $this->crateDetailFactory->createCrateDetail(
$crate,
$boxes
);
// Then
// (Unnecessary instanceof, if you have strict return types)
self::assertInstanceOf(Detail::class, $crateDetail);
self::assertCount(2, $crateDetail->getBoxes());
self::assertEquals(
'red',
$crateDetail->getBoxes()->first()->getColor()
);
}
}
With this your creation logic is covered; From here you can simply inject your factory where you need, and during the unit test time you just mock it away:
CrateService.php
class CrateServiceTest extends TestCase
{
private $crateDetailFactory;
private $crateService;
public function setUp(): void
{
$this->crateDetailFactory = $this->prophesize(CrateDetailFactory::class);
$this->crateService = new CrateService(
$this->crateDetailFactory->reveal()
);
}
public function testAMethodThatNeedsCrateDetails(): void
{
// Given
$crate = $this->givenIHaveACrateWithTwoBoxesInIt();
$boxes = $crate->getBoxes();
// When
$result = $this->crateService->AMethodThatNeedsCrateDetails();
// Then
$this->crateDetailFactory->createCrateDetail($crate, $boxes)
->shouldBeCalledOnce();
}
}
I hope that was useful. Cheers! :)
Using your classes above, to unit test this properly, you would have to use DI to inject \Details::class into either getDetails() or the __constructor. Then write tests for each method of each class, mocking any class dependencies/properties
class Create
{
public function getDetails(\Details $details)
}
//test.php
$mockDetails = $this->createMock(\Details::class)
->expects($this-once())
->method('item1')
->with('some_arg')
->willReturn('xyz')
$mockBox = $this-createMock(\Box::class)
......
$crate = new Create($boxMock);
$result = $crate->item1($mockDetails);
$this-assertSame('xyz', $result);
If it feels like your mocking way to much for one method, then you should consider refactoring to make the code more testable.
As far as assertions for multiple items, in PHPUnit you can use a dataprovider to pass an array of values as individual tests to one test method.
PHPUnit Docs - Data Providers
You would also write separate unit tests for the \Details::class that asserted what is passed to \Details::setItem1($item) is actually set on the item1 property. Ie.
Testing \Details::class -
//test2
public function test() {
$details = new Details('some stuff');
$details->setItem1('expected');
self::assertSame('expected', $details->item1);
}
I'm new in PHP OOP. I need some help for how to write the php OOP class then can call the class like below.
Query->table('user')->column('id','name')->where('name LIKE ?',
["name"=> 'John'])->orderby('name', 'desc');
But, I had try so many time, what I can get it something like below then stop.
Query->table('user')->column('id','name');
I'm running out of ideas and I had google a lot, but fail to find any solution.
Any suitable help is very much appreciated.
You just need to return on each function of class object itself using $this. All functions that you want to run "in chain" should be public. Check this code:
<?php
class ClassName
{
public function a()
{
// ...
return $this;
}
public function b($param)
{
// ...
return $this;
}
public function c()
{
// ...
return $this;
}
}
// testing
$obj = new ClassName;
$result = $obj->a()->b('someParam')->c();
You should read about method chaining.
I am trying to write a unit test for a function that immediately loads an object from a different class that uses the input to the function as a parameter. I am new to php unit testing and couldn't find anything that address my particular problem. A few leads that I had that led to no avail was using an injector, and trying to us a reflection.
The code I am trying to write a unit test for is:
public static function isUseful($item) {
$objPromo = MyPromoCodes::Load($item->SavedSku);
if (!is_null($objPromo)
&& ($objPromo->PromoType == MyPromoCodes::Interesting_Promo_Type)) {
return true;
}
return false;
}
My attempt at mocking this out:
public function testIsUseful() {
$injector = $this->getMockBuilder('MyPromoCodes')
->setMethods(array('Load'))
->getMock();
$objPromo = $this->getMock('MyPromoCodes');
$objPromo->PromoType = 'very interesting promo type';
$injector->set($objPromo, 'MyPromoCodes');
$lineItem1 = $this->getDBMock('LineItem');
$this->assertTrue(MyClass::isUseful($lineItem1));
}
however this doesn't work because there is no set method for this object....
Not sure what else to try, any help would be appreciated.
I made the library that makes static classes mocking possible:
class MyClass {
public static $myPromoCodes = 'myPromoCodes';
public static function isUseful($item) {
$objPromo = self::$MyPromoCodes::Load($item->SavedSku);
if (!is_null($objPromo)
&& ($objPromo->PromoType == MyPromoCodes::Interesting_Promo_Type)) {
return true;
}
return false;
}
}
class MyClassTest extends \PHPUnit_Framework_TestCase
{
public function testSomething()
{
$myClass = Moka::stubClass('MyClass');
$myClass::$myPromoCodes = Moka::stubClass(null, ['::Load' => (object)[
'PromoType' => MyPromoCodes::Interesting_Promo_Type
]]);
$this->assertTrue($myClass::isUseful((object)['SavedSku' => 'SKU']);
$this->assertEquals([['SKU']], $myClass::$myPromoCodes->moka->report('::Load'));
}
}
To start with you cannot mock static method with PHPUnit. At least not with 4.x and 5.x.
I would suggest a DI approach like this:
class MyClass
{
private $promoCodesRepository;
public function __construct(MyPromoCodesRepository $promoCodesRepository)
{
$this->promoCodesRepository = $promoCodesRepository;
}
public function isUseful(MyItem $item)
{
$objPromo = $this->promoCodesRepository->Load($item->SavedSku);
// ...
}
}
Here you can easily mock the Load method.
Unfortunately the "static" approach creates a lot of issues during tests so it is better to avoid it whenever possible.
I am looking to incorporate a testing framework into a project I am building and came across Enhance PHP which I like but I am having some difficulty finding relevant information on-line since "enhance php" is such a commonly used phrase.
Has anyone worked with this framework that might be able to point me toward some helpful guide? Have you worked with a unit test framework that you think is amazingly better?
Thanks in advance.
In response to Gotzofter, this is the class to be tested:
<?php
include_once('EnhanceTestFramework.php');
class ExampleClass
{
private $OtherClass;
function __construct($mock = null)
{
if ($mock == null)
$this->OtherClass = new OtherExampleClass();
else
$this->OtherClass = $mock;
}
public function doSomething()
{
return $this->OtherClass->getSomething(1, 'Arg2');
}
}
class OtherExampleClass
{
public function getSomething()
{
return "Something";
}
}
class ExampleClassTests extends \Enhance\TestFixture
{
public function setUp()
{
}
public function tearDown()
{
}
public function verifyWithAMock()
{
$mock = \Enhance\MockFactory::createMock('OtherExampleClass');
$mock->addExpectation(
\Enhance\Expect::method('getSomething')
->with(1, 'Arg2')
->returns('Something')
->times(1)
);
$target = new ExampleClass($mock);
$result = $target->doSomething();
\Enhance\Assert::areIdentical("Something", $result);
$mock->verifyExpectations();
}
}
\Enhance\Core::runTests();
look at my constructor for ExampleClass.
Because enhance-php's site example injects the $mock object by calling new ExampleClass($mock), I am forced to change my ExampleClass constructor to handle a $mock as an input parameter.
Do I have to handle this for all classes that I want to subject to unit testing with the framework?
Thanks.
This:
function __construct()
{
$this->OtherClass = new OtherExampleClass;
}
Should be:
function __construct($otherClass)
{
$this->OtherClass = $otherClass;
}
Your mock is never injected at this point in your test:
$target = new ExampleClass($mock);
One thing I would recommend no matter what testing framework you are using is type-hinting against the expected class, or interface.
<?php
class ExampleClass
{
private $OtherClass; // OtherClass instance
public function __construct(OtherClass $OtherClass=null)
{
// ...
}
}
I'm no di expert, but I don't see the problem in letting each class call new if an instance isn't provided for a particular dependency. You could also of course take the approach where you use setter methods to configure dependencies.
<?php
class class ExampleClass
{
private $OtherClass; // OtherClass instance
public function setOtherClass(OtherClass $OtherClass)
{
$this->OtherClass = $OtherClass;
}
}
It is lame that the ExampleClass in the sample code doesn't even define the doSomething method from the ExampleDependencyClassTests, but if I understand correctly it looks like Enhance PHP is not forcing you to take a particular style of dependency injection. You can write the test class however you want, so for example if you took the setter method approach I mentioned above, you could change the example mock code to
<?php
class ExampleDependencyClassTests extends \Enhance\TestFixture
{
public function verifyWithAMock()
{
$mock = \Enhance\MockFactory::createMock('ExampleDependencyClass');
$mock->addExpectation(
\Enhance\Expect::method('getSomething')
->with(1, 'Arg2')
->returns('Something')
->times(1)
);
$target = new ExampleClass();
$target->setExampleDependencyClass($mock);
$result = $target->doSomething();
$mock->verifyExpectations();
}
}
Of course it would probly make sense to make the appropriate revisions to the ExampleClass!
<?php
class ExampleClass
{
private $ExampleDependencyClass;
public function addTwoNumbers($a, $b)
{
return $a + $b;
}
public function setExampleDependencyClass(
ExampleDependencyClass $ExampleDependecyClass
) {
$this->ExampleDependecyClass = $ExampleDependecyClass;
}
public function doSomething($someArg)
{
return 'Something';
}
}
I've worked with PHPUnit quite a bit, and honestly you'll have to face the same challenges with Mocks there. My 2 cents, try to model your tests without Mocks if possible ;)
There is a tutorial on NetTuts titled Testing Your PHP Codebase With Enhance PHP, which will definitely help you to get started.
And there is a Quick Start Guide on Enhance PHP.