Should I Be Using Assert in My PHP Code

Should I be using assert in my PHP code?

The rule of thumb which is applicable across most languages (all that I vaguely know) is that an assert is used to assert that a condition is always true whereas an if is appropriate if it is conceivable that it will sometimes fail.

In this case, I would say that assert is appropriate (based on my weak understanding of the situation) because records should always be set before the given method is called. So a failure to set the record would be a bug in the program rather than a runtime condition. Here, the assert is helping to ensure (with adequate testing) that there is no possible program execution path that could cause the code that is being guarded with the assert to be called without records having been set.

The advantage of using assert as opposed to if is that assert can generally be turned off in production code thus reducing overhead. The sort of situations that are best handled with if could conceivably occur during runtime in production system and so nothing is lost by not being able to turn them off.

Why do we use assert() and assert_options() in PHP?

Assert() is a clever function that works along the same lines as our print statements, but they only have any effect if a certain condition is not matched. Essentially, assert() is used to say "This statement must be true - if it isn't, please tell me". Consider this following example:

<?php
print "Stage 1\n";
assert(1 == 1);
print "Stage 2\n";
assert(1 == 2);
print "Stage 3\n";
?>

Here we have two assert()s, with the first call asserting that one must be equal to one, and the second call asserting that one must be equal to two. As it is impossible to redefine constants like 1 and 2, the first assert() will always evaluate to true, and the second will always evaluate to false. Here is the output from the script:

Stage 1 Stage 2 Warning: assert()

[http://www.php.net/function.assert]: Assertion failed in
/home/paul/sandbox/php/assert.php on line 5

Stage 3

Note that the first assert() is not seen in the output at all because it evaluated to true, whereas the second assert() evaluated to false, so we get a warning about an assertion failure. Also note that we see "Stage 3" after the assertion failure warning, because the script carries on executing after the failure. As long as assertions evaluate to true, they have no effect on the running of the script, which means you can insert them for debugging purposes and not have to worry about taking them out once you are finished debugging.


If you are worried about your assertions slowing execution down, which, although the speed hit will be minimal, is still a valid concern, you can disable execution of assert() by using the assert_options() function or by setting assert.active to Off in your php.ini file. If you want to use assert_options(), it takes two parameters - the option to set and the value you wish to set it to - and there are a variety of ways it can make assert() more powerful.
Sample Image

Note that all of these options can be set in your php.ini file so that they are always in effect - the key options to change are assert.active, assert.warning, assert.bail, assert.quiet_eval, and assert_callback.

ASSERT_CALLBACK is a very useful options as it allows you to write an error handler for when your code fails an assertion. It takes the string name of a function to execute when assertions fail, and the function you define must take three parameters - one to hold the file where the assertion occurred, one to hold the line, and one to hold the expression. Using all three together in your callback function allows you to generate meaningful error messages that you can debug easily. Consider this code snippet:

<?php
function assert_failed($file, $line, $expr) {
print "Assertion failed in $file on line $line: $expr\n";
}

assert_options (ASSERT_CALLBACK, 'assert_failed');
assert_options (ASSERT_WARNING, 0);

$foo = 10;
$bar = 11;
assert('$foo > $bar');
?>

Ref:
http://www.hackingwithphp.com/19/8/3/making-assertions


Example from official documentation

assert_options — Set/get the various assert flags

Example #1 assert_options() example

<?php
// This is our function to handle
// assert failures
function assert_failure()
{
echo 'Assert failed';
}

// This is our test function
function test_assert($parameter)
{
assert(is_bool($parameter));
}

// Set our assert options
assert_options(ASSERT_ACTIVE, true);
assert_options(ASSERT_BAIL, true);
assert_options(ASSERT_WARNING, false);
assert_options(ASSERT_CALLBACK, 'assert_failure');

// Make an assert that would fail
test_assert(1);

// This is never reached due to ASSERT_BAIL
// being true
echo 'Never reached';
?>

As per PHP documentation assert()

  1. If the assertion is given as a string it will be evaluated as PHP code by assert().
  2. If you pass a boolean condition as assertion, this condition will not show up as parameter to the assertion function which you may have defined with assert_options().The condition is converted to a string before calling that handler function, and the boolean FALSE is converted as the empty string.
  3. Assertions should be used as a debugging feature only. You may use them for sanity-checks that test for conditions that should always be TRUE and that indicate some programming errors if not or to check for the presence of certain features like extension functions or certain system limits and features.
  4. Assertions should not be used for normal runtime operations like input parameter checks. As a rule of thumb your code should always be able to work correctly if assertion checking is not activated.
  5. The behavior of assert() may be configured by assert_options() or by .ini-settings described in that functions manual page.The assert_options() function and/or ASSERT_CALLBACK configuration directive allow a callback function to be set to handle failed assertions.
    6.assert() callbacks are particularly useful for building automated test suites because they allow you to easily capture the code passed to the assertion, along with information on where the assertion was made. While this information can be captured via other methods, using assertions makes it much faster and easier!

Should functional test just assert responses only?

Automated Testing (and testing in general) is considered a good practice in Software Engineering. However, there exist a lot of hot discussion on what are the boundaries for test methodology and types classification.

In this sense, a practical approach to correctly implement a testing methodology is learning from those who you may consider respectable in your context of software development AND making sure that the practice you adopt plays nice for the goal you are willing to achieve. Try to be consistent on this.

Just as a reference, lets take this definition.

Functional testing refers to activities that verify a specific action or function of the code. These are usually found in the code requirements documentation, although some development methodologies work from use cases or user stories. Functional tests tend to answer the question of "can the user do this" or "does this particular feature work."

Considering this approach, you want your tests to give you confidence that the user is able to execute some function (or feature) provided by your application. It does not matter (here) how that feature is provided, just get into your user's shoes and think "Am I getting what I'm expecting from this action?", the answer to that question should hint you the assertions for your test.

Non-functional testing refers to aspects of the software that may not be related to a specific function or user action, such as scalability or other performance, behavior under certain constraints, or security. Testing will determine the breaking point, the point at which extremes of scalability or performance leads to unstable execution. Non-functional requirements tend to be those that reflect the quality of the product, particularly in the context of the suitability perspective of its users.

If you want to test how a certain function is executed, it's probably a Unit Test what may help you to do those assertions.

But again, remember it's not that of a sharp limit on what your tests should perform out from their name, but just to know what classification would fit best for the type of assertions you are performing (that helps you better structure your tests and give you a clear idea of what are you testing). Try to be consistent and focus on testing what you really need to be confident on.

How Does PHP assert() Work Exactly?

It's not about how assert() works but how empty() works.

When used with $object->inaccessibleProp empty first tries the same thing as isset() and if this "test" returns false empty() doesn't even try to access the element but returns true.

And the XMLReader class obviously doesn't implement the __isset() method the way you'd need it.

<?php
$xml = <<< XML
<depts xmlns:apl="urn:my:ns" >
<apl:dept>Dept One</apl:dept>
<apl:dept>Dept Tow</apl:dept>
</depts>
XML;

$elmLocal = 'dept';
$elmUrn = "urn:my:ns";

$xr = new XMLReader;
$xr->XML($xml);

// move to the first desired element node
$found = false;
while ($xr->read()) {
if ($xr->localName === $elmLocal && $xr->namespaceURI === $elmUrn) {
$found = true;
break;
}
}
if (!$found) {
exit("---Error--- No element found with given Name/NS.\n");
}

$var = $xr->name;
var_dump(isset($xr->name));
var_dump(empty($xr->name));
echo $xr->name;

prints

bool(false)
bool(true)
apl:dept

How can you assert an assertion was done

I'll not go into possible implementation details, I just want to point out that it is basically a bad idea to test the number of assertions been made in a testsuite, since this value would be really hard to maintain. Let's say you have 1724 tests. You would need to track this value across every change in the test suite. This does not make sense imo.

Instead I would use $this->markTestSkipped(), $this->markTestIncomplete() and friends to make sure that everything runs as expected.



Related Topics



Leave a reply



Submit