Infection 0.9 is out!

I wrote about infection a while ago, and not too long after that, 0.9.0 was released. Lets look at the new features, and how we can use them in our projects!

Profiles

Previously, if you wanted to run all mutators, except for one, it would involve passing all mutators in the commandline, except for the one you didn’t want, which was tedious. With the addition of profiles this became a whole lot easier.

Here we have a simple configuration file, where we use all mutators, except for the @function_signature ones (PublicVisiblity & ProtectedVisibility).

{
    "timeout": 10,
    "source": {
        "directories": [
            "src"
        ]
    },
    "logs": {
        "text": "infection-log.txt"
    },
    "mutators": {
        "@default": true,
        "@function_signature": false
    }
}
Instead of a profile (which always starts with an @ and is snake_case), you can also disable a single mutator directly. e.g. "PublicVisiblity": false.

Disabling mutators for specific places.

Besides passing true or false to a mutator in the config, we now have the ability to add specific settings to it.

Lets say we want to disable the Plus mutator for the method named doPlus in the Foo\Bar\Baz class, we can do it like so:

{
    "mutators": {
        "@default": true,
        "Plus": {
            "ignore": [
                "Foo\\Bar\\Baz::doPlus"
            ]
        }
    }
}

But what if we want to disable the Minus mutator, for all classes in the Foo\Bar namespace, and the @function_signature profile for all methods named noTrue in the Foo namespace?

It can easily be done by adding the following parts to your configuration file.

{
    "mutators": {
        "@default": true,
        "Minus": {
            "ignore": [
                "Foo\\Bar\\**"
            ]
        },
        "@function_signature": {
            "ignore": [
                "Foo\\**::noTrue"
            ]
        }
    }
}

Important: If you want to (partially) disable certain mutators or profiles, but still run everything else, be sure to add "@default": true" to your config, which enables all the other mutators.

Mutation Badge


Thanks to the people who made Stryker, you can now add a new badge to show off your mutation score. You can find the instructions right here.

Smarter mutators

This mutation would always result in the following error, making it a useless mutation.

Fatal error: Access level to B::foo() must be public (as in class A) in /foo/test.php on line 14

abstract class A
{
    abstract public function foo();
}

class B extends A
{
-    public function foo()
+    protected function foo()
    {
    }

}

Infection now uses reflection to determine if a method has a parent class or an interface that declares the same method. And it does not mutate the visibility if the method is inherited. If you use a lot of abstract/parent classes and interfaces this could drastically reduce the amount of mutations you get, and thus speed up the process by a lot.

Phar Scoping

Now you may be worried, that autoloading your code, when using a phar could result in a clash while autoloading. e.g. your project has a dependency on Symfony/Console, and the phar does as well, so which one does infection load? This has been fixed by using PHP-Scoper, which prefixes all namespaces within the phar with a random string, and makes it so that the code, and autoloading, within the phar can not clash with the code in your project.

For example, your project would use (and thus autoload) the following class Symfony\Component\Console\Application. Now infection also uses this class, but within the phar its called something like: ScopedRandomgString\Symfony\Component\Console\Application. This means that infection has a separate version of the class that it uses.

PHP-Scoper a really cool project, so if you are interested in it, be sure to check it out.

New mutators

Not only did mutators get smarter, we also got a whole lot of new mutators:

Finally_:

Removes the finaly block that is part of a try catch block, e.g.

try {
    dangerous_function()
}
catch(Exception $e) {
    //handle exception
}
- finally {
-    // Do this no matter what
- }

This should help you test your error handling.

PregQuote:

Removes preg_quote function calls, e.g.

- $a = preg_quote('value');
+ $a = 'value';

This should help you test the usage of preg_quote calls, and maybe even show you don’t need/use them.

ArrayItem& Yield_:

Changes the => into > for arrays and yield respectively,

[
-    $a->var => $b->var
+    $a->var > $b->var
]

This should help to test the usages of your arrays and yield statements

Assignment:

Changes assignments like +=. -+, .= etc into plain =.

$a = 'title;
- $a .= ' suffix';
+ $a = ' suffix';

This should help you test that assignments are done properly.

For_:

The for counterpart to the ForEach_ mutator that stops for loops from happening:

- for($i =0; $i < count($array); $i++)
+ for($i =0; false; $i++)

This should help testing that your for loops are actually happening.

Updating

If you use composer to add infection as a dev dependency, be sure to change the version restriction in your composer.json to ^0.9.0, and hit composer update. Because in pre 1.0.0 releases composer won’t bump minor versions, as they could have breaking changes.

If you are downloading the par, simply change the current version in the url to 0.9.0 and you should be good.

In conclusion

Infection 0.9 came with a lot of improvements, especially for (partially) disabling mutators or groups of mutators. The mutators also got a bit smarter, and hopefully you should see your mutation testing being done a lot quicker.

If you liked this blog post, and/or have questions/tips/whatever, feel free to leave a comment here, or hit me up on twitter

Avatar
Gert de Pagter
Software Engineer

My interests include software development, math and magic.

Related