PHP - disabling classes that are only needed in development - php

I am writing a small script in PHP and am using a Profiler for the development zone to see which functions/actions are slow etc.
$this->profiler = new \Fabfuel\Prophiler\Profiler();
Then I am using it in different methods all over the place (passing it via DI, using League\Container\Container class):
$profilerSeg = $profiler->profilerStartSegment('Page Load','CMS');
...
$profiler->profilerStopSegment($profilerSeg);
Problem is, I only want to use this in development. Thought about an IF statement:
if($this->environment === 'DEV')
$profilerSeg = $profiler->profilerStartSegment('Page Load','CMS');
...
if($this->environment === 'DEV')
$profiler->profilerStopSegment($profilerSeg);
but it looks ugly to have it everywhere. Then I thought about making a "fake profiler" that gets passed if the script is in production so all the calls would return NULL.
What is the best way to handle such a case: a class needed only in development but that should not be loaded in production?

Typical oop uses inheritance
Most approaches define a root class that all (or most) other classes extend
Class main {
private $profiler;
public __construct(){
$this->profiler = new \Fabfuel\Prophiler\Profiler();
// you can comment and uncomment the above line.
}
public profStart(){
if (!empty($this->profiler)) {
$this->profiler->profilerStartSegment($a,$b);
}
}
}
Class someThing Extends main {
// $profiler is already set as part of the constructor
$this->profStart('someThing','strange');
}
Class otherThing Extends someThing {
// but if you want a constructor, you have to daisy-chain the class constructors
public __construct() {
parent::__construct();
}
}
Alternately, swap out an empty object
...but this is generally considered bad practice
Class deadendObject {
public __get($var){}
public __set($var,$val){}
public __call($var,$args){}
// see: http://php.net/manual/en/language.oop5.magic.php
}
if ($profileMe) {
$this->profiler = new \Fabfuel\Prophiler\Profiler();
} else {
$this->profiler = new deadendObject();
}
Set whether to do stuff in the Profiler object itself
Class Profiler {
public $enabled = true;
public function doSomething(){
if($this->enabled) {
// do stuff
}
}
}
$profiler = new \Fabfuel\Prophiler\Profiler();
$profiler->enabled = false;
Best is a combination of inheritance, and having the profiler handle behavior changes.
Class Profiler {
public $enabled = true;
public function doSomething($var = "unknown"){
if($this->enabled) {
// do stuff
}
}
}
Class main {
public $profiler;
public __construct(){
$this->profiler = new Profiler();
$this->profiler->enabled = true;
// better still would be to define this behavior according to a settings file, or environment variables with getenv()
}
}
Class someThing Extends main {
// $profiler is already set as part of the constructor
$this->profiler->doSomething('neighborhood');
}
I ain't afraid 'o no ghosts!

Related

How can I decouple instantiation from implementation for unit testing if DI is not feasible?

I have the following code (simplified and details changed for this question):
class model_to_be_tested {
// an array that holds a collection of thing A
public $array_of_thing_A;
// already doing constructor injection for the data object
public __construct($data_object) {
// details here
}
public function add_new_thing_A($has_relationship) {
$thing_A = new Thing_A();
$thing_A->is_thing = true;
$thing_A->has_relationship_with_thing_B = $has_relationship;
if ($has_relationship) {
$thing_B = new Thing_B();
$thing_A->relationship_with = $thing_B;
}
$this->array_of_thing_A[] = $thing_A;
}
}
In the above example, I have to decouple the instantiation of Thing_A and Thing_B from the add_new_thing method. However, a simple constructor injection will not do for these two classes. This is because I need fresh instances of Thing_A and Thing_B every time add_new_thing is called so that Thing_A can be added to the array_of_thing_A.
How can I make this function unit testable? And more specifically for me to use mocks of Thing_A and Thing_B in testing this function in PHPUnit?
Any suggestions with code example will be appreciated.
Additionally, I would like to mention that Thing_A and Thing_B are used elsewhere in the codebase that I am working with and the code using these classes will eventually need to be unit tested. Solutions that are too localized and would cause repeated code elsewhere will not be too ideal in my situation. Thank you.
As commenter xmike mentioned, you could use the factory pattern. You would inject a factory object through the ctor as well. Then you could have a factory that provides simplified instances of your Thing_A and Thing_B.
class ThingFactory {
public function buildThingA() {
return new Thing_A(); // or MockThing_A if you go the ducktyping route
}
public function buildThingB() {
return new Thing_B();
}
}
class model_to_be_tested {
// an array that holds a collection of thing A
public $array_of_thing_A;
// you could go the typed route and have an interface for this
private $factory;
// already doing constructor injection for the data object
public __construct($data_object, $factory) {
// details here
$this->factory = $factory;
}
public function add_new_thing_A($has_relationship) {
$thing_A = $this->factory->buildThingA();
$thing_A->is_thing = true;
$thing_A->has_relationship_with_thing_B = $has_relationship;
if ($has_relationship) {
$thing_B = $this->factory->buildThingB();
$thing_A->relationship_with = $thing_B;
}
$this->array_of_thing_A[] = $thing_A;
}
}
PHP is such a strange language, you can't assign a class to a variable. But you can do it as a string. Inject ThingA and ThingB on the constructor as strings. You can call new on the string member.
class ThingA {};
class ThingB{};
class model_to_be_tested {
// an array that holds a collection of thing A
public $array_of_thing_A;
private $_thingA;
private $_thingB;
public function __construct($data_object, $thingA, $thingB) {
$this->_thingA = $thingA;
$this->_thingB = $thingB;
}
public function add_new_thing_A($has_relationship) {
$thing_A = new $this->_thingA();
if ($has_relationship) {
$thing_B = new $this->_thingB();
}
$this->array_of_thing_A[] = $thing_A;
}
}
$model = new model_to_be_tested('foo', 'ThingA', 'ThingB');
$model->add_new_thing_A(true);
There's a live version here: https://repl.it/#rmoskal/InconsequentialAnotherGermanshorthairedpointer
Or provide a static constructor for the class.

Monkey patching in PHP 7

This four year old question uses third party libraries which I am a little dubious about.
For testing purposes only, I want to redefine a static method of one of my classes. Take the following example:
class Driver {
public static function getVersion() : string
{
// Retrieves a version from a system executable
return some_system_call();
}
}
class Module {
public function methodToTest()
{
if (Driver::getVersion() === '4.0.0') {
// allow for additional options/methods
} else {
// use a subset
}
}
}
I need for the Driver::getVersion to return different version strings. I would usually mock the class, but since this is neither injected nor an instance, it's not going to work.
I could change the source, adding in methods and property testing, so that the classes being tested would never need to call Driver, but, in my opinion, refactoring the code just to make tests "work" is not a solution.
I'm thinking along the lines of creating another Driver class and somehow loading it in place of the original.
How can I do this?
You might wanna use smth like:
class Module
{
private $version;
public function __construct($version){
$this->version = $version;
}
public function methodToTest()
{
if ($this->version === '4.0.0') {
// allow for additional options/methods
} else {
// use a subset
}
}
}
or another option would be injecting not version but a provider for that (if you know you will have some bit of complicated logic for versioning control -- so you can split the logic between Module and Provider as appropriate):
class Module
{
private $versionProvider;
public function __construct($provider){
$this->versionProvdier = $provider;
}
public function methodToTest()
{
if ($this->versionProvider->getVersion() === '4.0.0') {
// it could be even $this->versionProvider->newFeaturesAreSupported()
} else {
// some other stuff
}
}
}
and still another could be implementing some proxy class like
class Module
{
public function methodToTest()
{
$myMonostateProxy = new MyMonostateProxy();
$version = $myMonostateProxy->getVersion();
if ($version === '4.0.0') {
// allow for additional options/methods
} else {
// use a subset
}
}
}
so you can mock your monostate separately (probably via reflectioning on privtates or via its public interface, anyway don't forget to tearDown it). Real implementation of it would just call that uncontrollable Driver::getVersion().
I think first two options are cleaner but require some efforts for creation (as you need some injection to perform).
Third has that hidden dependecy and is somewhat tricky in testing and thus not quite clean and needs more efforts to maintaine but hides all that choice stuff inside itself making regular usage easier.
class Driver {
private static $testVersion;
public static function setTestVersion(string $testVersion = null)
{
static::$testVersion = $testVersion;
}
public static function getVersion() : string
{
if (static::$testVersion !== null) {
return static::$testVersion;
}
// Retrieves a version from a system executable
return some_system_call();
}
}
You could register a class loader that is somehow made aware of the testing and loads a modified Driver class from a different location.

PHP unit testing External static method call from different class

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.

PHPUnit : memcache_connect not working in PHPunit test case

Note:all code working fine without phpunit
file 1:common.php:
public function setNIMUID( $NIMUID ) {
if(is_bool(Cache::get("$NIMUID"))) {
$user_Array=array("_JID"=>(string)$NIMUID);
Cache::set("$NIMUID",$user_Array);
}
$this->NIMUID=(string)$NIMUID ;
}
File 2 :memcache.class.php
method 1:
protected function __construct(array $servers) {
if(!$servers) {
trigger_error('No memcache servers to connect', E_USER_WARNING);
}
for($i = 0, $n = count($servers); $i<$n; ++ $i) {
($con = memcache_connect(key($servers[$i]), current($servers[$i])))&&$this->mc_servers[] = $con;
}
$this->mc_servers_count = count($this->mc_servers);
if(!$this->mc_servers_count) {
$this->mc_servers[0] = null;
}
}
method 2:
static function get($key) {
return self::singleton()->getMemcacheLink($key)->get($key);
}
method 3:
static function singleton() {
//Write here where from to get the servers list from, like
global $memcache_servers;
self::$instance||self::$instance = new Cache($memcache_servers);
return self::$instance;
}
File 3 : commonTest.php
public function testCommon()
{
$Common = new Common();
$Common->setNIMUID("saurabh4");
}
$memcache_servers variable :
$memcache_servers = array(
array('localhost'=>'11211'),
array('127.0.0.1'=>'11211')
);
Error :
Fatal error: Call to undefined function memcache_connect()
Unit tests should be repeatable, fast and isolated. That means that you shouldn't connect to external services to unit test your classes.
If you want to test that Common is working fine, you should test its behaviour, which in this case is that is calling the Cache class as you'd expect.
For that, you'll need to use mocks. With mocks you can set some expectations, like that the object will be called in a specific manner. If your class is called the memcached class as expected, you can assume your functionality is working fine. How do you know the Cache class is working fine? Because the Cache class would have its own unit test.
In order to use mocks (or stubs), you need to change the way you program and avoid static calles like the one in Cache::set(). Instead, you should use class instances and normal calls. How? Passing the Cache instance to your Common class. This concept is called Dependency injection. Your Common code would look like this:
public function __construct( $cache ) {
$this->cache = $cache;
}
public function setNIMUID( $NIMUID ) {
if(is_bool($this->cache->get("$NIMUID"))) {
$user_Array=array("_JID"=>(string)$NIMUID);
$this->cache->set("$NIMUID",$user_Array);
}
$this->NIMUID=(string)$NIMUID ;
}

Getting started with "Enhance PHP"

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.

Categories