For the following function I need to write more test cases, I have already written one, can someone give some ideas, Perhaps to test return values of intermediate function calls.
public function calculateShortestPath($graphObj, $start, $destination)
{
$shortestPath = null;
if ($this->validateParams($graphObj, $start, $destination) == true) {
$result = $this->getAllVerticesAndNeighbours($graphObj);
$vertices = $result[self::VERTICES];
$neighbours = $result[self::NEIGHBOURS];
$vertexCost = array_fill_keys($vertices, self::INFINITY);
$visitedVertices = array_fill_keys($vertices, null);
$vertexCost[$start] = 0;
$vertexQueue = $vertices;
$result = $this->getShortestPath($vertexQueue, $vertexCost, $neighbours, $visitedVertices, $destination);
$vertexCost = $result[self::VERTEX_COST];
$shortestPathVertices = $result[self::SHORTEST_PATH_VERTICES];
$path = $this->getRefinedShortestPath($shortestPathVertices, $destination);
$shortestPath = new ShortestPath($path, $vertexCost[$destination]);
}
return $shortestPath;
}
I have written following case already,
/**
* Test for calculateShortestPath function
*
* #param string|int $start starting point
* #param string|int $destination destination point
* #param array $expectedShortestPath expected shortest path
* #param int|float $expectedCost expected cost
* #dataProvider testCalculateShortestPathDataProvider
*/
public function testCalculateShortestPath($start, $destination, $expectedShortestPath, $expectedCost)
{
$actualResult = $this->shortestPathCalc->calculateShortestPath($this->graph, $start, $destination);
/* #var $actualResult ShortestPath */
$this->assertEquals(
$expectedShortestPath,
$actualResult->getPath(),
sprintf('Incorrect shortest path from %d to %d !', $start, $destination)
);
$this->assertEquals(
$expectedCost,
$actualResult->getCost(),
sprintf('Incorrect shortest path cost from %d to %d !', $start, $destination)
);
}
As a general rule, unit tests should exhibit two traits:
Each test should test one and only one thing
Each test should be as dumb as humanly possible
The reason for the first trait is that if a test fails it will log which test case method triggered the failure. If that method tests lots of things it makes it more of a nuisance to determine the exact failure.
The second trait exists because every time a test fails, then there must be a problem. The problem can only exist in one of two places (ignoring bugs in PHP and its extensions, or in the unit tester): in the code under test, or in the test itself. "Clever" tests will make it difficult to determine which case it is, and you don't want to spend an hour or two hunting down a bug in your class when it turns out it's actually the test that's buggy.
In your example above your current test is pretty good, but it breaks the first rule (there's two tests happening at once). Unless running the method under test is really expensive it might be worth having this test case twice, with the first run asserting the expected shortest path and the second one asserting the expected cost (and if your method does have an expensive run time then there's an incentive to try and optimise it right there :) ).
Your test may also break the second rule because I have no idea what $this -> graph is or how it's set up. Is this an actual business object or just a mockup of it? You might want to look into the mocking an stubbing capabilities of PHPUnit.
Regarding test strategies, there are two general approaches - Black box testing (where you test a unit against its specifications but treat it like you have no knowledge of its internal workings) and glass box (where you use your knowledge of the unit's internal workings to devise tests). My preferred approach it to mainly adopt a black box strategy, build tests around the specs, and then once I've got the spec fully covered move to a glass box strategy to write additional tests that will cover any code paths that the black box test doesn't exercise.
Testing is often about boundaries, as in testing responses to input when that input is both valid and invalid. So for each method your class has, you want one typical case (what's often called the "happy path") that demonstrates typical usage, a number of extreme but still valid inputs, a range of inputs that are just outside the valid range (if your method excepts numbers in the range 1-10 then a test case with 0 and a test case with 11 would cover those cases) and a test case with data wildly outside of the valid range. Many errors in programming occur at the transition between valid and invalid input (the off-by-one error is probably the most notorious example) so your tests should cover those regions thoroughly.
One nice thing about black box testing is that if you know the specs, you can write the tests before there's any code to even test. Then you can start implementing code, test it, correct it for failed tests and repeat this process until you get a 100% pass rate. This is called Test Driven Development.
Related
an old fashioned example:
/**
* #param string $a - test parameter
*/
public function test($a)
{
}
but now that Php has types, I would write:
/**
* #param $a - test parameter
*/
public function test(string $a)
{
}
since it has a parameter, so adding "string" to phpdoc is verbose.
"Necessary" depends on what you're using to parse the annotation (if anything)*
If this is PHPDocumentor itself, you'll want to stick with the standard that it prescribes. Even if it works without the type now, there's no guarantee that a future version will, and as mentioned in Alex Howansky's answer, the type is currently defined as mandatory. From their manual:
With the #param tag it is possible to document the type and function of a single argument of a function or method. When provided it MUST contain a Type to indicate what is expected; the description on the other hand is OPTIONAL yet RECOMMENDED in case of complicated structures, such as associative arrays.
PHPStorm (at least the version I have in front of me) acts a bit strangely if you leave out the type-hint in a parameter. If I use
* #param $a Some useful comment about my parameter
then I get a warning about Undefined class Some. Apparently it's taking the first word other than the #param annotation and the variable name, and assuming that's the type. I can't find a reference to this behaviour in the phpdoc manual (providing the type after the variable), so that could itself be non-standard. Interestingly, if the first character after the variable name is a hyphen (as in the example in your question), then the warning is supressed.
I've seen a lot of code recently that leaves out the annotations entirely, and relies on the language's internal type-hinting (both parameter and return) to do the job. This is perfect, as long as you don't need to add a description to any of them. PHPStorm will warn you about missing parameter annotations the moment you provide any (but not all) of them, which means if you want to provide a comment for one then you'll need to add the rest, commented or not.
You mention verbosity in your question, and if all you're concerned about is human readability then by all means leave out the type. Phpdoc itself has a standard, but you're absolutely not bound by it. It's your code, ultimately. But if you're shipping a package that other developers might use, or if any of your toolchain (from IDE, through static analysis, to documentation generation itself) aren't happy with the non-standard usage, then you'll have to weigh up the decision again. Either way it comes down to whether you're the only one (person or machine) reading your code; if you're not, then stick with the standards, even if it means typing a few extra characters.
--
* This can include things that actually do affect the way the code runs - PHP allows you to fetch these annotations with the getDocComment methods in the Reflection API. Use-cases for this tend not to include #param annotations (more often it'll be something package specific like Doctrine's ORM annotations), which are almost exclusively used for documentation, but I don't want to over-generalise and say that this can't have an effect on your code's actual functionality.
The phpDocumentor docs state that the Datatype field is required for #param. Those docs are quite old, but I would expect apps which consume tags to still abide by that requirement. Lately, I've tended to skip the #param tag completely when I have an explicit typehint present.
PHPCS will alert if you leave it out but have a typehint, like in your example:
/**
* #param $arg The arg.
*/
public function foo(int $arg) {
PHPStan will alert if you have a #param tag type and a typehint that don't match, like this:
/**
* #param string $arg The arg.
*/
public function foo(int $arg) {
I'm writing a lot of unit tests and I'm afraid one day I'll come back to read the test codes and not be able to understand what's being tested.
The question is: How do i document PHPUnit tests using PHPDoc?
Use the #covers annotation (it's specific to PHPUnit, not a documentation tag used by phpDocumentor) to highlight what the test case is supposed to exercise. By having it in the docblock, you tell the code reader what code is targeted by the test. If you have phpDocumentor also generating docs for your test cases, then it should treat the annotation as a "custom tag", and show it as generic information. Note, though, that the point of #covers is to limit the code coverage measurements done by PHPUnit. Its use as doc info is incidental, but useful.
If you want some kind of document linking between the test case and the tested code, also put the #uses tag in the test case's docblock. That should result in #used-by tag automatically appearing in the documentation for the tested method/function.
One way as suggested is to use the test function name but this can end up too abbreviated and cryptic. In this case put some text in the optional $message parameter to explain what the test is doing.
assertSame(mixed $expected, mixed $actual[, string $message = ''])
I finds this helps, particularly if you are used to writing JavaScript tests with something like Jasmine where you put a human readable sentence to explain what is being tested for each test.
Here is a simple example. If you put the test description as the default value for a function argument it will be documented. If you put just one test per function (i.e. single responsibility principle) then when you look back in a few years time maybe the tests will make more sense than having multiple tests per function.
<?php
use PHPUnit\Framework\TestCase;
final class ArrayPushTest extends TestCase
{
public function testPushStringToEmptyArray(string $description =
'A string is pushed into an empty array at array index 0.'
) : void
{
$a = [];
array_push($a, 'zero');
$this->assertSame('zero', $a[0], $description);
}
}
And this is what it looks like in the docs with phpDocumentor:
phpDocumentor output
For example, if I have a method that execute simple logic and take an int parameter in input, do I have to unit test cases when input is :
a good int (like 1)
a bad int (like 822873873824676372732367232637233)
a descent float
a bad float
a bool
a string
no parameter
Well I think I already know the answer, but where is the limit?
Also, when the method have A and B parameters, do I have to test every cases that could happens by doing these methods? :
testGoodAWithoutB
testGoodBWithoutA
testGoodAWithGoodB
testBadAWithoutB
testGoodAWithBadB
...
The answer to your question is: no, of course not.
It makes no sense to test every possible value. If you are working with unit tests I guess that you are trying to ceap your code as clean as possible. For this your function should follow the single responsibility principle.
If your function just hase one functionality it makes it easy just to test that.
It depends also what your function can expect. When you have a function add(a, b) it depends on the context inside your programms. When the error handling is done before the function call and you are sure that you will always get valid values there is no need to test this, because your function is not responsible for that. If the same function is for example a public method the function has to check the input values. In that case you should also test that.
You need to keep in mind that your tests has to be readeable for humans. If you are testing everything which could happen the next 100 years the tests will be not so easy to understand.
Your tests should also be something like a documentation for other programmers. So just test your function like your function should be called.
Is there any harm to having functions one lined instead of multilined? For example, I wrote a class file for a PDO database connection. All the functions I wrote for it, are one lined. I haven't run into any errors yet, but can they crop up if there is lag or something?
Here are some of the functions.
public function getObject($query, $object) {
return $this->db->prepare("$query")->execute()->fetch(PDO::FETCH_OBJ)->$object;
}
public function getArray($query) {
return $this->db->prepare("$query")->execute()->fetchAll(PDO::FETCH_NUM);
}
public function insertArray($query, $array) {
return $this->db->prepare("$query")->execute($array);
}
Will this eventually run into problems? Or do they get called one section at a time, as if they were on their own line?
Thanks.
Using chained methods in one single line like this
return $this->db->prepare("$query")->execute()->fetch(PDO::FETCH_OBJ)->$object;
is what Robert Martin calls "Train Wrecks" in Clean Code (pg 98/99). It's hard to read and "generally considered sloppy" and should be avoided for the more readable
$statement = $this->db->prepare("$query");
$statement->execute();
$result = $statement->fetch(PDO::FETCH_OBJ);
return $result->$object;
Please note that I corrected the code in the snippet above, as your one-liner will not work, because execute returns a boolean, so you cannot call fetch on it:
bool PDOStatement::execute ([ array $input_parameters ] )
In other words, you cannot write this as a one-line statement anyway.
Also note that Method Chaining is very often a violation of the Law of Demeter, which states that
a method M of an object O may only invoke the methods of the following kinds of objects:
O itself
M's parameters
any objects created/instantiated within M
O's direct component objects
a global variable, accessible by O, in the scope of M
Not following LoD usually leads to Mockfests in your UnitTests and makes your application tightly coupled to much more classes than necessary, which in turn impairs reusability and increases the time required for changes (among other things).
Imperative code will always get called in the expected order. There is nothing to worry about, except maybe if the code is readable or not. In case where the line is very very long, you might want to wrap it to multiple lines, but the examples you show look OK to me.
If the connection to the database fails for some reason it could cause a FATAL_ERROR because one of the objects will return false.
When i write down a function is a good practice to validate all the parameters of the function like this:
<?php
/**
* foo - Test function
*
* #param int $paramA
* #param str $paramB
* #param array $paramC
*
* #return something
*/
function foo($paramA, $paramB, $paramC) {
// verify all parameters
if(!is_int($paramA)) {
return 0; // or error msg
}
if(!is_string($paramB)) {
return 1; // or error msg
}
if(!is_array($paramC)) {
return 2; // or error msg
}
// some code in function scope that uses parameters
// and saves the results in $result
return $result;
}
or should i rely on the user that will use the function and assume that he knows what he is doing and forget about parameter validation?
Personally i prefer to validate all the parameter of the function and return error codes for consistency of the code and make my life easier afterwords.
Thank you in advance.
I think the answer is: it depends
It depends on who you are writing your code for. If you're looking for the most generalized answer, then yes, you should.
If your script will only ever be used by you and you know in your heart of hearts that you will always use it correctly, then you will (probably) be safe if you don't.
If your script is part of an API that will be distributed to many, many people, then you most definitely want to add error checking and provide good fall back error messages for people.
If you are part of a small team that will all be using the same code, I would also suggest that validating your arguments according to your particular context would be a good idea.
If it is an internal function that only you will be using, it is probably okay to not validate every single parameter like that. If it is a function that has parameters that depend on user input (for example, you get information through $_GET, and then pass that to a function), then it needs to be validated.
Never rely on a user or anyone else besides yourself to know what they are doing. Sometimes it can be helpful to validate things like this if you are writing code that you can re-use later (like if you were writing your own framework to use across all of your PHP projects). But if it is just one application-specific function that only you can call with parameters that only you define, I wouldn't worry about it.
Why validate your parameters:
Sometimes we really need a certain datatype for an operation. If you give me a string that is not representative of an integer and you use that as a search for a primary key in a database (usually an int), then you will have trouble.
It makes fixing your code easier. When you are checking parameters, you can throw individual exceptions and give messages that are very descriptive. This makes it easier to fix your code when something breaks, because you know exactly what went wrong.
Some tips on type checking:
In case you didn't know, is_int is pretty faulty. I think a valid check to see if it is an int is is_numeric($int) && $int==(int)$int.
You can specify types for objects and arrays, like so: function foo($paramA, $paramB, array $paramC).
Really depends on the type-strictness of what you're doing with those parameters.
Since PHP is weakly typed, you can get away with a lot by casting or just letting the implicit type conversion/coercion do it's thing.
And if you're just going to throw a bunch of guard conditions at the head of every function only to return errors, that's really not much different than PHP triggering errors of its own.
I beg to differ. I recently stumbled upon an article about a similar issue (null checks), but it seems to apply for type checking in php as well. I agree with the author.
Write code in a way that makes type checking redundant.
explicitly validate user input and cast the values to the required types as soon as possible.
In some exceptional cases, you can and should use exceptions instead of returning some obscure error numbers.
It is a very good practice to validate function arguments. I suspect you ask this question because on the other hand it makes your functions look longer and uglier. However with args module from NSPL, it is very easy to do. The function from your example will be:
function foo($paramA, $paramB, array $paramC)
{
expects(int, $paramA);
expects(string, $paramB);
// do something
}
// or type check several arguments
function sum($x, $y)
{
expectsAll(numeric, [$x, $y]);
return $x + $y;
}
More examples here.