Search code examples
javascriptnode.jsjasminenode-inspector

How to drop into a debugger in a Jasmine test?


I'm improving my JavaScript by doing exercises on Exercism.io; I'm currently working on http://exercism.io/exercises/javascript/leap/readme.

So far, I've written a leap.js like so:

var Year = function (year) {};

Year.prototype.isLeap = function () {
    return (this.year % 4 === 0 && this.year % 100 !== 0) || this.year % 400 === 0
};

module.exports = Year;

The Jasmine test, leap.spec.js, is

var Year = require('./leap');

describe('Leap year', function () {
  it('is not very common', function () {
    var year = new Year(2015);
    expect(year.isLeap()).toBe(false);
  });

  it('is introduced every 4 years to adjust about a day', function () {
    var year = new Year(2016);
    expect(year.isLeap()).toBe(true);
  });

  it('is skipped every 100 years to remove an extra day', function () {
    var year = new Year(1900);
    expect(year.isLeap()).toBe(false);
  });

  it('is reintroduced every 400 years to adjust another day', function () {
    var year = new Year(2000);
    expect(year.isLeap()).toBe(true);
  });

However, some tests are still failing:

Kurts-MacBook-Pro:leap kurtpeek$ jasmine leap.spec.js
Started
.F.F

Failures:
1) Leap year is introduced every 4 years to adjust about a day
  Message:
    Expected false to be true.
  Stack:
    Error: Expected false to be true.
        at UserContext.<anonymous> (/Users/kurtpeek/exercism/javascript/leap/leap.spec.js:11:27)

2) Leap year is reintroduced every 400 years to adjust another day
  Message:
    Expected false to be true.
  Stack:
    Error: Expected false to be true.
        at UserContext.<anonymous> (/Users/kurtpeek/exercism/javascript/leap/leap.spec.js:21:27)

Ran 4 of 8 specs
4 specs, 2 failures
Finished in 0.009 seconds

Strangely, if I copy what is returned to the Node REPL with this.year replaced by 2016, I get true as expected:

Kurts-MacBook-Pro:leap kurtpeek$ node
> (2016 % 4 === 0 && 2016 % 100 !== 0) || 2016 % 400 === 0
true

What I suspect is happening is that this.year is not the number 2016, but the instance year of Year that was instantiated previously, so that the modulo expression doesn't make sense.

To confirm this, however, I would like to inspect the variables in the scope of the isLeap function. After some Googling I've tried installing jasmine-debug and jasmine-node-debug, but when I try to run either one of them (after inserting a debugger; statement before the return statement) I get the following error:

Kurts-MacBook-Pro:leap kurtpeek$ jasmine-node-debug
internal/modules/cjs/loader.js:550
    throw err;
    ^

Error: Cannot find module '_debugger'
    at Function.Module._resolveFilename (internal/modules/cjs/loader.js:548:15)
    at Function.Module._load (internal/modules/cjs/loader.js:475:25)
    at Module.require (internal/modules/cjs/loader.js:598:17)
    at require (internal/modules/cjs/helpers.js:11:18)
    at Object.<anonymous> (/usr/local/lib/node_modules/jasmine-node-debug/node_modules/node-inspector/lib/debugger.js:2:16)
    at Module._compile (internal/modules/cjs/loader.js:654:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:665:10)
    at Module.load (internal/modules/cjs/loader.js:566:32)
    at tryModuleLoad (internal/modules/cjs/loader.js:506:12)
    at Function.Module._load (internal/modules/cjs/loader.js:498:3)

From what I've read at https://github.com/angular/protractor/issues/4307, this error has to do with the Node.js team migrating users to the new inspect API - basically, these packages are outdated.

Is there any other way to enter the debugger through a Jasmine test?


Solution

  • I managed to start the debugger by running node --inspect-brk with the jasmine.js file called by Jasmine's CLI:

    Kurts-MacBook-Pro:bin kurtpeek$ node --inspect-brk /usr/local/lib/node_modules/jasmine/bin/jasmine.js ~/exercism/javascript/leap/leap.spec.js
    Debugger listening on ws://127.0.0.1:9229/03d10b73-1d60-4db3-be79-850f7ee4d0d6
    For help see https://nodejs.org/en/docs/inspector
    Debugger attached.
    Started
    

    Then, upon navigating to chrome://inspect in the Chrome browser and hitting 'continue', I was able to determine that this.year is indeed undefined:

    enter image description here

    I then fixed the test by defining this.year:

    var Year = function (year) {
        this.year = year;
    };
    
    Year.prototype.isLeap = function () {
        return (this.year % 4 === 0 && this.year % 100 !== 0) || this.year % 400 === 0
    };
    
    module.exports = Year;
    

    Now the tests pass:

    Kurts-MacBook-Pro:bin kurtpeek$ jasmine ~/exercism/javascript/leap/leap.spec.js
    Started
    ....
    
    
    Ran 4 of 8 specs
    4 specs, 0 failures
    Finished in 0.009 seconds