Summary
I have a class that uses debug_backtrace() for reporting where a method was called. The method may be called in third party code either directly or via some helper method that's in the same package as the class in question. In the latter case the method will report the line from the third party code and not from the helper class, thus the index at which the actual caller is in the results varies, so I'd like to include a test in my PHPUnit suite which would ensure that the correct index is found in each use case.
Is there a way I could automatically and reliably figure out the correct line in the test file that my method should return?
tl;dr, code plz
Here's some code to demonstrate. This is just the most bare bones example of the situation, please disregard that it doesn't really make sense. The actual use case is more complex and it's very much possible, likely even, that at some point in time I will mess something up and the traces end up giving wrong results. I'd like my tests to catch that.
Classes.php:
<?php
class Tracer
{
public function send()
{
$trace = debug_backtrace();
if ($trace[1]['class'] === 'Helper') {
$calledOnLine = $trace[2]['line'];
} else {
$calledOnLine = $trace[1]['line'];
}
return $calledOnLine;
}
}
class Helper
{
public static function send()
{
$Tracer = new Tracer;
return $tracer->send();
}
}
TracerTest.php:
<?php
class TracerTest extends PHPUnit_Framework_TestCase
{
public function testGetProperCallerInfo()
{
$Tracer = new Tracer;
$result1 = $Tracer->send(); // $TargetLine1
$result2 = Helper::send(); // $TargetLine2
$this->assertEquals($TargetLine1, $result1);
$this->assertEquals($TargetLine2, $result2);
}
}
Ideas
I could hardcode the line numbers into the test, and then update them each and every time something higher up in the test file changes. Even if I separate this particular test to its own file it's still going to break at some point, and besides it's such an ugly solution I wouldn't be able to look myself in the eye anymore.
Another method might be to tag the target lines with comments, load the test file to an array with file() and find the tags in the array. This is still somewhat fragile, and not really all that elegant.
Ask yourself, what is the behavior you are trying to test?
If you want to ensure you are generating exact strings, then maybe test fixtures is a good idea.
Probably more easier to maintain is to assert parts of the string which can be easily inferred.
Alternatively, you can mock the function call itself with libraries such as
https://github.com/instaclick/ICBaseTestBundle/blob/master/Test/Helper/Unit/FunctionHelper.php
http://www.workhabit.com/labs/mock-function-testing-drupal
Since I haven't been able to find a better solution, I ended up putting together a small package that let's me find lines reliably from the test source.
Accepted answer is still up for grabs if you can come up with a better solution!
In case someone is interested, the package I made is here.
Related
I am quite new to OOP, so this is really a basic OOP question, in the context of a Laravel Controller.
I'm attempting to create a notification system system that creates Notification objects when certain other objects are created, edited, deleted, etc. So, for example, if a User is edited, then I want to generate a Notification regarding this edit. Following this example, I've created UserObserver that calls NotificationController::store() when a User is saved.
class UserObserver extends BaseObserver
{
public function saved($user)
{
$data = [
// omitted from example
];
NotificationController::store($data);
}
}
In order to make this work, I had to make NotificationController::store() static.
class NotificationController extends \BaseController {
public static function store($data)
{
// validation omitted from example
$notification = Notification::create($data);
}
I'm only vaguely familiar with what static means, so there's more than likely something inherently wrong with what I'm doing here, but this seems to get the job done, more or less. However, everything that I've read indicates that static functions are generally bad practice. Is what I'm doing here "wrong," per say? How could I do this better?
I will have several other Observer classes that will need to call this same NotificationController::store(), and I want NotificationController::store() to handle any validation of $data.
I am just starting to learn about unit testing. Would what I've done here make anything difficult with regard to testing?
I've written about statics extensively here: How Not To Kill Your Testability Using Statics. The gist of it as applies to your case is as follows:
Static function calls couple code. It is not possible to substitute static function calls with anything else or to skip those calls, for whatever reason. NotificationController::store() is essentially in the same class of things as substr(). Now, you probably wouldn't want to substitute a call to substr by anything else; but there are a ton of reasons why you may want to substitute NotificationController, now or later.
Unit testing is just one very obvious use case where substitution is very desirable. If you want to test the UserObserver::saved function just by itself, because it contains a complex algorithm which you need to test with all possible inputs to ensure it's working correctly, you cannot decouple that algorithm from the call to NotificationController::store(). And that function in turn probably calls some Model::save() method, which in turn wants to talk to a database. You'd need to set up this whole environment which all this other unrelated code requires (and which may or may not contain bugs of its own), that it essentially is impossible to simply test this one function by itself.
If your code looked more like this:
class UserObserver extends BaseObserver
{
public function saved($user)
{
$data = [
// omitted from example
];
$this->NotificationController->store($data);
}
}
Well, $this->NotificationController is obviously a variable which can be substituted at some point. Most typically this object would be injected at the time you instantiate the class:
new UserObserver($notificationController)
You could simply inject a mock object which allows any methods to be called, but which simply does nothing. Then you could test UserObserver::saved() in isolation and ensure it's actually bug free.
In general, using dependency injected code makes your application more flexible and allows you to take it apart. This is necessary for unit testing, but will also come in handy later in scenarios you can't even imagine right now, but will be stumped by half a year from now as you need to restructure and refactor your application for some new feature you want to implement.
Caveat: I have never written a single line of Laravel code, but as I understand it, it does support some form of dependency injection. If that's actually really the case, you should definitely use that capability. Otherwise be very aware of what parts of your code you're coupling to what other parts and how this will impact your ability to take it apart and refactor later.
To make it simple, I have noticed that PHP doesn't seem to offer any magic constant for determining what the name that a trait has been changed to in a class. Since this sounds confusing to me in words, I will give an example, as it is rather easy and would expect it to be somewhere in the new PHP 5.5, I don't see a way to doing it. So here it is:
Say we have some class, that uses some trait that conflicts with some function inside the class, example:
class SomeClass {
use \Name\Space\SomeTrait { SomeFunction as private NewFunctionName; }
function SomeFunction() {
$this->NewFunctionName();
}
}
Since, obviously this class has the "SomeFunction" function, and we are aware that inside of SomeTrait we have included, is a function that is matching by name to a function we have inside this class. Now, since the "SomeFunction" came into this class via a trait inside of a \Name\Space, these 2 functions do 2 different things, but happen to use the same name, and inside of either another function or literally our 'SomeFunction', we then use the "SomeFunction" from our trait, by calling it by the "NewFunctionName".
So hopefully I haven't lost anyone here, as here is what my question comes down to in the above scenario. Within the \Name\Space\SomeTrait\SomeFunction(), how could one get the "NewFunctionName" that this trait function was assigned too? One would think to use a magic method, such as __FUNCTION__, or __METHOD__, or even __TRAIT__, except none of these give the expected result, so does anyone know a way to get this information without passing it to the function as a parameter and resulting in a hacky code? Maybe PHP 5.6 needs to add a new Magic Constant __AS__, or adjust the result of __TRAIT__, I dont understand why __TRAIT__ and __FUNCTION_ need to return the same information (or nearly the same information). Any help would be awesome, if it is an unconventional hacky method, Im interested in seeing my options, until I can open up a bug report with php regarding this. (if it is truly a bug)
edit:
My current, least hacky, method seems to be,
debug_backtrace()[0]['function']
and though it works, I feel it is doing a lot to get a simple string, especially if you use the function a lot. :/
This is the solution I ended up using. It's not very good but probably better than using the debug_backtrace function.
Problem example:
Trait ExampleTrait {
protected function doSomethingRecursive() {
// this is a problem because it could be renamed
$this->doSomethingRecursive();
}
}
Solution example:
Trait ExampleTrait {
protected function doSomethingRecursive() {
// this is a problem because it could be renamed
$this->__internal_doSomethingRecursive();
}
private function __internal_doSomethingRecursive() {
// this works because the class would have
// used and renamed the above function
// but this "internal" function *should*
// remain available under it's original name
$this->__internal_doSomethingRecursive();
}
}
Of course it's possible to break this but for most cases it should be fine. You could also include the name of the trait in the internal function name to further prevent conflicts.
Your current solution is going to be the best, with a couple tweaks:
debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS,1)[0]['function']
The DEBUG_BACKTRACE_IGNORE_ARGS and the frame limit of 1 there will make the backtrace shallow, faster, and use less memory.
Lets suppose I want to auto load classes, so far thats normal. Now, lets suppose we are in "test" environment. I want to load other classes instead, this classes act just like others, but with some modifications. So, originally
class A
{
public function method()
{
return rand(1,10);
}
$a = new A(); // in the meantime autoloader finds and load class A
$a->method();
and what I want:
class Adev
{
public function method()
{
something::log ('method running');
return rand(1,10);
}
}
$a = new A(); // and then I dont need "A" class but "Adev"
$a->method();
so somewhat "renaming" method should be used, instead of refactoring the code.
use get_declared_classes and eval e.g.
$classes = get_declared_classes();
foreach ($classes as $class)
eval("\$".$class." = new ".$class."();");
Updated (and messy)
A couple of possible ways of tackling your issue - may be worth a closer look. At the bottom, there is also a personal consideration/suggestion.
The shortest of short fixes might just apply in your case: instead of having PHP's autoloader looking for the .php extension, you could set it so that, when testing your code, you are in fact looking for files ending on dev.php, so that class A, when passed as a string to the autoloarder becomes Adev.php:
if (RUNNING_ENV=== 'test')
{
spl_autoload_extensions('dev.php');
}
Not sure, but perhaps use getenv() to determine if you're running on test/dev or production environment, and based on that register different autoloaders? spl_autoload_register is a handy function for that:
function prodAutoload($class)
{
//normal loading
}
function tstAutoload($class)
{
$class .='Dev';
//add Dev to className, proceed as you normally would?
}
if (getenv('RUN_ENV') === 'prod')
{
spl_autoload_register('prodAutoload');
}
else
{
spl_autoload_register('tstAutoload');
}
Of course, there will be a bit more to it, than just these few lines. But with this approach, you don't need differently named classes: A will be loaded form either the dev or live file, based on the autoloader/extension.
That way, you can at least keep type-hinting all the way through, without any issues. Of course, maintainability will be even more of a nightmare: Edit 1 file, make sure to edit the other one, too.
That's why I must say, personally, I wouldn't go through all this trouble of writing different classes for test & live environments. At one point, you'll run into trouble with that...
Suppose you fix a bug in test, but fail to edit the production version? Or the other way 'round... I think you're better off spending a little time in setting up a decent debugger and test environment that will work with the same code, but (for example) not the actual production databases.
Useful links:
manage autoload extensions
workings of default autoloader + examples
register (multiple) custom autoloaders)
Have you considered using namespaces? The code that follows is probably not 100% correct, but the gist of it would be:
# A.php
class A {...}
# ADev.php
class ADev {...}
# script.php
use ADev as A;
$a = new A; # of class ADev
See:
http://php.net/manual/en/language.namespaces.importing.php
Have you considered a simple solution rather than a complex one?
Make Class A do what ADev does i.e. include the logging function and forget about duplicating all your classes.
Make the something:: class test a enviroment variable or a simple config variable.
So something::debug tests $DO_I_WANT_DEBUGGING_ON = TRUE, if it is then you do the logging otherwise you do not.
I understand that 100% code coverage is just a goal to shoot for, but it's annoying to have a line containing a closing brace counted as not covered because it follows a method call whose sole purpose is to throw an exception. Here's a simple example from my base test case class to demonstrate:
function checkForSkipAllTests() {
if (self::$_skipAllTests) {
self::markTestSkipped(); // [1] always throws an exception
} // [2] shown as executable but not covered
}
Since [1] always exits the method, line [2] is not actually reachable. Is there any way to tell Xdebug this by annotating the markTestSkipped() method itself?
Your pull request got merged so starting with php-code-coverage 1.1.2, which should come around rather soon (with PHPUnit 3.6.3 or 3.6.4) one will be able to write:
private static function checkForSkipAllTests() {
if (self::$_skipAllTests) {
self::markTestSkipped();
} // #codeCoverageIgnore
}
Also in the further away future when xDebug will be able to provide 'Conditionals' coverage i think i remember discussion about making the whole issue going away with that refactoring as the closing brace will just count as 'covered' when the last statement in a function terminates the function... But I might be wrong on that
You can surround the line with stard/end comments to have PHP_CodeCoverage ignore it, but that means doing it everywhere the method is called.
function checkForSkipAllTests() {
if (self::$_skipAllTests) {
self::markTestSkipped();
// #codeCoverageIgnoreStart
}
// #codeCoverageIgnoreEnd
}
This is a maintenance nightmare and prone to error. I would really like to avoid this solution.
I understand that 100% code coverage is just a goal to shoot for, but it's annoying to have a line containing a closing brace counted as not covered because it follows a method call whose sole purpose is to throw an exception. Here's a simple example from my base test case class to demonstrate:
Indeed, 100% code coverage is not a goal, but it's nice to have, especially if it takes you zero time to make it so. I do wonder though; your tests are not the files that are to be tested. I never test my tests, nor am I interested in their code coverage. I already know which tests are done, which succeeded, which failed and which are skipped. This is what PHPUnit brings to the table for me; .....S...F is enough feedback.
My tests are in a separate directory, which isn't included in code coverage; it just seems useless to do so, in my eyes. Anyway, if you're sold on having code coverage reports on your testcases, you might want to simply get rid of the }, like so:
function checkForSkipAllTests() {
if (self::$_skipAllTests)
self::markTestSkipped();
}
Yeah, I know that having an if without curly brackets will make me the least cool person answering your question, but it seems like a much easier solution than having some annotations which magically work.
I'm looking to create an on-site API reference for my framework and application.
Basically, say I have a class file model.class.php:
class Model extends Object {
... code here ...
// Separates results into pages.
// Returns Paginator object.
final public function paginate($perpage = 10) {
... more code here ...
}
}
and I want to be able to generate a reference that my developers can refer to quickly in order to know which functions are available to be called. They only need to see the comments directly above each function and the declaration line. Something like this (similar to a C++ header file):
// Separates results into pages.
// Returns Paginator object.
final public function paginate($perpage = 10);
I've done some research and this answer looked pretty good (using Reflection classes), however, I'm not sure how I can keep the comments in.
Any ideas?
I want to keep the current comment formatting. Myself and the people who are working on the code hate the verbosity associated with PHPDocumentor. Not to mention a comment-rewrite of the entire project would take years, so I want to preserve just the // comments in plaintext.
Why don't you just run PHPDocumenter on the code?
PHPDoc is identical to JavaDoc, and will retain all the parameters and comments in the docblock above the function.
EDIT: Since you don't want PHPDoc, I think it would probably be easiest to write a RegEx parser to do it, since Reflection won't provide you with any surrounding comments.
PHP code to get the comment:
if(preg_match("#//(.+)$#",$line,$match)>0)
{
echo "Comment is: ".$match[1];
}
this is what phpdoc is for. you comment your code a specific way, following certain rules, and when you're done you run your code through a parser. It outputs exactly what you're looking for.