Reputation: 967
I have a custom Lightweight PHP framework that our company uses. To start, I don't want the suggestion to use a third-party framework. I am trying to write unit tests for it but I have stumbled upon a problem. I am relatively new to unit testing, but most of the code is already covered.
I have a loader class that will require files that can't be loaded with a class auto loader (e.g. Configuration files). It has a kind of fallback system where if you don't specify a module to load a particular resource from, it will try to guess based on the current module, then the default module. This has required me (so far as I can see) to check for the existence of a particular file before falling back to the default.
The problem I'm running into is writing a test for the class that doesn't actually depend on any files existing (which is obviously desirable). I've read about virtual file systems and testing, but I'm having trouble seeing how I can utilize it to solve my problem.
I can write an abstraction of the filesystem that I inject into this class, but then that class will have a similar problem.
In pseudo code: if file exists in this module, load it; if not, load the default.
Any suggestions are greatly appreciated.
EDIT:
Here's an excerpt of the code:
class Base implements Loader {
...
protected function _include( $type, $name ){
$identifier = $this->parseName($name);
$path = '/'.$this->resource_types[$type]['directory'].'/'.$this->resource_types[$type]['file_prefix'].$identifier['name'].'.php';
if( $identifier['module'] && is_file(Core::basePath().'/'.Constant::get('MODULE_PATH').'/'.$identifier['module'].$path) ){
Core::requireRoot('/'.Constant::get('MODULE_PATH').'/'.$identifier['module'].$path);
} else if( is_file(Core::basePath().'/'.Constant::get('CORE_PATH').$path) ){
Core::requireRoot('/'.Constant::get('CORE_PATH').$path);
} else {
throw new Exceptions\Load(ucwords($type).' '.$name.' not found');
}
}
....
}
This is a very low-level class, so it has a few static calls that are basically substitutes for or wrappers around native PHP functions. Core::requireRoot
is just a wrapper around the native require
function. Constant::get
is a custom implementation of constants (an alternative to define
, then using the constant directly). Keep in mind I'm not actually testing this method directly. I have public methods such as loadHooks
that I am actually testing that call this method behind the scenes.
Upvotes: 1
Views: 310
Reputation: 967
Thanks both to @hek2mgl and (to a lesser extent) @deceze (who was correct about my static calls), I have a solution to this problem. It is not the kind of solution I originally hoped for, but I think it's the best that can be done. Here goes.
I am going to use temporary files, as per @hek2mgl's suggestion here (github.com/metashock/Jm_Autoloader). I don't think there is any way around this, even with a filesystem abstraction object. That said, I am going to use a filesystem abstraction object which I'll inject into this class, because it will make it easier to re-use the temporary file substitution in other contexts.
I am going to remove the static calls in that function (which I knew better than to do, but it can be easy to convince yourself that "this is such a low-level class it actually doesn't matter"). Instead I will create getter and setter methods for the CORE_PATH and MODULES_PATH, and revert to native call to require. This does not need to be done with a new filesystem object, but I think it is a lot cleaner and more flexible, and more loosely coupled.
Upvotes: 1