I am using node-cron to schedule a task to be executed once a day, and I want to test this in mocha.
I am using child_process.spawn to run the script, and I need to progress the internal clock of the created child. This is what i have so far:
it.only('Should run the script every 24 hours and trigger an event',function(done){
var clock = sinon.useFakeTimers(),
path = process.cwd() + '/cronjob/',
env = Object.create(process.env);
env.NODE_ENV = 'test';
var cronjob = child.spawn('node', ['index.js'], {
cwd: path,
env: env
});
cronjob.stdout.on('data',function(data){
console.log(data);
if (data.toString().trim() === 'Script ran successfully'){
cronjob.kill('SIGINT');
done();
}
});
cronjob.stderr.on('data',function(data){
done(new Error(data.toString()));
});
clock.tick(24*60*60*1000);
}
This only progresses time in the mocha test instance not in the index.js.
Is there any way i can pass the sinon timer to the child process?
I'm not sure if this will be at all useful, but I've done something similar using Node's vm. Here's a VERY simplified version of what I've done in the past.
Sample index.js:
setTimeout(function() {
console.log('ok');
}, 24*60*60*1000);
Sample index.spec.js
var sinon = require('sinon');
var vm = require('vm');
var fs = require('fs');
describe('sample', function() {
it('Should detect if script runs 24 hours later',function(){
var clock = sinon.useFakeTimers();
var script = fs.readFileSync(require.resolve('../index.js'));
sinon.stub(console, 'log');
var result = vm.runInThisContext(script);
clock.tick(24*60*60*1000);
sinon.assert.calledOnce(console.log);
sinon.assert.calledWith(console.log, 'ok');
console.log.restore();
});
});
Sample output:
sample
✓ Should run the script every 24 hours and trigger an event
1 passing (19ms)
In the above sample code I accomplish the following:
setup Sinon's fake timers
stub console.log() so I can make assertions later
execute the sample index.js in vm using the current context, but isolated from the local scope (index.js calls console.log() 24 hours later)
time travel 24 hours using Sinon's fake timers
make assertions on calls to console.log()
restore console.log() (so Mocha reporters don't get cranky)
Since I don't know what's going on in your index.js it's hard to say if a solution like the one I've outlined above could be helpful, but I hope it is.
Related
I'm using ResembleJS for image comparison. I can get it to run when I run it in a standalone script. Here's the code:
var compareImages = require('resemblejs/compareImages');
var fs = require('fs');
var path = require('path');
// The parameters can be Node Buffers
// data is the same as usual with an additional getBuffer() function
async function getDiff() {
var img = path.join(__dirname, 'small.jpg');
const data = await compareImages(
fs.readFileSync(img),
fs.readFileSync(img)
);
console.log(data);
fs.writeFileSync('./output.png', data.getBuffer());
}
getDiff();
Everything works as expected.
But when I run the comparison inside of a test in with the jest framework, it hangs and eventually times out. At first I thought maybe it was just running really slow, so I set my max timeout in jest to be 1 minute. Still failed. So I set my test image to be 1 pixel so it's the simplest test. Still wouldn't finish.
Running from a docker container with Node 8.9.4 (which is what comes from the docker hub node:8). Running jest 22.0.4.
Anybody else have issues running these two together?
I know Resemblejs runs tests with Jest, so not sure what could be causing the issue.
could you please post the code for your tests ?
Are you sure you are returning something from your test block ? In order for an test not to hang you need to return a promise which will resolve before the timeout. Below two examples
test("test", () => {
// test is done when writeFile resolves
return new Promise((resolve, reject) => {
fs.writeFile("path", "encoding", (err) => {
if (err) {
reject(err);
} else {
resolve();
}
});
});
});
test("test", async function () {
// test is done after the assertion
const result = await fetch();
expect(result).toBe(); // test;
});
I had a similar problem with slow tests with Jest, React and Docker (but I'm not using Resemblejs).
I found the solution on Github:
And for me solution was simply add "roots": ["./src"] to jest.config.js
My mocha tests are failing with:
MongoError: server XXXX sockets closed
I have a workaround how to fix them:
const https = require('https');
const server = https.createServer(..);
close() {
mongoose.disconnect(); // <-------- I will comment this line
this.server.close();
};
I would comment out the line mongoose.disconnect(); and my test suite starts working. I would like to clean up after my tests too. Each of my test files recreates server and starts from the scratch. It seems like the error appears because there needs to be some 'waiting' before the next test file executes.
How can I correct this error?
Solution - Captain Hook to the rescue!
If I understand correctly, you wish to startup and cleanup your server after the tests. You also have a series of repetitive tasks you need to do before and after each test.
Mocha has the perfect solution for you: Say hello to Mr. Hook!
Mocha hooks are functions that you can run both before all tests, after all tests, or before each test and after each test:
https://mochajs.org/#hooks
The documentation is pretty complete and I really do recommend it. I your case however, since you are dealing with databases, you probably will be dealing with async hooks.
Sounds complex? Don't worry!
This is how normal sync hooks work:
describe('hooks', function() {
before(function() {
// runs before all tests in this block
});
after(function() {
// runs after all tests in this block
});
beforeEach(function() {
// runs before each test in this block
});
afterEach(function() {
// runs after each test in this block
});
//tests
it("This is a test", () => {
assert.equal(1, 1);
});
});
async hooks only have one difference: they have a parameter done, which is called once your task is finished. Lets assume that we are setting up a DB that takes 1.5 seconds to setup. We want to do this before all the tests, and we only want to do it once.
Let's assume this is our listen function from our DB:
const listen = callback => {
setTimeout(callback, 1500);
};
So after 1.5 seconds, it calls the callback function signalizing it is ready for action.
Now lets see how we would make an async hook:
describe('hooks', function() {
let myDB;
before( done => {
myDB = newDB();
myDB(done);
});
//tests
});
And that's it! Hope it helps!
I want to compute some values before some tests. what is the best way to do that? To use "before" or call functions?
// Way 1
var expect = require('chai').expect;
describe("a test", function() {
var val1, val2;
before(function() {
val1 = computeVal1();
});
it("should return hi1", function() {
expect(val1).to.equal('hi1');
});
before(function() {
val2 = computeVal2();
});
it("should return hi2", function() {
expect(val2).to.equal('hi2');
});
});
Is the above way is better or the below way is better?
// Way 2
var expect = require('chai').expect;
describe("a test", function() {
var val1, val2;
val1 = computeVal1();
it("should return hi1", function() {
expect(val1).to.equal('hi1');
});
val2 = computeVal2();
it("should return hi2", function() {
expect(val2).to.equal('hi2');
});
});
It really makes no difference in this case because ultimately in the second example computeVal1() will run before the tests do. However, given this is exactly what the before / beforeEach hooks were designed for, I would be inclined to stay consistent with the framework and use them.
This is from the mocha documentation:
describe('hooks', function() {
before(function() {
// runs before all tests in this block
})
after(function(){
// runs after all tests in this block
})
beforeEach(function(){
// runs before each test in this block
})
afterEach(function(){
// runs after each test in this block
})
// test cases
})
So, there is not much difference in using before or calling functions directly under the describe. In your second example, val2's value won't be available to the first test, but in the first example, val2's value will be.
However, it is better practice IMO, to use beforeEach. This way state from one test does not affect another test.
Using the hooks (before and beforeEach) to initialize the data that your test uses is usually the best thing to do. Consider this:
In your 2nd snippet, if computeVal1 or computeVal2 fail, then Mocha won't run any test whatsoever. In the 1st snippet, Mocha may run some tests before it tries to call them and they fail. If you have many test files that Mocha loads, it may matter to you that Mocha runs as many test as it can before a failure occurs.
In your 2nd snippet, Mocha will almost always run computeVal1 and computeVal2 even if they are not needed. If they are computationally costly, you'll pay this cost every time. If you run mocha --grep=foo so that it does not select any of your tests, both functions will be called.
In your first snippet, Mocha will run them only if they are needed. If you run mocha --grep=foo, Mocha won't call your functions.
In your 2nd snippet, even using describe.skip for your describe call won't make Mocha skip calling the functions.
I'm setting up a test suite with Mocha, on an ExpressJS app. In the test, I want to drop all models in a collection before the suite runs, and I"m doing the following:
var Users = require("../models/Users").model;
before(function(done){
Users.remove({}, function(){
console.log("removed");
done();
});
//rest of the test suite here
The trouble is, this before hook is timing out. What am I missing here? BTW, if I change this to be an after hook, the result is the same - it never drops the models, and times out.
Mocha timeouts all tests that take more than 2 sec by default.
Try this:
var Users = require("../models/Users").model;
this.timeout(5000); //sets timeout to 5 sec
before(function(done){
Users.remove({}, function(){
console.log("removed");
done();
});
Disable timeout as specified in the manual.
I've got a node.js + express web server that I'm testing with Mocha. I start the web server within the test harness, and also connect to a mongodb to look for output:
describe("Api", function() {
before(function(done) {
// start server using function exported from another js file
// connect to mongo db
});
after(function(done) {
// shut down server
// close mongo connection
});
beforeEach(function(done) {
// empty mongo collection
});
describe("Events", function() {
it("Test1", ...);
it("Test2", ...);
it("Test3", ...);
it("Test4", ...);
it("Test5", ...);
});
});
If Mocha runs more than 4 tests at a time, it times out:
4 passing (2s)
1 failing
1) Api Test5:
Error: timeout of 2000ms exceeded
at null.<anonymous> (C:\Users\<username>\AppData\Roaming\npm\node_modules\moch\lib\runnable.js:165:14)
at Timer.listOnTimeout [as ontimeout] (timers.js:110:15)
If I skip any one of the 5 tests, it passes successfully. The same problem occurs if I reorder the tests (it's always the last one that times out). Splitting the tests into groups also doesn't change things.
From poking at it, the request for the final test is being sent to the web server (using http module), but it's not being received by express. Some of the tests make one request, some more than one. It doesn't affect the outcome which ones I skip. I haven't been able to replicate this behaviour outside mocha.
What on earth is going on?
With Mocha, if you declare a first argument to your (test) functions' callback (usually called done), you must call it, otherwise Mocha will wait until it's called (and eventually time out). If you're not going to need it in a test, don't declare it:
it('test1', function(done) {
..
// 'done' declared, so call it (eventually, usually when some async action is done)
done();
});
it('test2', function() {
// not an async test, not declaring 'done', obviously no need to call it
});
And since you're using http, try increasing http.globalAgent.maxSockets (which defaults to 5):
var http = require('http');
http.globalAgent.maxSockets = 100;
(I believe 0 turns it off completely but I haven't tried).