samedi 2 janvier 2016

Troubleshooting execution order issue in Apps Script

I'm trying to implement the example development process outlined on the Apps Script blog, but am running into an issue (I believe is) related to execution order. Part of what this example is demonstrating is a way to simplify unit testing in Apps Script, which is what I'm struggling to adapt to my own purposes.

Per the blog, I cloned the google-apps-script-samples repo.

In the example, there are only 2 test files. One has some assertions, but relevant to this question is the following inherit_ func:

function inherit_(childClass, parentClass) {
  /**
   * Empty wrapper class.
   * @constructor
   */
  var TempClass = function() {};
  TempClass.prototype = parentClass.prototype;
  childClass.prototype = new TempClass();
  childClass.prototype.super_ = parentClass.prototype;
  childClass.prototype.constructor = childClass;
}

The other contains both a base 'Test' class with methods for running the tests, AND a sample test class ('SampleTests'):

function runAllTests() {
  var sampleTests = new SampleTests();
  sampleTests.run();
}

/**
 * Base class for all tests.
 * @constructor
 */
var Tests = function() {
  // Do nothing.
};

/**
 * Runs all member functions in the child class whose names begin with "test".
 */
Tests.prototype.run = function() {
  var testCount = 0;
  var failCount = 0;
  var failNames = [];
  Logger.log('Starting test run.');
  for (var member in this) {
    if ((typeof this[member] !== 'undefined') &&
        (member.indexOf('test') === 0)) {
      try {
        testCount++;
        this[member].call(this);
        Logger.log('[PASS] : ' + member);
      } catch (error) {
        failCount++;
        failNames.push('\t' + member + '\t\t' + error);
        Logger.log('[FAIL] : ' + member + '\n' + error);
      }
    }
  }
  Logger.log(testCount + ' tests completed in this run.');
  Logger.log(failCount + ' tests failed:\n' + failNames.join('\n'));
  Logger.log('');
};

var SampleTests = function() {
  Tests.call(this);
};
inherit_(SampleTests, Tests);

/**
 * Test to confirm that the test spreadsheet has 5 sheets.
 */
SampleTests.prototype.testCorrectCount = function() {
  var testActual = countSheets();
  assertEquals_(5, testActual);
};


/**
 * Test to confirm that the test spreadsheet does not have 10 sheets.
 */
SampleTests.prototype.testIncorrectCount = function() {
  var testActual = countSheets();
  assertEquals_(10, testActual);
};

I tried to modify this example in the following way: I added 2 more sample tests, but moved each test into its own .gs file for organizational clarity. (I.e. SampleTestsA, SampleTestsB, and SampleTestsC each in it's own .gs file).

But when running these tests, I got the error 'TypeError: Cannot read property "prototype" from undefined...'. It seems that one of my test files is being compiled, and the inherit_ func is being called before the 'Test' class file has been compiled.

So, my initial solution was to move each call to inherit_ inside the runAllTests func. So I now have:

function runAllTests() {
  inherit_(sampleTestsA, Test);
  var sampleTestsA = new SampleTestsA();
  sampleTestsA.run();

  inherit_(sampleTestsB, Test);
  var sampleTestsB = new SampleTestsB();
  sampleTestsA.run();

  inherit_(sampleTestsC, Test);
  var sampleTestsC = new SampleTestsC();
  sampleTestsA.run();
}

The error went away, but for some reason the run prototype method no longer recognizes any tests. In my Logger log I would see:

[16-01-02 11:58:12:347 EST] Starting tests for SampleTestsA
[16-01-02 11:58:12:348 EST] 0 tests completed in this run. 
[16-01-02 11:58:12:349 EST] 0 tests failed:

For each of my 3 tests.

Finally, my question: Why is this happening? My current plan is to create a Gulp task that simply appends each test file to the bottom of the .gs file that defines the 'Test' class, as this should ensure they load in the correct order. Is that a reasonable workaround? It seems simpler to me than implementing this suggestion.

Aucun commentaire:

Enregistrer un commentaire