How can I test JavaScript code without using an additional framework such as Mocha? Is it possible to create a unit test case, write test functions manually, test the code, etc.?
I've tried to write a test case, but even though they were in the same folder, I couldn't link them.
Let's say this is a function in the main.js file
function calculate(a, b) {
return a + b;
}
And this is a test case in the testMain.js file
function testCalculate(){
if(calculate(1, 1) == 2)
console.log('It Works!');
else
console.log('Test failed');
}
testCalculate();
When I try to run testMain.js in the IntelliJ IDEA IDE I get an error similar to
"ReferenceError: calculate is not defined"
It depends whether you are trying to test Node.js code or front end code. In both cases you have to "expose" the function under test to your test framework.
Node.js
// main.js
const obj = {};
obj.sum = (a, b) => {
return a+b;
};
module.exports = obj; // Export 'obj' so that it is visible from your test runner
// test.js
const main = require('main.js');
const assert = require('assert');
const it = (desc, fn) => {
try {
fn();
console.log('\x1b[32m%s\x1b[0m', `\u2714 ${desc}`);
} catch (error) {
console.log('\n');
console.log('\x1b[31m%s\x1b[0m', `\u2718 ${desc}`);
console.error(error);
}
};
it('should return the sum of two numbers', () => {
assert.strictEqual(main.sum(5, 10), 15);
});
When you run node test.js you should be able to see the test result.
Front End
// app.js
self.myapp = myapp; // All the methods in myapp will be exposed globally
myapp.sum = function(a, b) {
return a + b;
}
// test.js
function it(desc, fn) {
try {
fn();
console.log('\x1b[32m%s\x1b[0m', '\u2714 ' + desc);
} catch (error) {
console.log('\n');
console.log('\x1b[31m%s\x1b[0m', '\u2718 ' + desc);
console.error(error);
}
}
function assert(condition) {
if (!condition) {
throw new Error();
}
}
it('should return a sum of two integers', function(){
assert(myapp.sum(5, 10) === 15);
});
// test.html - This is your test runner for the front end
<html>
...
<body>
...
<script src="app.js"></script>
<script src="test.js"></script>
</body>
</html>
Open test.html in a browser and open the browser console. You should be able to see the success message.
This way you can write test cases for Node.js and front end JavaScript code without using Mocha or any other framework.
To make your code work, your testMain.js file needs to import your main.js code somehow.
In the main.js file:
function calculate(a, b) {
return a+b;
}
module.exports.calculate = calculate
in testMain.js file, import the main.js:
var main = require('main.js')
function testCalculate(){
if(main.calculate(1+1)==2)
console.log('It Works!');
else
console.log('Test failed');
}
Note: I'm aware this isn't necessarily showing good coding style, just aiming to demonstrate what the original issue was with minimal changes to the original snippets
That said, it's not usually worth reinventing the wheel and building your own test framework. Can you clarify the reason why you would like to avoid an existing framework? If you are looking for simplicity, maybe something like jstinytest would do the trick.
If it is a Node.js application you can simply require the other file and import the other function. If the project uses Babel you can use ES6 import to import the function from the other file.
I was also bothered by the same issue for a while. The question is how to test your JavaScript code with out a testing framework since testing frame works bring a lot to the table and most of the time they get into the way.
The answer is to use assertion libraries without the testing frame work. For example you can just use chai assertion library with out mocha frame work
you can simple install chai with
npm install chai
After that you can just use it:
var should = require('chai').should()
const log = console.log;
//log(should);
//const letters = "abcdef";
const letters = 555;
letters.should.be.a('string');
Related
I am creating a test suite that will be run by mocha. In the test, I plan on using should and chai at the very least. I am testing JSON objects. I want mocha to be silent about successful tests and I want to control what the errors look like at the end of the run. Right now, there is a lot of red text describing the JSON object and other information. What I want is to override the output.
Here is an example test for an element in the JSON object which I would import from testData.
var should = require("should");
var chai = require("chai");
var expect = chai.expect;
const testData = require("./testData");
for (t of testData.data) {
describe("This will fail", function() {
it("Should have a zipperNut",function(done) {
t.should.have.property('zipperNut');
done();
});
});
}
What I want is to see just this:
$ mocha testSuite.js
zipperNut
$
And so on for all the tests I want to run. Can I do this? Do I need to use a different library?
Mocha's output is determined by something called a "default reporter". If you want to change the output you can specify a different reporter, which is presumably available through npm or similar. To specify a new custom reporter you do this:
$ mocha --reporter my-reporter.js
Where my-reporter.js is the file containing my "reporter". For me, I just want the plain name of the failing tests, so my reporter would look like:
'use strict';
const Mocha = require('mocha');
const {
EVENT_RUN_END,
EVENT_TEST_FAIL,
} = Mocha.Runner.constants;
class MyReporter {
constructor(runner) {
const stats = runner.stats;
runner
.on(EVENT_TEST_FAIL, (test, err) => {
console.log(
`${test.title}`
);
})
.once(EVENT_RUN_END, () => {
console.log(`end: ${stats.passes}/${stats.passes + stats.failures} ok`);
});
}
}
module.exports = MyReporter;
Documentation here:
https://mochajs.org/api/tutorial-custom-reporter.html
I'm making a bot for the mattermost server of my company (like a discord bot), and for my bot commands, I created one file per command.
The idea is that to send a command, the user must send a message like "!giveChannelPerms arg1 arg 2 ...". The bot will parse the message to identify the command (in this case !giveChannelPerms) and execute the code related to the command.
The problem is that for each command, I have to require() the file and make an if {} else if {} else if {} ... to find the command, as you can see with the code below.
const giveChannelPerms = require('../cmd/giveChannelPerms');
const removeChannelPerms = require('../cmd/removeChannelPerms');
[...]
if (cmd == "!giveChannelPerms") {
giveChannelPerms.giveChannelPerms(post, args, db, obj);
} else if (cmd == "!removeChannelPerms") {
removeChannelPerms.removeChannelPerms(post, args, db, obj);
}
This code is good if we only have 2 commands for our bot, but the more commands I create, the more require() and if {} else if {} will be big.
Isn't there a more "optimized" way to do what I'm trying to do? I had thought of doing something like C function pointers but I have no idea how to do it.
If you want less require and reduce if elses, I recommend you to create a file importing your commands and returning an associated map
const { giveChannelPerms } = require('../cmd/giveChannelPerms');
const { removeChannelPerms } = require('../cmd/removeChannelPerms');
const cmdMap = new Map();
cmdMap.set('!giveChannelPerms', giveChannelPerms)
cmdMap.set('!removeChannelPerms', removeChannelPerms)
export default cmdMap
Then you will be able to import it only once and use it without conditions in your file :
// Imported multiples functions in one require
const commands = require('../cmd/commands');
// Will execute function associated to cmd string without conditions
commands.get(cmd)(post, args, db, obj);
Borrowing from https://stackoverflow.com/a/38397640/932256 try:
const commandFiles = {
giveChannelPerms: require('../cmd/giveChannelPerms'),
removeChannelPerms: require('../cmd/removeChannelPerms')
};
const commandList = Object.keys(commandFiles);
if (cmd.match(new RegExp('^!(' + commandList.join('|') + ')$'))) {
let baseCommand = cmd.replace('!', '');
commandFiles[baseCommand][baseCommand](post, args, db, obj);
}
Now all you need to do is add your commands to the commandFiles object.
Maybe something like this would work
[...]
require('../cmd/giveChannelPerms').then(res => {
if (res) {
res.giveChannelPerms(post, args, db, obj);
} else {
require('../cmd/removeChannelPerms').then(rslt => {
if (rslt) rslt.removeChannelPerms(post, args, db, obj);
})
}
});
I think you are looking for below two solution, hope this helps
More elegant way to manage your imports -
For this you can create a index file (index.js) which will import all your commands from different file and export all commands from one place which is index.js
Simplify your if else condition - from your code snippet, it seems like you need to evaluate against each command as the code then executed is different for each command. As there is no escape from, switch-case will provide you better code readability than if-else
const Cmds = require('../cmd/index');
// or you use import as below
//const {giveChannelPerms, removeChannelPerms} = require('../cmd/index')
switch(cmd)
case: '!giveChannelPerms'
Cmds.giveChannelPerms.giveChannelPerms(post, args, db, obj);
case: '!removeChannelPerms'
Cmds.removeChannelPerms.removeChannelPerms(post, args, db, obj);
defualt:
// do-somthing
}
I have an instability issue.
I'm using openzeppelin-test-helpers, and first had an issue with typescript saying Could not find a declaration file for module '#openzeppelin/test-helpers'.. This issue was solved by creating a .d.ts file containing declare module "#openzeppelin/test-helpers";.
However, adding this created a new problem, which is that now, most of the time, only one file is being run by rm -rf build && truffle test (I guess this is similar to truffle test --reset).
I got 2 test files. The first one looks like:
require("chai")
.use(require("chai-as-promised"))
.should();
const EventHandler = artifacts.require("EventHandler");
const { expectRevert } = require("#openzeppelin/test-helpers");
contract("EventHandler", function([_, superAdmin0, admin0, device0]) {
beforeEach(async function() {
this.eventHandler = await EventHandler.new(superAdmin0);
});
describe("Initial tests", function() {
it("should print hello", async function() {
await this.eventHandler
.printHello()
.should.eventually.equal("Hello");
});
});
});
The second one looks like:
require("chai")
.use(require("chai-as-promised"))
.should();
const { expectRevert } = require("#openzeppelin/test-helpers");
const EventHandler = artifacts.require("EventHandler");
contract("Roles", function([_, superAdmin0, superAdmin1, admin0, device0]) {
beforeEach(async function() {
this.EventHandler = await EventHandler.new(superAdmin0);
});
it("...should work", async function() {});
});
When I comment the content of one file, or just what's inside contract(..., {}), the other file works just fine and the tests pass successfully.
However, whenever I leave those 2 files uncommented, I get a massive error:
Error: Returned values aren't valid, did it run Out of Gas?
Of course, resetting ganache-cli didn't solve anything...
Does anyone know where it could come from?
Lets say we are using setInterval inside of a hapi plugin, like so:
// index.js
internals = {
storage: {},
problemFunc: () => {
setInterval((storage) => {
storage.forEach((problem) => {
problem.foo = 'bar';
});
}, 500);
}
};
module.exports.register = (server, options, next) => {
server.on('start', () => { // called when the server starts
internals.storage.problem1 = {};
internals.storage.problem2 = {};
internals.storage.problem3 = {};
internals.problemFunc(internals.storage);
});
}
In our tests for this server, we may start up and stop the server many times to test different aspects of the server. Sometimes, we will get an error like cannot set property 'foo' of undefined. This is because the server gets shutdown right before that async code runs, and internals.storage.problem gets removed right along with the server stop.
This makes total sense, and I don't have a problem with that. I'd really like to know what would be some good ways to make sure my tests work 100% of the time rather than 90% of the time.
We could do:
problemFunc: () => {
setInterval((storage) => {
storage.forEach((problem) => {
if (problem !== undefined) { // check if deleted
problem.foo = 'bar';
}
});
}, 500);
}
or:
problemFunc: () => {
setInterval((storage = {}) => { // default assignment
storage.forEach((problem) => {
problem.foo = 'bar';
});
}, 500);
}
But I would rather not add conditionals to my code just so that my tests pass. Also, this can cause issues with keeping 100% code coverage because then sometimes that conditional will get run and sometimes it wont. What would be a better way to go about this?
It's absolutely normal to have slight differences in set-up and configuration when running code in a test environment.
A simple approach is to let the application know the current environment, so it can obtain the appropriate configuration and correctly set-up the service. Common environments are testing, development, staging and production.
Simple example, using an environment variable:
// env.js
module.exports.getName = function() {
return process.env['ENV'] || 'development'
}
// main.js
if (env.getName() !== 'testing') {
scheduleBackgroundTasks()
}
Then run your tests passing the ENV variable, or tell your test runner to do it:
ENV=testing npm test
I am writing test cases for NODE JS API. But wherever console.log() is there in routes or services of NODE JS File, it gets printed to CLI. Is there a way to mock these so that these won't get printed in CLI.
I have explored couple of libraries like Sinon, Stub for mocking. But couldn't grasp the working of those libraries.
You can override function entirely: console.log = function () {}.
You should not try to mock console.log itself, a better approach is for your node modules to take a logging object. This allows you to provide an alternative (ie. a mock) during testing. For example:
<my_logger.js>
module.exports = {
err: function(message) {
console.log(message);
}
}
<my_module.js>
var DefaultLogger = require('my_logger.js');
module.exports = function(logger) {
this.log = logger || DefaultLogger;
// Other setup goes here
};
module.exports.prototype.myMethod = function() {
this.log.err('Error message.');
};
<my_module_test.js>
var MyModule = require('my_module.js');
describe('Test Example', function() {
var log_mock = { err: function(msg) {} };
it('Should not output anything.', function() {
var obj = new MyModule(log_mock);
obj.myMethod();
});
});
The code here I've simplified, as the actual test isn't the reason for the example. Merely the insertion of alternative logging.
If you have a large codebase with lots of console.log calls, it is better to simply update the code as you add tests for each method. Making your logging pluggable in this way makes your code easier and more receptive to testing. Also, there are many logging frameworks available for node. console.log is fine during development when you just want to dump out something to see what's going on. But, if possible, try to avoid using it as your logging solution.
I could not find a solution which only hides the console.log calls in the module to be tested, and mocks none of the calls of the testing framework (mocha/chai in my case).
I came up with using a copy of console in the app code:
/* console.js */
module.exports = console;
/* app.js */
const console = require('./console');
console.log("I'm hidden in the tests");
/* app.spec.js */
const mockery = require('mockery');
var app;
before(() => {
// Mock console
var consoleMock = {
log: () => {}
}
mockery.registerMock('./console', consoleMock);
// Require test module after mocking
app = require('./app');
});
after(() => {
mockery.deregisterAll();
mockery.disable();
});
it('works', () => {});
You could do something along the lines of adding these before/after blocks to your tests, but the issue is that mocha actually uses console.log to print the pretty messages about the results of the test, so you would lose those
describe('Test Name', function() {
var originalLog;
beforeEach(function() {
originalLog = console.log;
console.log = function () {};
});
// test code here
afterEach(function() {
console.log = originalLog;
})
})
The problem is that your output would just look like
Test Name
X passing (Yms)
Without any intermediate text