Search code examples
phplaravelphpunittddlaravel-5.3

Automatically run specific tests on file change?


I'm looking for a way to automatically run specific tests when specific files are changed, similar to what you can do with a Guardfile in Ruby on Rails. I was wondering if there is a way to do this with Laravel Elixir or with gulp (I.e. gulpfile.js)

Here is an example of what I'm looking for:

watch('^app/Http/Controllers/(.+)(Controller)\.php$', function($match) { 
    return ["tests/{$match[1]}"];
});

watch('^app/Policies/(.+)(Policy)\.php$', function($match) { 
    return ['tests/' . str_plural($match[1])];
});

watch('^app/User.php$', function($match) { 
    return [
        'tests/Admin',
        'tests/Auth',
        'tests/Users',
    ];
});

Solution

  • You can do this with grunt and a couple of the plugins, if that is an option for you. I do this for PHP, javascript and CSS source files and it works a treat.

    Example gruntfile, trimmed down:

    module.exports = function(grunt) {
        grunt.initConfig({
            pkg: grunt.file.readJSON('package.json'),
            watch: {
                grunt: { files: ['Gruntfile.js'] },
                php: {
                    files: ['src/**/*.php'],
                    tasks: ['phpunit']
                }
            },
            shell: {
                phpunit: 'phpunit --testsuite Unit' // or whatever
            }
        });
    
        grunt.loadNpmTasks('grunt-contrib-watch');
        grunt.loadNpmTasks('grunt-shell');
    
        grunt.registerTask('phpunit', ['shell:phpunit']);
    };
    

    You'll need grunt-contrib-watch and grunt-shell

    This will now run phpunit any time a php file within src/ changes, provided you have the grunt watch task running in the background. You can of course restrict and change which files you listen for with regex patterns within files section of the watch task.

    EDIT:

    To run specific tests based on a specific file change rather than a wildcard-update-all, you would have the following:

    watch: {
        grunt: { files: ['Gruntfile.js'] },
            UserSrc: {
                files: ['app/**/UserController.php'], // The ** matches any no. of subdirs
                tasks: ['UserControllerTests']
            }
        },
        shell: {
            userTests: 'phpunit tests/User' // To run all tests within a directory, or:
            //userTests: 'phpunit --testsuite UserController // to run by testsuite
        }
    }
    // ... Other config
    
    grunt.registerTask('UserControllerTests', ['shell:userTests']);
    // ... more tasks
    

    Using test suites is the better route to use if your User tests span multiple directories. So if you want to run all test files within tests/Users and tests/Auth etc, you'd have a testsuite in your phpunit.xml file that would run those. Something like:

    // ...
    <testsuite name="UserController">
        <directory suffix="Test.php">tests/Users</directory>
        <directory suffix="Test.php">tests/Auth</directory>
        // .. Other directories
    </testsuite>
    // ...