Phpunit: assertType() invoking autoloader for scalar types

Created on 23 Nov 2010  ·  10Comments  ·  Source: sebastianbergmann/phpunit

When using assertType() to check whether a value is an array or a scalar value, the autoloader is invoked (as side-effect of using class_exists()). It would be nice to check if the expected type string parameter matches "array" or a scalar type first.

Example:

$this->assertType('array', array());

Expected: true, without invoking the autoloader
Actual: true, invoking the autoloader for a class named "array"

Most helpful comment

Please use assertInternalType() instead of assertType() for internal types.

All 10 comments

Please use assertInternalType() instead of assertType() for internal types.

That works, but why is that necessary?

Here is the code of assertType:

public static function assertType($expected, $actual, $message = '')
{
    if (is_string($expected)) {
        if (PHPUnit_Util_Type::isType($expected)) {
            if (class_exists($expected) || interface_exists($expected)) {
                throw new InvalidArgumentException(
                  sprintf('"%s" is ambigious', $expected)
                );
            }

It appears to me that PHPUnit_Util_Type::isType already tests whether the type is an internal type, so all you would have to change is this:

            if (class_exists($expected, false) || interface_exists($expected, false)) {

to disable the autoloader for internal types. What am I missing?

I have to agree with arnoschaefer, why are you requiring that we use a different function when his solution suffices? I am not changing thousands of unit tests already in place that use assertType from the < 3.5. So I will be overriding assertType for all my unit tests with this solution. I think we deserve a better explanation why you chose this path otherwise I'm going to have to declare phpunit FAIL.

A single assetion method cannot implement the two different functionalities. It was my mistake to assume initially that it were possible. This is why assertInternalType() exists now.

Please provide an explanation as to why a single assertion method cannot implement the two different functionalities?

In addition, your documentation contradicts this statement:

http://www.phpunit.de/manual/3.5/en/api.html#api.assert.assertType:

Alternatively, $expected can be one of these constants to indicate an internal type:

* PHPUnit_Framework_Constraint_IsType::TYPE_ARRAY ("array")
* PHPUnit_Framework_Constraint_IsType::TYPE_BOOL ("bool")
* PHPUnit_Framework_Constraint_IsType::TYPE_FLOAT ("float")
* PHPUnit_Framework_Constraint_IsType::TYPE_INT ("int")
* PHPUnit_Framework_Constraint_IsType::TYPE_NULL ("null")
* PHPUnit_Framework_Constraint_IsType::TYPE_NUMERIC ("numeric")
* PHPUnit_Framework_Constraint_IsType::TYPE_OBJECT ("object")
* PHPUnit_Framework_Constraint_IsType::TYPE_RESOURCE ("resource")
* PHPUnit_Framework_Constraint_IsType::TYPE_STRING ("string")

Your note below only "recommends" that assertInternalType should be used and that it is not required:

Note

It is recommended to use assertInternalType (see the section called “assertInternalType()”) instead to check for internal types.

A single assertion method cannot implement the two different functionalities because they are ambigious. Lets say you have a class named String and use assertType("string", ...) -- how is PHPUnit supposed to know what it is you want?

The recommendation is exactly for the case you are experiencing: you are using an autoloader and want to use assertType(). That does not work.

While php allows a class to be called integer or string, your code currently does not allow a class to be called string, integer, etc as it would throw an invalid argument (%s is ambiguous) exception.

The problem is that if you don't set the autoload parameter to false in the class_exists and interface_exists functions on line 1208, the autoloader will attempt to include a file that does not exist (e.g. class.array.php) and throw a php error instead of an exception.

Making this modification will allow assertType to be used as it always has been.

No, it won't. Because then other people will yell at me / PHPUnit because they expect the autoload to autoload their String class.

Here's my advice, take it or leave it:

Do not use assertType(). It is deprecated (although I forgot to mark it as such) and will go away eventually. It only exists in current versions of PHPUnit to ease migration.

Use assertInternalType() to assert that a variable has a specified internal type.

Use assertInstanceOf() to assert that an object has a specified type (class or interface).

I have no problems with a deprecated function but you are not easing migration at all. You have changed the original functionality from phpunit 3.4 which did not have this exception in the first place and is now breaking my code instead of the guy who autoloads the String class. So I think you should either remove it completely in phpunit 3.5 or revert it back to the 3.4 functionality.

Settle down, there's no need to get excited. I for one can live with Sebastian's decision, it actually does make sense, it is just good to know the reason behind it. So I will be using the other two functions in the future. On the other hand, if assertType is deprecated anyway, why not keep it working as it did before, and clearly document that it is deprecated, subject to removal sometime in the future. But that is of course Sebastian's decision, as this is the way of free software.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

dkarlovi picture dkarlovi  ·  3Comments

rentalhost picture rentalhost  ·  4Comments

greg0ire picture greg0ire  ·  4Comments

sebastianbergmann picture sebastianbergmann  ·  4Comments

keradus picture keradus  ·  3Comments