How to mock test a Node.js CLI with Jest? - node.js

I'm stuck at the very beginning, simply requiring the CLI and capturing its output. I've tried two methods but both don't work.
This is my cli.js:
#!/usr/bin/env node
console.log('Testing...');
process.exit(0);
And this my cli.test.js:
test('Attempt 1', () => {
let stdout = require("test-console").stdout;
let output = stdout.inspectSync(function() {
require('./cli.js');
});
expect(output).toBe('Testing...');
});
test('Attempt 2', () => {
console.log = jest.fn();
require('./cli.js');
expect(console.log.calls).toBe(['Testing...']);
});
Doesn't really matter which test is actually being run, the output is always:
$ jest
RUNS bin/cli.test.js
Done in 3.10s.

Node.js CLI applications are no different to other applications except their reliance on environment. They are expected to extensively use process members, e.g.:
process.stdin
process.stdout
process.argv
process.exit
If any of these things are used, they should be mocked and tested accordingly.
Since console.log is called directly for output, there's no problem to spy on it directly, although helper packages like test-console can be used too.
In this case process.exit(0) is called in imported file, so spec file early exits, and next Done output is from parent process. It should be stubbed. Throwing the error is necessary so that code execution is stopped - to mimic the normal behavior:
test('Attempt 2', () => {
const spy = jest.spyOn(console, 'log');
jest.spyOn(process, 'exit').mockImplementationOnce(() => {
throw new Error('process.exit() was called.')
});
expect(() => {
require('./cli.js');
}).toThrow('process.exit() was called.');
expect(spy.mock.calls).toEqual([['Testing...']]);
expect(process.exit).toHaveBeenCalledWith(0);
});

Related

Selenium Test Case not getting executed with Jest

I am working on setting up an automation test suite for an application using selenium and jest and it turns out that the console never reaches the inner body of the minimalist test case written below.
describe('When a user Opens Launchpad', () => {
test('It should be able to Navigate to Tasks Application without errors', async () => {
driver.get('http://localhost:4004/fiori.html').then(function () {
const temp = driver.findElement(By.xpath("li[#id='__tile11']"));
temp.then(function (element){
element.getAttribute("innerHTML");
expect(element.getText()).toBe("VT Dashboard");
})
});
}, 200000);
});
I looked online and tried multiple fixes like putting the driver.get() method above all these functions, making the test cases synchronous and using getText() instead of getAttribute() but none of them worked.
I either get an element not found error (The element actually exists when I check it on the chromium browser) or the test case executes successfully without reaching the expect statement in debug mode.
Bottomline is that driver.findElement() returns a promise instead of an element and would be great if I could get an element instead of promise.
Any help or correction here would be greatly appreciated.
If the function is async you should return the promise chain from that function or just use await keyword. So try following:
test('It should be able to Navigate to Tasks Application without errors', async () => {
await driver.get('http://localhost:4004/fiori.html');
const temp = await driver.findElement(By.xpath("li[#id='__tile11']"));
element.getAttribute("innerHTML");
expect(element.getText()).toBe("VT Dashboard");
}, 200000);
or
test('It should be able to Navigate to Tasks Application without errors', async () => {
return driver.get('http://localhost:4004/fiori.html').then(function () {
const temp = driver.findElement(By.xpath("li[#id='__tile11']"));
temp.then(function (element){
element.getAttribute("innerHTML");
expect(element.getText()).toBe("VT Dashboard");
})
});
}, 200000);

How to avoid looping expect when asserting on stdout from child

I'm trying to write an integration test in Jest with TypeScript for a logging utility that has some logic and wraps Gulp Fancy Log. I can't use Jest's spyOn with console (no stdout event occurs on the spy), I think because fancy log resets console.log.
My approach is to spawn a fixture to test the logging utility with ts-node, and then subscribe to the data event of the child process stdout stream. But it's called twice, first with fancy log's work and second with the param provided to log:
test('prints a string given to the logging function', done => {
const loggerInstance = path.resolve(process.cwd(), 'logger_string_param.ts')
const sut = spawn('ts-node', [loggerInstance])
sut.stdout.on('data', data => {
// can't expect here, it's called twice, first with `[16:41:50]`
// or similar and then with `something happened`
console.log(data.toString())
})
sut.on('exit', () => done())
})
And the fixture:
import { log } from '../utils'
log(`something happened`)
I would like to assert inside the data event listener, in the order the assertion is called (e.g. different assertions for first and second call). How can I do that (or refactor my test to achieve it)?

Force node-qrcode to put data into PNG files before the end of a script

I'm trying to write a script that create several QR code PNG files and then does something with the the newly created files.
I'm using node-qrcode but it doesn't flash the data into the files before the end of the script.
Here is the relevant part of the script:
const QRCode = require('qrcode')
const myFunc = async () => {
return new Promise( (res, rej) => {
QRCode.toFile(
'foo.png',
[{ data: [253,254,255], mode: 'byte' }],
() => {
res(true);
console.log('callback')
}
)
});
}
(async () => {
await myFunc()
})()
console.log('almost done');
In your case the output will be the following and it is expected as NodeJS is still asynchronous even with async/await.
almost done
callback
NodeJS reaches console.log('almost done'); first when processing your script, while QRCode.toFile() is working to resolve your promise and output console.log('callback'). Processing of QR code takes some time and it will be finished some time later, but almost done output is being reached first.
Also your outputs are executed in different contexts, so it is not expected to be executed one-by-one.
UPD:
To execute sync and async operations in sequence you can do the following.
(async () => {
console.log('Operation before `myfunc`');
await myFunc();
console.log('Operation after `myfunc`');
await smthElse();
console.log('End Of Script');
})()
In this case you should wrap all your initial scope into async function and invoke it in-place like described above (and in question) with (async () => { ... })(). And your main scope will contain only this single function which is being invoked right away.
As stated in docs:
The async function declaration defines an asynchronous function, which returns an AsyncFunction object. An asynchronous function is a function which operates asynchronously via the event loop, using an implicit Promise to return its result. But the syntax and structure of your code using async functions is much more like using standard synchronous functions.

Resemblejs in jest hangs

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

How can I build my test suite asynchronously?

I'm trying to create mocha tests for my controllers using a config that has to be loaded async. Below is my code. However, when the mocha test is run, it doesn't run any tests, displaying 0 passing. The console.logs are never even called. I tried doing before(next => config.build().then(next)) inside of the describe, but even though the tests run, before is never called. Is there a way to have the config be loaded one time before any tests are run?
'use strict';
const common = require('./common');
const config = require('../config');
config
.build()
.then(test);
function test() {
console.log(1);
describe('Unit Testing', () => {
console.log(2);
require('./auth');
});
}
You should run Mocha with the --delay option, and then use run() once you are done building your test suite. Here is an example derived from the code you show in the question:
'use strict';
function test() {
console.log(1);
describe('Unit Testing', () => {
console.log(2);
it("test", () => {
console.log(3);
});
});
// You must use --delay for `run()` to be available to you.
run();
}
setTimeout(test, 1000);
I'm using setTimeout to simulate an asynchronous operation. Using --delay and run() allows you to build a suite that is the result of an asynchronous computation. Note, however, that the suite must be built in one shot. (You cannot have an asynchronous process inside describe that will make calls to it. This won't work.)
One thing you should definitely not do is what rob3c suggests: calling describe or it (or both) from inside a hook. This is a mistake that every now and then people make so it is worth addressing in details. The problem is that it is just not supported by Mocha, and therefore there are no established semantics associated with calling describe or it from inside a hook. Oh, it is possible to write simple examples that work as one might expect but:
When the suite becomes more complex, the suite's behavior no longer corresponds to anything sensible.
Since there are no semantics associated with this approach, newer Mocha releases may handle the erroneous usage differently and break your suite.
Consider this simple example:
const assert = require("assert");
const p = Promise.resolve(["foo", "bar", "baz"]);
describe("top", () => {
let flag;
before(() => {
flag = true;
return p.then((names) => {
describe("embedded", () => {
for (const name of names) {
it(name, () => {
assert(flag);
});
}
});
});
});
after(() => {
flag = false;
});
it("regular test", () => {
assert(flag);
});
});
When we run it, we get:
top
✓ regular test
embedded
1) foo
2) bar
3) baz
1 passing (32ms)
3 failing
// [stack traces omitted for brevity]
What's going on here? Shouldn't all the tests pass? We set flag to true in the before hook for the top describe. All tests we create in it should see flag as true, no? The clue is in the output above: when we create tests inside a hook, Mocha will put the tests somewhere but it may not be in a location that reflects the structure of the describe blocks in the code. What happens in this case is that Mocha just appends the tests created in the hook the the very end of the suite, outside the top describe, so the after hook runs before the dynamically created tests, and we get a counter-intuitive result.
Using --delay and run(), we can write a suite that behaves in a way concordant with intuition:
const assert = require("assert");
const p = Promise.resolve(["foo", "bar", "baz"]).then((names) => {
describe("top", () => {
let flag;
before(() => {
flag = true;
});
after(() => {
flag = false;
});
describe("embedded", () => {
for (const name of names) {
it(name, () => {
assert(flag);
});
}
});
it("regular test", () => {
assert(flag);
});
});
run();
});
Output:
top
✓ regular test
embedded
✓ foo
✓ bar
✓ baz
4 passing (19ms)
In modern environments, you can use top-level await to fetch your data up front. This is a documented approach for mocha: https://mochajs.org/#dynamically-generating-tests
Slightly adapting the example from the mocha docs to show the general idea:
function fetchData() {
return new Promise((resolve) => setTimeout(resolve, 5000, [1, 2, 3]));
}
// top-level await: Node >= v14.8.0 with ESM test file
const data = await fetchData();
describe("dynamic tests", function () {
data.forEach((value) => {
it(`can use async data: ${value}`, function () {
// do something with data here
});
});
});
This is nice as it is on a per-file basis, and doesn't involve you taking on management responsibility of the test runner as you do with --delay.
The problem with using the --delay command line flag and run() callback that #Louis mentioned in his accepted answer, is that run() is a single global hook that delays the root test suite. Therefore, you have to build them all at once (as he mentioned), which can make organizing tests a hassle (to say the least).
However, I prefer to avoid magic flags whenever possible, and I certainly don't want to have to manage my entire test suite in a single global run() callback. Fortunately, there's a way to dynamically create the tests on a per-file basis, and it doesn't require any special flags, either :-)
To dynamically create It() tests in any test source file using data obtained asynchronously, you can (ab)use the before() hook with a placeholder It() test to ensure mocha waits until before() is run. Here's the example from my answer to a related question, for convenience:
before(function () {
console.log('Let the abuse begin...');
return promiseFn().
then(function (testSuite) {
describe('here are some dynamic It() tests', function () {
testSuite.specs.forEach(function (spec) {
it(spec.description, function () {
var actualResult = runMyTest(spec);
assert.equal(actualResult, spec.expectedResult);
});
});
});
});
});
it('This is a required placeholder to allow before() to work', function () {
console.log('Mocha should not require this hack IMHO');
});

Resources