Here I am writing a small app with the sole intent of acquiring better OOP/testable code habits. And loving it, btw!
I am striving to assimilate the methodology behind developping 'testable code', mostly by reading posts from unit testing evangelists such as Sebastien Bergmann, Misko Hevery and Giorgio Sironi.
Among the hardships I assimilated is the misuse of static methods, objects that depend on objects that depend on objects. Currently, I am stuck on global wide constants. At the start of my application I load one single CONSTANT that simply sets the application mode in debug or prod:
/**
* APP_MODE values:
*
* PROD Production, no errors displayed email sent on error, logs to
* logs/app-<date-time>.log.
*
* DEBUG: All warnings and errors displayed, emails disabled and log messages
* sent to console. Whether in-memory modifications to configuration
* data are allowed
*/
define("APPMODE", "DEBUG");
How can one test app classes for proper error handling depending on the state of this constant?
At first my thought was to simply move the global constant to a class constant instead in my init class and that solves the case for this particular class, but I am not satisfied with this procedure. I mean, should one simply avoid sitewide constants that are not "truly" constants in the strict sense of one possible value always?
I can't imagine testers have to write 2 test suites for every class, ie initClassDebugTest.php and initClassProdTest.php unless phpUnit can somehow reset global state? Should global constants used this way be avoided? I have a weird gut feeling I should not use a constant here at all. I would be very curious to know how test savy coders out there would handle global defines with 2 possible values at runtime.
It mainly depends on how you create your objects and how many classes access this APPMODE.
Let's see what APPMODE does:
* DEBUG: All warnings and errors displayed, emails disabled and log messages
* sent to console. Whether in-memory modifications to configuration
* data are allowed
Something like this usually gets solved by passing in "DebugLogger" and "DontSendEmailMailer" to the classes that need to send mail.
By doing this you only need a few factories (or whatever you use to create your object graph) that need to know about "production" vs "development".
The classes that do your actual business logic should not know if it's run in production or not. That would mean the developer of each class would have to care about that and every class would need to be changed if you .. say.. have a "staging" environment. It introduces lots of global state that, like you discovered, is hard to test.
If errors should be displayed or not to be decided in your models in your php.ini or in your application bootstrap and should not concern the rest of your application.
I'd start of moving that "debug" functionality out of classes that need your APPMODE setting and move that into to dedicated (logging, mailing, ...) classes. The real thing (that actually sends mail) and the debug thing (that maybe writes mails to disk?). Both of those classes can be tested properly (testing a null logger is pretty easy ;) ) and you need to make that switch only a few times.
if($config->evironment() == "debug") {
$logger = new DisplayEverythingLogger();
} else {
$logger = new OnlyLogErrorsToTextfileLogger();
}
$neededModel = new ClassThatDoesActualWork($logger);
$controllerOrSomething = new ControllerOrWhatEveryDoesYourWorkflow($neededModel);
$controllerOrSomething->dispatch();
and so on. You can reduce the amount of global state step by step until you get rid of the define and only have a configuration setting.
When it comes to testing the class that does work you now won ether way because the logging is injectable and you can pass in a mock for testing.
So far for a first idea.. if you think that doesn't work for you maybe provide an example where APPMODE is used
Use phpunit's --bootstrap option. It runs the given file prior to any tests being done.
Related
I'm writing an yii2 app that is mainly used as an console application. I have components or (micro)services that fetches some data from a server, handle them and save the information I need to db. Of course there are several for loops and in these loops I output my current progress with the use of yii\helpers\Console::updateProgress or just echo to watch the progress and testing output (like starting with xxx products). On some events I log important informations with Yii::info() or Yii::error() and so on. Normally a cron handling tasks like pullProductUpdates or something else and i.
However in some cases I need the method (i.e. pullProductUpdates) in my webapplication too. But then there must not be any echo command active or Console::updateProgress commands.
Of course I don't have problems with the logging methods from Yii because I configured the log targets and they will not echoing something. But I'm uncertain how to handle the echo commands...
One way is to check wether $_SERER['REMOTE_ADDR'] is set or not. In console it will evaluate to null so I can warp an if {} else {} around. A probably better solution is to write a log() or progress() method. A trait could be useful?
So how should I design a solution? Is there any pattern for this? Should my services implement an interface like loggable or proressable? Or use an Logger/ Progress objects and use some kind of DI (dependency injection)? Because I don't want to write those log() or progress() methods functions more than one time. Besides I can't use a progress function in a webapplication. One reason is I don't know how to to that (if its possible with php here), but this would be another question.
Thanks in advance.
As a PHP programmer you should be aware of and use the PSR. In this case you should use dependency injection and the LoggerInterfaces.
For web application you should configure your composition root to use a logger implementation that logs to a file. For console application you should log to the terminal.
The composition root is the place where you configure your Dependency Injection Container (DIC). See more about Yii DIC here.
In order to do that you should be able to switch between these two composition roots by an environment variable or by php_sapi_name.
I am trying to implement some unit tests into a legacy PHP application.
There have been a number of challenges with this, but in particular for this question, I am currently looking at a small class that manages the app config.
The class interface is pretty simple; it does the following:
The constructor calls a Populate method, that uses our Recordset class to load the config for the requested module from the database.
Get method, which returns a specified config value.
Set method, which sets the config value in memory.
Save method, which writes the config update(s) back to the DB.
Testing the Get/Set methods is straightforward; they map directly to private array, and work pretty much as you'd expect.
The problem I have is with testing the database handling. The class uses a number of fields on the config table (module name, language, etc) to determine which config items to load and in what priority. In order to do so, it constructs a series of elaborate SQL strings, and then makes direct calls to the DB to get the correct config data.
I have no idea how to go about writing a unit test for this. Aside from the Get/Set methods, the class consists pretty much entirely of building SQL strings and running them.
I can't see a way to test it sensibly without actually running it against a real DB, and all the issues that go with that -- if nothing else, the complexity of the config loader would mean I'd need at least seven or eight test databases populated with slightly different config. It seems like it would be unmanageable and fragile, which would defeat the point somewhat.
Can anyone suggest how I should proceed from here? Is it even possible to unit test this kind of class?
Many thanks.
I must say I'm not sure I agree that unit tests would be somewhat pointless without hitting the database here. My goal would be to get the business logic that produces the SQL under test, without involving your database. Here is an example of what I'm talking about:
class Foo {
// ... Getters and setters for your config ...
public function doSomeBusinessLogicThenHitDb()
{
$sql = 'SELECT * FROM mytable WHERE ';
$sql .= $this->_doSomethingComplicatedThatInvolvesParsingTheConfig();
$this->_queryDb($sql);
}
protected function _queryDb($sql)
{
// Do something with a PDO or whatever
}
}
Having abstracted the _queryDb() bit to a separate function, you can then write this test:
public function testMyClassUnderSomeCircumstances()
{
// Set up config
$exampleConfig = // whatever
// Set up expected result
$whatTheSqlShouldLookLikeForThisConfig = 'SELECT ... WHERE ...';
// Set up a partial mock that doesn't actually hit the DB
$myPartialMockObject = $this->getMock('Foo', array('_queryDb'), array(), '');
$myPartialMockObject->expects($this->once())
->method('_queryDb')
->with($whatTheSqlShouldLookLikeForThisConfig);
// Exercise the class under test
$myPartialMockObject->setConfig($exampleConfig);
$myPartialMockObject->doSomeBusinessLogicThenHitTheDb();
}
The point of this test is to test the business logic that produces your SQL - not to test your database itself. By putting the expectation in place that the resulting SQL must look like whatever it must look like, you are ensuring that your tests will fail if innocent refactoring of _doSomethingComplicatedThatInvolvesParsingTheConfig() accidentally breaks your code, by making it produce different SQL from what it used to.
If testing the whole application, including its database, is your goal, try a proper integration testing suite like Selenium. Unit tests monitor individual classes and tell you when they've stopped behaving as they're supposed to. You will face problems with speed of execution, and with error localisation (i.e. is the bug even in the code, let alone the class under test, or is it a DB thing?), if you let them overreach.
One straight forward to better test these things is to give your config class an object to access the database, so you can run any config with the real database or just some mock of it that writes to memory instead or even lightweight files if you need persistence for example.
That can be done with creating an adapter with a defined interface. The first adapter you write is for your database.
When the config object is created, you pass in the adapter. As it has a defined interface, the config class can work with any adapter that has that interface. First of all the database.
Then you either mock an adapter or write an adapter of it's own for your tests. Inside your tests, you don't use the database adapter, but the test adapter.
You then can unit-test the config class independent of the database.
I would like to extend the cakephp logging facility.
Using
$this->log($msg, $level)
you can log a $msg with $level to tmp/logs/$level.log.
How I would like to use logging is:
Separate functions for different levels, e.g. $this->debug($msg) for $this->log($msg, 'debug') and $this->error($msg) for $this->log($msg, 'error') etc. for logging.
Automatically put the name of the class in front of a message, e.g. $this->debug($msg) will lead to "MyClass: $msg" if $this is of type "MyClass".
I know I can extend the functionality by extending AppModel, AppController etc., but as I need the functionality everywhere in my application, I would rather need to extend cakephp's Object - but didn't find a stable mechanism for that (don't want to change it in the cake/ folder).
I though about implementing a new class for that functionality, but I'm not sure how to make that available in cakephp.
Could you please give me some hints where/how I can implement these extensions neatly?
Global convenience methods
Well, you can't really do monkey patching in PHP (5.2 at least), and that is probably a good thing for the the developers that have to maintain your code after you are gone. :)
CakePHP - being an MVC framework with strict conventions - makes it hard for you to break the MVC paradigm by only allowing you the extend the parts you need in isolation (ie. AppModel, AppController, etc.) and keeping the object-orientated foundation untouched in the core (making it hard to add code that "can be used everywhere" for potential misuse).
As for adding functionality that transcends all the MVC separation, the place for this is app/config/bootstrap.php. When you place code here it seems clear that it is not part of the framework (quite rightly so), but allows you to add these sort of essentials before CakePHP even loads. A few options of what to do here might be:
Create a function (eg. some custom functions such as error() that call CakeLog::write() in the way you like.)
Load a class (eg. load your very own logging class called something like.. Log, so you can call Log::error() in places)
See below:
The logger API
Cake does allow for many customisations to be made to things like the logger, but unfortunately the API exposed to us is already defined in the core in this case. The API for logging in CakePHP is as follows, and you can use either approach anywhere you like (well, the former only in classes):
$this->log($msg, $level) // any class extending `Object` inherits this
// or
CakeLog::write($level, $message); // this is actually what is called by the above
The arbitrary $level parameter that you are trying to eliminate is actually quite a powerful feature:
$this->log('Cannot connect to SMTP server', 'email'); // logs to app/logs/email.log
// vs
$this->email('Cannot connect to SMTP server'); // ambiguous - does this send emails?
We just created a brand new log type without writing an extra line of code and it's quite clear what the intention of our code is.
Customising the logger
The core developers had the foresight to add a condition allowing us to completely replace the logger class should we wish to:
function log($msg, $type = LOG_ERROR) {
if (!class_exists('CakeLog')) { // winning
require LIBS . 'cake_log.php';
}
// ...
As you can see, the core CakeLog class only gets instantiated if no such class exists, giving you the opportunity to insert something of your own creation (or an exact copy with a few tweaks - though you would want to sync changes with core - manually - when upgrading):
// app/config/bootstrap.php
App::import('Lib', 'CakeLog'); // copy cake/libs/cake_log.php to app/lib/cake_log.php
The above would give you full control over the implementation of the CakeLog class in your application, so you could do something like dynamically adding the calling class name to your log messages. However, a more direct way of doing that (and other types of logging - such as to a database) would be to create a custom log stream:
CakeLog::config('file', array(
'engine' => 'FileLog', // copy cake/libs/log/file_log.php to app/libs/log/file_log.php
));
TL;DR - Although you can load your own code before CakePHP bootstraps or for use in isolation in each of the MVC layers provided, you shouldn't tamper with the object hierarchy provided by the core. This makes it hard to add class methods that are inherited globally.
My advice: use the API given to you and concentrate on adding more features instead of syntactical subtleties. :)
In addition to what deizel said (great writeup, by the way, deizel), you don't have to use Cake's logger. You're welcome to use any logging system you'd like. Choosing an existing logging framework that you like would probably be the safest bet. bootstrap.php is a good place to do any require calls or initializations.
Otherwise, if you'd like to do things 'in' Cake, I'd recommend creating a plugin with a trio of logging interfaces: a Component, a Behavior, and a Helper. That way the logging functionality would be available in your models, views, and controllers. As for how to code it, I like the idea making the cake classes thin proxies to your real logging class, and using the magic method __call() in your proxies to parse logging requests and environment, then forward on that information to your logger to handle uniformly.
You'd be able to write things like $this->MyLogger->oops("stubbed my toe") and potentially have an oops.log file with your messages and whatever additional information (calling controller/view/model, time&date, etc.) you'd like included.
So I have a large class that I'm refactoring to be more flexible.
well over 100 configurable properties (variables) in a INI file. Some are dev settings and others are production settings.
Currently I use a environment flag to pull the INI settings I need (either dev or prod).
All the INI fields are mandatory and must be set. (currently using setters for this).
So what makes more sense?
A: to keep the current setup
B: Move to another type of configuration setup (like xml or something)
C: Move all settings into the script itself and remove the INI parsing and validation code (setters)
D: suggestions?
Bonus question:
I was reading this over the weekend: http://berryllium.nl/2011/02/getters-and-setters-evil-or-necessary-evil/ and wanted to know how I could follow more of this type of development style but confused as how to not use getter/setter functionality with setting mandatory fields? I understand the need to getters/setters and wanted to know if I'm implementing them correctly?
I use the INI for stuff like DB settings, validation limits (think spending limit of $100 but could change), large array (static values like the 50 US States but with the ability to add US territories as well)
If you want to keep settings
readable, keep the current setup or
move settings into PHP script (but do
not hardcode them into classes).
If
you want to increase performance -
use JSON format or PHP script -
json_decode works faster, PHP script
works faster and can be easily cached
by APC.
As variant, you can parse
the settings file once and put all
settings in cache (APC or memcache).
Also. I think nobody cares, but I have my opinion about getters and setters :)
Getters and Setters aren't evil. Idea of Encapsulation is not just hide fields, but hide, how class works. Getters and setters can be declared in interface, so you can replace one object by another - and it is what for encapsulation was invented!
Let's take example from article of Berry Langerak - withdraw and deposit it's setters. All this code can be successfully done in setBalance method, almost nothing will be changed. All these checks, comparison - it's usual work of setters.
Why public fields are evil? Because object can't control their changing and because they can't be declared in interfaces. Getters and setters can do it, so it's perfect tool of OOP.
Accessors can be written silly, of course, but it doesn't mean that they are evil. Any method in the class can be written silly.
Interesting question.
I highly doubt that all of those variables are/could be different between your development area and your production area. Not every variable that you might change when debugged would have to be stored inside a config file.
I would advice using constants for the most variables.
You have auto complete, tons of IDE options and it's easily editable. Not to mention it's more logic to split them up into different files then to parse tons of ini's.
I am just now switching back to PHP after enterprise open-source Java development for three years. Now I am tasked with updating our platform for better logging.
I now understand better how the PHP object lifecycle regarding when objects are garbage collected and have trapped my problem. I am trying to invoke the logger after its already been destructed, when a fatal error occurs. My question is, how do I fix this? How can I stop an object from being destroyed until the end of the request?
Ideally I would like to keep this logger around in memory like I would in Java but is that even possible with PHP? Is there anything shared between two different threads or requests?
With PHP, each request is processed by a different process -- which means you quite cannot keep some object arround between requests (you could serialize it and store it in a file or something like that, and un-serialize it when another requests comes ; but that's not really the way things are generally done)
This means each time your PHP script receives a request, you have to re-instanciate your logger.
Now, if you want to use your logger from several different classes/methods/functions in the same script, you have to know that variables are not global "by default" in PHP : a variable declared outside of a function is not accessible from inside a function, unless you said so using the global keyword.
In this kind of situation, when you want one and only one instance of a specific class (your logger) available from anywhere in your application, people often use the Singleton Design Pattern.
It'll allow to use something like this :
My_Logger_Class::log('blah');
From any portion of your code, and the log method will deal with :
instanciating the class if there was not already one existing instance
the actual logging
And, yes, the first time this method is called for one HTTP request, it'll have to re-open the log file (if logging to a file).
As a sidenote : there are already some existing great logging components, like PEAR::Log or Zend_Log.
Maybe using one of those might help you spend less time re-inventing some wheel ?