In-Portal is cool, but testing it isn't. This is because it's core functionality wasn't designed the way, that would allow easy testing.
Mocking
We need to ensure, that before even writing any tests the In-Portal code is adapted to allow that. Here is the plan:
we adapt, that code, that can be adapted easily to allow testing in isolation (test each class separately)
what can't be adapted right now will be tested using integration tests using Mink/Selenium later
Main concept of testing in isolation is by using Mock objects. These are special stub-type objects, that replace real object, that testable class used to depend on, but they just return "null" or whatever we tell them to return. PHPUnit (https://github.com/sebastianbergmann/phpunit/) has already some basic mocking capabilities (http://phpunit.de/manual/3.6/en/test-doubles.html#test-doubles.mock-objects), but compared to dedicated mocking framework called Mockery (see https://github.com/padraic/mockery) they are fairly limited and hard to comprehend.
I won't be describing why Mockery is worth noticing, because it's author already did that in great details on library's home page on GitHub, which I encourage all to visit. However one problem remains: how to make mocked objects have IDE auto-complete of mocked classes plus one, that Mockery framework provides. And I found the answer in Laravel code:
/** * The resolved object instances. * * @var array */ protected static $resolvedInstance; /** * Initiate a mock expectation on the facade. * * @param dynamic * @return \Mockery\Expectation */ public static function shouldReceive() { $name = static::getFacadeAccessor(); if (static::isMock()) { $mock = static::$resolvedInstance[$name]; } else { static::$resolvedInstance[$name] = $mock = \Mockery::mock(static::getMockableClass($name)); static::$app->instance($name, $mock); } return call_user_func_array(array($mock, 'shouldReceive'), func_get_args()); } /** * Determines whether a mock is set as the instance of the facade. * * @return bool */ protected static function isMock() { $name = static::getFacadeAccessor(); return isset(static::$resolvedInstance[$name]) and static::$resolvedInstance[$name] instanceof MockInterface; }
Based on code above we just need to add following 2 methods to kBase class, which is base class of all In-Portal classes. This would result in following benefits:
each class now would have static "shouldReceive" method, that when called will:
create mock object of that class
put it in Factory, so anyone calling it will get a Mock back
in tests we can use original class name (one to be mocked) for IDE auto-complete and all will work, because both original and mocked class have "shouldReceive" method
anywhere in code (e.g. outside of tests) we can alter behavior based on fact, a mock class came in instead of a real class (but won't advise to do so)
Instantiating application
In-Portal and many other frameworks use to initialize Application class in PHPUnit's bootstrap file. This has a downside in terms, that through that shared Application fixture tests, that must be isolated in fact can communicate and produce coupled behavior, that must not be used at all.
However in Laravel Application is initialize before each test being run in a test case right in "setUp" method of a test case. This might slow down tests a bit (if application initialization took much time), but I'm for it anyway.
P.S.
To learn more about Laravel framework visit http://laravel.com/ page.