0

So when you do TDD, do you wait it to run all tests till the one you're working on? It takes too much time. When I'm in rush, I rename test file to something like aaaaaaaaaaaaaaaa_testsomething.test.js so it's run first and I see errors asap.

I don't like this approach and I'm sure there's solution, but I can't find it. So what is the easiest way to run unit tests in mtime order with Mocha? There's -sort option, but it sorts files by name only. How can I sort them by modification time?

There's my Gruntfile.js:

module.exports = function(grunt) {

  grunt.initConfig({
    watch: {
      tests: {
        files: ['**/*.js', '!**/node_modules/**'],
        tasks: ['mochacli:local']
      }
    },
    mochacli: {
      options: {
        require: ['assert'],
        reporter: 'spec',
        bail: true,
        timeout: 6000,
        sort: true,
        files: ['tests/*.js']
      },
      local: {
        timeout: 25000
      }
    }
  });

  
  grunt.loadNpmTasks('grunt-mocha-cli');
  grunt.loadNpmTasks('grunt-contrib-watch');

  grunt.registerTask('test', ['mochacli:local']);
  grunt.registerTask('livetests', [ 'watch:tests']);

};

Note: it's not duplicate. I don't want to edit my tests or Gruntfile.js each time I save source code file. I'm asking about how to modify Grunt task so it runs tests from last modified *.test.js file first. Sort unit tests by mtime, as stated in Title.

Simple scenario: I open test1.test.js in editor, change it, hit Ctrl+B and it runs unit tests from test1.test.js then test4.test.js. I open test4.test.js, hit Ctrl+S, Ctrl+B and it runs tests from test4.test.js then test1.test.js

I'm thinking about some Grunt plugin to sort files first, so I can put its results there insted of 'tests/*.js' with grunt.config.set('mochacli.options.files', 'tests/recent.js,tests/older.js', ....); but I can't find anything I can use as middleware there, don't want to invent bycicle as I'm sure there's something for this implemented already.

  • You did not actually read the answers on the other question. Using Mocha's `grep` option, for instance, does not require you to edit **anything**. – Louis Oct 12 '17 at 23:30
  • Louis, I did. I don't want to run one test. I want to change execution order. And I don't want to modify Gruntfile.js itself each time I want to run tests. It runs unit tests on Ctrl+B, executing grunt test file from Gruntfile.js I provided. – Evgeniy Kiselyov Oct 12 '17 at 23:35
  • Using grep option require you to edit grep option at least. It requires you to switch to CLI. Remembering test name to put it into grep. And I want it to run tests ordering by mtime of *.test.js files. Simple. – Evgeniy Kiselyov Oct 12 '17 at 23:37
  • As the name `grep` implies, it does pattern matching. So you **can** run multiple tests with it. And it does not require you to "switch to CLI" any more than Grunt does. – Louis Oct 12 '17 at 23:40
  • Louis. Can you please read my question once again? I want to run all tests. Not one, not filtered by grep. I want to run all tests, but most recent one - first. – Evgeniy Kiselyov Oct 12 '17 at 23:43
  • I think your approach is ill-conceived, but I've reopened. – Louis Oct 12 '17 at 23:46
  • And I want to do this with Ctrl+B from Sublime. Like it does now, executing `grunt test` task. There's no way I can add grep option to Ctrl+B in Sublime. And I don't want to Ctrl+Tab to Terminal, edit mocha grep testname, wait for results and Ctrl+Tab to Sublime again. It's TDD, not CI, it should be fast. – Evgeniy Kiselyov Oct 12 '17 at 23:46

2 Answers2

1

If you're using mocha, you can set .only on the test you are interested in:

describe(function () {
  // these tests will be skipped
});
describe.only(function () {
  // these tests will run
})
Thomas5631
  • 244
  • 1
  • 11
  • I don't want to make significant changes to tests. I want mocha to run most recent edited *.test.js file first. Simply -sort .test.js files by mtime, not by name. – Evgeniy Kiselyov Oct 12 '17 at 22:55
  • Also, I don't want it to run single file only. I just want to change the order and run all tests, but most recent one - first. – Evgeniy Kiselyov Oct 12 '17 at 22:59
  • I'm not sure I understand the motivation to control test execution order in this way. I initially thought that you were interested in monitoring a particular test or set of tests while developing a feature. Is that not the case? – Thomas5631 Oct 12 '17 at 23:17
  • It is. But it's inital stage of development, and many things are related to almost every test. I want to see results of the unit I'm currently working on asap. But I also want it to continue checking everything in background. And if there's error in other tests - I'd like to switch to them quickly, like hitting Ctrl+S there, insted of adding some control code to it. – Evgeniy Kiselyov Oct 12 '17 at 23:25
  • As far as I can see, there is no built in way to run the tests in the order that you're looking for. Normally I set .only on the tests I care about during initial development. Once I get them passing, I remove the .only and run all of the tests again. – Thomas5631 Oct 12 '17 at 23:38
  • Yep, looks that there's no such option in mocha. But I thought there may be some middleware for this. Thanks, Thomas. – Evgeniy Kiselyov Oct 12 '17 at 23:49
1

don't want to invent bicycle as I'm sure there's something for this implemented already.

...Sometimes you have to ride the bicycle ;)


Solution

This can be achieved by registering an intermediate custom-task in your Gruntfile.js to perform the following dynamically:

  1. Obtain filepaths for all unit test files (.js) utilizing grunt.file.expand with the appropriate globbing pattern.
  2. Sort each matched filepath by the files mtime/modified-date.
  3. Configure the mochacli.options.file Array with the chronologically sorted filepaths using grunt.config
  4. Run the local Target defined in the mochacli Task using grunt.task.run

Gruntfile.js

Configure your Gruntfile.js as follows:

module.exports = function(grunt) {

  // Additional built-in node module.
  var statSync = require('fs').statSync;

  grunt.initConfig({
    watch: {
      tests: {
        files: ['**/*.js', '!**/node_modules/**', '!Gruntfile.js'],
        tasks: ['runMochaTests']
      }
    },
    mochacli: {
      options: {
        require: ['assert'],
        reporter: 'spec',
        bail: true,
        timeout: 6000,
        files: [] // <-- Intentionally empty, to be generated dynamically.
      },
      local: {
        timeout: 25000
      }
    }
  });

  grunt.loadNpmTasks('grunt-mocha-cli');
  grunt.loadNpmTasks('grunt-contrib-watch');

  /**
   * Custom task to dynamically configure the `mochacli.options.files` Array.
   * All filepaths that match the given globbing pattern(s), which is specified
   # via the `grunt.file.expand` method, will be sorted chronologically via each
   * file(s) latest modified date (i.e. mtime).
   */
  grunt.registerTask('runMochaTests', function configMochaTask() {
    var sortedPaths = grunt.file.expand({ filter: 'isFile' }, 'tests/**/*.js')
      .map(function(filePath) {
        return {
          fpath: filePath,
          modtime: statSync(filePath).mtime.getTime()
        }
      })
      .sort(function (a, b) {
        return a.modtime - b.modtime;
      })
      .map(function (info) {
        return info.fpath;
      })
      .reverse();

    grunt.config('mochacli.options.files', sortedPaths);
    grunt.task.run(['mochacli:local']);
  });

  grunt.registerTask('test', ['runMochaTests']);
  grunt.registerTask('livetests', [ 'watch:tests']);

};

Additional Notes

Using the configuration above. Running $ grunt livetests via your CLI, then subsequently saving a modified test file will cause Mocha to run each test file in chronological order based on the files last modified date (I.e. The most recent modified file will run first and the last modified file will run last). This same logic applies when running $ grunt test too.

However, if you want Mocha to run the most recent modified file first, yet run the other files in normal order (I.e. by name), then the custom runMochaTests Task in the Gruntfile.js above should be replaced with the following logic instead:

/**
 * Custom task to dynamically configure the `mochacli.options.files` Array.
 * The filepaths that match the given globbing pattern(s), which is specified
 # via the `grunt.file.expand` method, will be in normal sort order (by name).
 * However, the most recently modified file will be repositioned as the first
 * item in the `filePaths` Array (0-index position).
 */
grunt.registerTask('runMochaTests', function configMochaTask() {
  var filePaths = grunt.file.expand({ filter: 'isFile' }, 'tests/**/*.js')
    .map(function(filePath) {
      return filePath
    });

  var latestModifiedFilePath = filePaths.map(function(filePath) {
      return {
        fpath: filePath,
        modtime: statSync(filePath).mtime.getTime()
      }
    })
    .sort(function (a, b) {
      return a.modtime - b.modtime;
    })
    .map(function (info) {
      return info.fpath;
    })
    .reverse()[0];

  filePaths.splice(filePaths.indexOf(latestModifiedFilePath), 1);
  filePaths.unshift(latestModifiedFilePath);

  grunt.config('mochacli.options.files', filePaths);
  grunt.task.run(['mochacli:local']);
});
RobC
  • 22,977
  • 20
  • 73
  • 80
  • I was sure I thanked you for this, but don't see comments here. Checking your answer back many times, thank you very much, @RobC You are great! – Evgeniy Kiselyov May 29 '19 at 08:09
  • 1
    You're welcome @EvgeniyKiselyov - glad it helped :) You probably did thank me previously, however comments often get deleted. – RobC May 29 '19 at 08:14