sindresorhus/gulp-jasmine

End Process

Closed this issue · 17 comments

Is there a way to end it after finishing the tests:

gulp.task('testBE', ['watch-backend'], function () {
  return gulp.src(paths.specs + '/backend/**/*.spec.js')
    .pipe($.jasmine({verbose: true}));
});

It would be useful because then mongoose connections are not opened/ models are not loaded twice. I always have to quit the process in my terminal. I found this but it does not work:
http://stackoverflow.com/questions/27158530/cant-tell-when-jasmine-is-done-running

kevva commented

That's weird. It should always emit end when it's done. Have you tried listening on the close event?

It's probably because you still have an event listener open, for example a web server. See #41.

I see this problem but in a different set up, here's the terminal output:

ᐅ gulp jasmine
[08:41:28] Using gulpfile ~/.../gulpfile.js
[08:41:28] Starting 'jasmine'...
...................[08:41:33] Finished 'jasmine' after 5.09 s
...........................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................

526 specs, 0 failures
Finished in 0 seconds

And the code is simply:

...
  .pipe(require('gulp-jasmine')())
  .on('finish', cb);

Using end or close does nothing :(

Maybe I'm wrong but I don't see me opening an express server or listening with express. I do require mongoose. But I never close the db connection, because I actually don't know how to do with the async tests.

Gulp:

gulp.task('testBE', ['watch-backend'], function () {

  return gulp.src(paths.specs + '/backend/**/*.spec.js')
    .pipe($.jasmine({verbose: true}));
});

gulp.task('watch-backend',['scripts'], function () {
  gulp.watch([
    paths.src + '/{node,config}/**/*.coffee'
  ], ['testBE']);
});

The task scripts just compiles the coffee.

Function file:

#Dependencies
mongoose = require('mongoose')
async = require('async')

#Configuration
environment = require('../config/config.js')()
configDB = require('../config/database.js')(environment)

CodeModel = require('./models/code.model.js')
DonationModel = require('./models/donation.model.js')

exports.checkGuestCode = (db, guestCode, callback) ->
 ...
)
+ dozens of functions 

Specs

applicationDir = '../../.tmp/serve/'

environment = 'test'
databaseService = require(applicationDir + 'node/database.service.js')
mongoose = require('mongoose')
configDB = require(applicationDir + 'config/database.js')(environment)

There are more specs & functions than that but they all look similar. If I close it after the last describe my code fails. Thus I was hoping to close the connection with ending gulp-jasmine.

db.connection.db.dropDatabase(() ->
  console.log "dropped database"
  db.connection.close(() ->
    console.log "closed connection"
    mongoose.connect(configDB.url,{auth:{authdb:configDB.authdb}}, (err)->
      if (err)
        console.log(err)
      else
        console.log "reopened connection"
    )
    db = mongoose.connection
  )
)

I'll try to update the docs this weekend with this info, but here's my understanding of what's happening:

Gulp and gulp-jasmine run as node processes. A node process exits either when it's explicitely killed or when the event loop has no more listeners.

Gulp-jasmine has no understanding of the process in which it's running. It might run as part of a larger, long running process (for example with gulp.watch). It also has no knowledge of the resources your specs use (web server, database connections, file watchers, etc.). It's up to you as a developper to set-up your resources and clean them up once you're done with your tests.

The classic way to do this is to use test set-up (beforeEach, beforeAll) and teardown (afterEach, afterAll) to respectively set-up and clean-up your resources. I've included a pattern in #41 that I think is pretty easy to implement and versatile: Instead of having a sequential server.js file, return an object with start() and stop() functions, then create a 'start.js' file that you use to load your configurations and start your server or process. Then in your tests, import your server.js, load your test configuration and call start() in either beforeAll or beforeEach and stop() in afterAll or afterEach.

Hope this helps.

It sounds like a good solution for handling an express server. But what if I want to end it within gulp-watch? It would be good to automatically close the connections to the database.

From my understanding jasmine describes work async, only its can be synced (maybe I'm wrong). Launching mongodb on every before and closing it afterwards could cause duplicate connections. Which throw an error unless the db connections are created with createConnection.

You have to understand that Jasmine has no knowledge of your code or your requirements. This is not a gulp-jasmine issue. The hooks to let Jasmine know the set-up (opening a connection, starting a server, creating test data, etc.) and tear down required to run your tests are the before* and after* methods. That's hardly unique to Jasmine. I'm leaving this issue open because I want to update the readme with a FAQ since this is a pretty frequent question.

As for your specific issue with opening multiple connections, this is easily solvable via a singleton with a counter. Something like this:

var connCount = 0;
var conn = ...;

function open() {
    if (connCount === 0) conn.open();
    connCount++;
    return conn;
}

function close() {
    connCount--;
    if (connCount === 0) conn.close();
}

Hope this helps.

This problem seems to have been around for quite a while in general based on what I've found doing different searches:

sindresorhus/gulp-mocha#1
sindresorhus/gulp-mocha#54

Most people tend to use this approach here:
sindresorhus/gulp-mocha#1 (comment)

For anything external resource that I hold a connection to, I have a listener for SIGINT.

So I used a slightly modified version within my gulpfile:

        .pipe(jasmine())
        // Creating the reports after tests ran
        .pipe(istanbul.writeReports())
        // required otherwise the process never actually exits
        .once('end', function () {
            process.kill(process.pid, 'SIGINT');
            setTimeout(function() {
                /* eslint-disable */
                process.exit(0);
                /* eslint-enable */
            }, 100);
        });

Basically using signal SIGINT I'm able to close all connections after testing completes, and then exit cleanly.

I prefer this approach, alluded to above, but not spelled out?

For app.js:

app.connect = function() {
  mongoose.connect('mongodb://127.0.0.1/exampledb');
};

app.disconnect = function() {
  mongoose.disconnect();
};

And in an example.spec.js (full gist):

/*global describe:false, it:false, expect:false*/

var request = require('request');

var app = require('../app');
var server = require('http').createServer(app);
var baseUrl = 'http://127.0.0.1:3000/example-route';

describe('example-route', function() {

  describe('app spinup', function() {
    it('should be ok', function(done) {
      server.listen(3000);
      server.on('listening', function() {
        app.connect();
        done();
      });
    });
  });

 <add your describe/it/expect blocks here>

  describe('app spindown', function() {
    it('should be ok', function(done) {
      app.disconnect();
      server.close();
      done();
    });
  });

});

I had similar issues where gulp-jasmine ran my tests, but then the process didn't exit.

My solution was to use gulp-exit and that ended my task for me. (I'm using gulp-load-plugins, so that is the $). Perhaps not the best solution but it got my task to exit for me.

var $ = require('gulp-load-plugins')();
function test(sources) {
    return gulp.src(sources)
        .pipe($.jasmine({ verbose: true, captureExceptions: true }))
        .pipe($.exit());
}

Fwiw, my solution started failing when i added some middleware that fire up their own redis clients, making app dissconnect/spinDown/close function not stop all of the created listeners. I am switching to gulp-exit, I think.

I think I just ran into a similar issue myself, where the stream from gulp.src('*').pipe(jasmine()).pipe(myCleanupCode) does not behave in a consistent manner (the behavior depends on the number of files in the gulp.src).

To me, it looks like the issue is that gulp-jasmine is using the through function slightly incorrectly. I can't find it in the specification, but if you call the callback in the flush function for through in an asynchronous manner, it doesn't behave the way you would expect. I've had this problem in some of my application code with asynchronous callbacks in through. The solution I've found is to pause and resume the stream.

Here is the fix that works great for me 3ee47bb
I'd be happy to make a pull request and try to write a test if you think you'd accept it.

@charleshansen

TLDR version: to be notified when jasmine completes, use: .on('jasmineDone', () => { /*...*/})

Longer version: check #71 for more context on this solution.

@jbblanchet I did read #71, even 'jasmineDone' is not emitted 100% reliably for me. I'd also really prefer using another pipe and not using on, this makes it difficult to work with multiple streams (gulp-jasmine is just one of many things that run in my default gulp task).

Regardless of what events to use, I do think it isn't cool to call the flush callback asynchronously without pausing it first. It seems that this breaks the natural stream lifecycle. The stream spec doesn't seem to mention asynchronous callbacks one way or the other.

Your idea has merit, but I'd like to think about it a bit more. It's not a replacement for the jasmineDone event, because it was implemented for people having no drain downstream, and calling pause should have no effect in that case. Please open a pull request so we can have that discussion there.

Thats what I came up with, which works for me. The db connection where indeed the problem. Unfortunately I can't properly use the disconnect event.

Connection Module

connections = 0
disConnectionRetries = 0
MAX_DISCONNECTION_RETRIES = 10

connectDB = (callback) ->
  console.log "CONNECTING TO DB", mongoose.connection.connectionState
  configDB = require(applicationDir + 'backend/config/database.js')(environment)

  if connections == 0
    mongoose.connect configDB.url,{auth:{authdb:configDB.authdb}}, (err)->
      if (err)
        console.log(err)
        return callback err

    db = mongoose.connection
    db.on 'error', console.error.bind(console, 'connection error:')

    db.once 'open', () ->
      connectionRetries = 0
      connections++
      return callback null, db
      #console.log "Database established"
      #Delete all data and seed new data

  else
    db = mongoose.connection
    return callback null, db

exports.disconnectDB = (callback) ->
  console.log "DISCONNECTING FROM DB", mongoose.connection.readyState
  mongoose.disconnect (err)->
    return callback err if err?
    connections--
    isDisconnected(callback)

isDisconnected = (callback)->
  console.log "SHOULD BE DISCONNETCED", mongoose.connection.readyState
  throw new Error "Cannot disconnect more than #{MAX_DISCONNECTION_RETRIES} retries" if disConnectionRetries > MAX_DISCONNECTION_RETRIES
  if mongoose.connection.readyState == 0
    disConnectionRetries = 0
    return callback()
  else
    disConnectionRetries++
    return setTimeout ()->
      isDisconnected(callback)
    , 50

Unit Tests

describe('>>>>>>>>>>>>>>>>>>>>>>>>>  XY CLASS  <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<', () ->

  beforeAll (done)->
    db = testSettings.connectDB (err)->
      done()

   ...

  afterAll (done)->
    testSettings.disconnectDB (err)->
      console.log "DONE DISCONNECTING"
      done()