Ilija
Ilija

Reputation: 4413

Testing Node.js application that uses Kue

I would like to test an application that uses Kue so that job queue is empty before each test and cleared after each test. Queue should be fully functional and I need to be able to check status of jobs that are already in the queue.

I tried mock-kue and it worked well until I had to get jobs from the queue and analyze them. I couldn't get it to return jobs by job ID.

Situations that I need to be able to test:

  1. Something happens and there should be a job of a given type in the queue,
  2. Something happens and produces a job. Something else happens and that job gets removed and replaced with another job (rescheduling or existing job).

Seams straightforward, but I have hard time wrapping my head around the problem. All pointers are welcome.

Upvotes: 3

Views: 1808

Answers (2)

Dan K.K.
Dan K.K.

Reputation: 6094

Take a look at the kue-mock lib, it is more likely for integration testing than unit.

The library doesn't hack on any kue's internals (replacing/overriding methods etc.). Instead, it creates the original queue instance with a separate redis namespace, then, when stubbing, it creates job process handlers on the fly, putting its own implementation that gives you the ability to control the job processing behaviour.

Example usage:

const expect = require('chai').expect;

const kue = require('kue');
const KueMock = require('kue-mock');
const $queue = new KueMock(kue);

const app = require('./your-app-file');

describe('functionality that deals with kue', () => {
  before(() => $queue.clean());
  afterEach(() => $queue.clean());

  it('enqueues a job providing some correct data', () => {
    let jobData;

    $queue.stub('your job type', (job, done) => {
      jobData = job.data;
      done();
    });

    return yourJobRunnerFunction()
      .then(() => {
        expect(jobData).to.be.an('object')
          .that.is.eql({ foo: 'bar' });
      });
  });

  describe('when the job is completed', () => {
    beforeEach(() => {
      $queue.stub('your job type')
        .yields(null, { baz: 'qux' });
    });

    it('correctly handles the result', () => {
      return yourJobRunnerFunction()
        .then((result) => {
          expect(result).to.eql({ baz: 'qux' });
        });
    });

    // ...
  });

  describe('when the job is failed', () => {
    beforeEach(() => {
      $queue.stub('your job type')
        .yields(new Error('Oops!'));
    });

    it('correctly handles the job result', () => {
      return yourJobRunnerFunction()
        .catch((err) => {
          expect(err).to.be.an('error')
            .with.property('message', 'Oops!');
        });
    });

    // ...
  });
});

Upvotes: 1

Andrew Lavers
Andrew Lavers

Reputation: 8141

In my experience it's more straightforward to simply have redis running on localhost wherever you want to run your tests rather than dealing with a mocked version of kue.

First, to make sure kue is empty before each test it could be as simple as flushing redis, eg:

var kue = require('kue');
var queue = kue.createQueue();

queue.client.flushdb(function(err) {});

For #1, kue has a rangeByType() method that should solve your problem:

var getJobs = function(type, state, cb) {
   kue.Job.rangeByType(type, state, 0, -1, 'asc', cb);    
}
// After something happens
getJobs('myJobType', 'active', function(err, jobs) {});

For #2, you can use the same method and simply keep track of the job id to know that it has been replaced:

var jobId;
getJobs('myJobType', 'active', function(err, jobs) {
    assert.lengthOf(jobs, 1);
    jobId = jobs[0].id;
});

// After the thing happens
getJobs('myJobType', 'active' function(err, jobs) {
    assert.lengthOf(jobs, 1);
    assert.notEqual(jobId, jobs[0].id);
});

And if you ever need to query a job by ID you can do it like so:

kue.Job.get(jobId, function(err, job) {});

Upvotes: 3

Related Questions