How to run mocha tests and node in a single npm test command ? (CI) - node.js

I want to use Circle CI to integrate a git project.
I'm using mocha for my tests .
What i want to do?
When running npm test I want:
my node server to start
my test file to run
How can I run a single npm test command to run both node and my mocha tests which are already wrapped in a single index.js file.
I have tried this in my package.json:
"scripts": {
"test": "node server/app.js & mocha server/tests/index.js",
"start": "node server/app.js",
"postinstall": "bower install"
}
The problems with the above
My server takes some time to start and the tests fail since they run before the server starts
Is there a standard way to run a server and the tests with a single command but I'm missing something?

If it is possible at all in your case I'd suggest using something like supertest to do the testing. This way, you can avoid having to start a server before starting the test.
I understand that there are scenarios where using supertest is not possible. In such case, you could poll your server in a before hook before all tests to wait until it is ready:
before(function (done) {
// Set a reasonable timeout for this hook.
this.timeout(5000);
function check() {
if (serverIsReady()) {
done();
return;
}
// The server is no ready, check again in 1/10th of a second.
setTimeout(check, 100);
}
check(); // Start checking.
});
I'm not sure what serverIsReady should be precisely in your case. It could be an attempt at getting a trivial path from your server like issuing a GET on the path /.

I think the key is to run your node server in your test, rather than trying to initialise it in another process.
Your mocha test should start with a require to your app, then each
of your tests can interact with it.
For example:
var http = require('http');
var server = http.createServer(function(req, res){
res.end('Hello World\n');
})
server.listen(8888);
describe('http', function(){
it('should provide an example', function(done){
http.get({ path: '/', port: 8888 }, function(res){
expect(res).to.have.property('statusCode', 200);
done();
})
})
})

What I do when running a test that needs certain pre-requisites is use mochas beforeEach() functionality.
From the documentation
You may also pick any file and add “root”-level hooks. For example, add beforeEach() outside of all describe() blocks. This will cause the callback to beforeEach() to run before any test case, regardless of the file it lives in (this is because Mocha has an implied describe() block, called the “root suite”).
beforeEach(function() {
console.log('before every test in every file');
});
In the before each code block you can run your command to start the server
using for example the exec library from npm
https://www.npmjs.com/package/exec
This will ensure your server is running before your tests are run allowing you to simply run npm test.

Related

How to knowif all tests have passed with mocha

I would like to know if it is possible to execute something if all my test files have passed with mocha.
Below: is my index.js file which is run by mocha (in order to get the order I want)
require('dotenv').config();
const logger = require('../toolkits/logger');
//silent mode for testing
logger.transports.forEach((t) => (t.silent = true));
require("./broker.test");
require("./mongo.test");
require("./auth.test");
require("./meal.test");
require("./bowl.test");
I want to process.exit(0) if the tests passed (to integrate it into gitlab CI).
I succeded in check if a test hasn't passed by doing this :
afterEach(function(){
if (this.currentTest.state === "failed")
process.exit(1);
})
But I can't detect if all the tests passed.
I can't just process.exit(0) after requiring all the tests because some of them are asynchronous.
Do you have an idea how I can do that?
! Update !
I found out that I could pass the --exit argument when running mocha from npm :
mocha file --exit

Execute npm script within mocha test

I currently have a script 'npm run make-file' which will create a boiler plate file. Nothing fancy at all, but I am trying to create a test for that in my test suite. I am using Mocha/Chai for testing.
Is it even possible to run a script in the terminal from a mocha test?
describe('Make File', function () {
it('Runs npm run make file', function (done) {
// What would belong here?
});
});

mocha tests not found: "0 passing"

I dont know why the test runner wont detect my tests.
npm test
myproject#1.0.0 test C:\Users\sjsui\Desktop\git_workshop\myproject
mocha ./built/source
0 passing (4ms)
PS C:\Users\sjsui\Desktop\git_workshop\myproject>
I know that test is the default folder that mocha searches for so that is how I structured my project.
built
\--source
\--test
\--test.js
here is my test.js file
describe("consumer tests", function()
{
it("runs bad request tests", (done) => {
let tests = new badrequesttests()
iteratebadtests(tests, done)
})
it("normal consumer tests", (done) => {
let tests = new normaltests()
iteratenormaltests(tests, done)
})
it("abnormal consumer tests", (done) => {
let tests = new unauthorizedtests()
iterateunauthorizedtests(tests, done)
})
})
npm scripts section in package.json
"scripts": {
"test": "mocha ./built/source"
},
as a side note, break points wont hit inside the test (visual studio code debug task -> mocha)
I think your test scripts are not detected y mocha because by default mocha does not scan sub-directories and your tests are inside a subdirectory of path you are passing at the time of invking mocha. There are two ways to resolve this.
Provide the path of test folder to mocha as below
mocha ./built/source/test
Trigger mocha tests with recursive flag. Using this flag mocha will scan the subdirectories of the path you provide
mocha ./built/source --recursive
I think this should solve your problem
You can pass the directory to search for your test files like below:
mocha -- ./built/source/**/*.test.js
This will check for all test files ending with .test.js in their file name in any directory within the source directory.
mocha -- ./built/source/*.test.js
That will check for test files within the source directory.

Why can't get the global variable which was set in globalSetup in test code?

I use Jest to do unit test in node.
And I use the new feature globalSetup which come in Jest v22.
I have defined a global variable in globalSetup.
But I can't get it in the test code. Console log is undefined.
Anyone in this question?
Thanks.
Jest version: 22.0.0
node version: 8.9.0
yarn version: 1.3.2
OS: mac High Sierra 10.13.2
The code as follow:
// package.json
{
"jest": {
"globalSetup": "<rootDir>/src/globalSetupTest.js"
}
}
// globalSetupTest.js
module.exports = async function() {
global.foo = 'foo';
console.log(`global setup: ${global.foo}`);
};
// App.test.js
describe('APP test', () => {
it('renders without crashing', () => {
console.log({ foo: global.foo });
});
});
// test result
yarn run v1.3.2
$ node scripts/test.js --env=node --colors
global setup: foo
PASS src/App.test.js
APP test
✓ renders without crashing (5ms)
console.log src/App.test.js:3
{ foo: undefined }
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 0.354s, estimated 1s
Ran all test suites.
There's a solution offered from Jest people themselves: https://jestjs.io/docs/en/puppeteer.html. Note that if you're using CRA, this won't work out of the box (solution below), cause it currently doesn't support testEnvironment option for Jest.
Anyways:
In Jest config file you setup paths to global setup, teardown and test environment scripts
In global setup script, you create a browser and write it's WSEndpoint to a file
In global teardown script, you simply close the browser
In testEnvironment script, you read WSEndpoint from the file you saved before and then use it to connect to the running browser - after this, browser is available in your tests by using a global variable
If you're using CRA, you can use a custom setup for these tests and run them completely separately. And if you're using Puppeteer for e2e tests, this is probably what you want to do anyway.
You just add another script to your package.json: "test:e2e": "jest -c ./jest-e2e.config.js" and set it up as you want. Now you will have npm run test for unit tests and npm run test:e2e for end to end tests.
For what I understood is a design decision of Jest because is considered a bad practice to share state across different tests. Tests run in parallel and they should keep their own state.
See:
https://github.com/facebook/jest/issues/6007#issuecomment-381743011
https://github.com/facebook/jest/issues/4118
Can you try..
global.foo = 'foo';
console.log(`global setup: ${global.foo}`);
(remove the exports)
You can try changing globalSetup to setupFiles. That one, won't expect a function.
https://facebook.github.io/jest/docs/en/configuration.html#setupfiles-array

Automate Functional Testing in node.js

I checked some NPM libraries to test a webpages or web-services. But all of them expect that the server is already running. Since I want to automate functional testing, how can I setup NPM package in such a way that
It can start the server
Test the application
Stop the server
So that I can test it locally, as well as on online CI tools like travis-ci or circleci.
Case 1: Webservice
I wrote a NPM package which starts nodejs HTTP(s) server. It can be started from command line $stubmatic. Currently, I use 2 approaches to test it,
manual : I manually start it from command line. Then run the tests.
Automatic: I use exec module to run a unix command which can start the application and run pkill command to kill the application. But for this automation, my application need to be installed on testing machine.
Case 2: Website
I have create a NPM package: fast-xml-parser and created a demo page within the repo so that I can be tested in the browser. To test the demo page, I currently start a local server using http-server npm package manually. Test the application.
What can be the better way to write automate functional tests for node js applications?
Note:
I never used task runners like gulp or grunt. So I'm not sure if they can help in this case.
In case 1, my application starts node js native HTTP server. I'm not using any 3rd party application like express currently.
This question mentions a new Docker container system for Travis that could be duplicated locally. It might be a way: How to run travis-ci locally
Did you look at supertest (SuperAgent driven library for testing HTTP servers) and expect (Assertions library)(documented here) with mocha (Test Framework)?
I use them and I never had any kind of problems for all the tests I do until now.
The documention in the links contains all the informations you need to build up your test.
Case 1: Webservice
Problem 1
As nodejs server.close() was not working. I copied paste this snippet in every test file which is starting my webservice.
try{
server.setup(options);
server.start();
}catch(err){
console.log(err);
}
Once all the tests are completed, server stops.
**Problem 2
I was using chai-http incorrectly. Here is the complete working solution.
//Need to be placed before importing chai and chai-http
if (!global.Promise) {
global.Promise = require('q');
}
var server = require('.././lib/server');
var chai = require('chai')
, chaiHttp = require('chai-http');
chai.use(chaiHttp);
try{
server.setup(someoptions);
server.start();
}catch(err){
console.log(err);
}
describe('FT', function () {
describe('scenario::', function () {
it('responds to POST', function (done) {
chai.request("http://localhost:9999")
.post('/someurl')
.then(res => {
expect(res.status).toBe(200);
//console.log(res.text);
done();
}).catch( err => {
console.log(err);
done();
});
});
});
Case 2: Website This was quite simple.
I used http-server to start the server so my html files can be accessed.
I used zombie js for browser testing. (There are many other options available for browser testing)
Here is the code
process.env.NODE_ENV = 'test';
const Browser = require('zombie');
const httpServer = require('http-server');
describe("DemoApp", function() {
var browser = new Browser({site: 'http://localhost:8080'});
var server = httpServer.createServer();
server.listen(8080);
beforeEach(function(done){
browser.visit('/', done);
});
describe("Parse XML", function() {
it("should parse xml to json", function(done) {
browser.pressButton('#submit');
browser.assert.text('#result', 'some result text');
done();
});
});
afterEach(function(){
server.close();
})
});

Resources