Intern and testing Grunt tasks

By on September 3, 2015 9:55 am

Intern

You can use Intern to test just about everything!  How about testing your Grunt tasks?  Even though Intern focuses on tests authored as AMD modules, it certainly can be used to test CommonJS modules, like those used with Grunt. (As well as TypeScript, ES6 modules, and non-module code)

Why would you want to test your Grunt tasks under Intern? Well, for all of the advantages that Intern brings, like integrated code coverage analysis, CI integration, etc. In a lot of cases, your Grunt tasks might be part of a larger project and you can use the same tool to fully test your Grunt tasks. In addition, you can encapsulate all of your Grunt configuration and execution without having to have a Gruntfile.js as part of your project (or at least have your test configuration separated from your “real” Gruntfile.js). So even if you just want to support Grunt, but not use Grunt for your task orchestration, you can. This way you can cover all of your code, including your build tooling, in one place. End-to-end test coverage nirvana!

In order to get started, you would go ahead and develop your Grunt tasks and place them in the traditional place in your package (myPackage/tasks). There is more on the topic of creating plugins and tasks in the Grunt Documentation.

You would also want to add Grunt as a development dependency to your package.json (be nice and don’t add it as a dependency though, as it only burdens others unnecessarily):

{
  "devDependencies": {
    "grunt": "~0.4.5"
  }
}

We would then create an Intern module to test our tasks. In the example below, we have developed a task named myTask, which is a single target task.

define(function (require) {
  var registerSuite = require('intern!object');
  var assert = require('intern/chai!assert');

  /* This will load the Grunt CommonJS module */
  var grunt = require('intern/dojo/node!grunt');

  /* A helper function that will run our task for us */
  function runGruntTask(taskName, callback) {
    var task = grunt.task._taskPlusArgs(taskName);
    task.task.fn.apply({
      nameArgs: task.nameArgs,
      name: task.task.name,
      args: task.args,
      flags: task.flags,
      async: function() { return callback; }
    }, task.args);
  }

  registerSuite({
    name: 'tasks/myTask',

    /* let's setup Grunt for our tests */
    setup: function () {
      /* we go ahead and configure our tasks for testing */
      grunt.initConfig({
        myTask: {
          options: { foo: 'bar' },
          src: 'tests/support/test.js'
        }
      });

      /* now we will tell Grunt to go load our tasks, so now our
       * environment should be "production-like" */
      grunt.loadTasks('tasks');
    },

    basic: function () {
      /* we will need to deal with a callback from our runGruntTask function */
      var dfd = this.async();

      /* now we go ahead and run our task */
      runGruntTask('myTask', dfd.callback(function () {
        /* and then make our assertions here */
      }));
    }
  });
});

You can obviously extend this to support all sorts of Grunt task testing, including multi-target tasks. Traditionally these would be considered unit tests and you wouldn’t expect to run them in a browser environment, just under Node.js.

Learning more

If you’re interested in learning more about automated testing with Intern, we offer a one-day online Intern workshop, and we also help organizations with their approach to testing under our expert JavaScript support plans. If you’re not sure where to start with Intern, or you need some help making your source code more testable, or want assistance in defining a testing strategy for your organization, SitePen can help! Contact us for a free 30-minute consultation.