I'm making my first attempt at Javascript testing, with Buster.js
I've followed the instructions at the Buster site to run "states the obvious" test. However, I haven't been able to import any of my existing .js files into the tests.
For instance, I have a file js/testLibrary.js, containing:
function addTwo(inp) {
return inp+2;
}
and a file test/first-test.js, containing:
// Node.js tests
var buster = require("buster");
var testLibrary = require("../js/testLibrary.js");
var assert = buster.referee.assert;
buster.testCase("A module", {
"Test The Library": function() {
result = addTwo(3);
console.log(result);
assert(true, 'a message for you');
}
});
Running buster-test gives:
Error: A module Test The Library
ReferenceError: addTwo is not defined
[...]
Replacing result = addTwo(3); with result = testLibrary.addTwo(3); gives:
Error: A module Test The Library
TypeError: Object #<Object> has no method 'addTwo'
[...]
I'm probably missing something really basic, but at present, I'm completely stumped. Can someone point me in the right direction?
That is because you are not exporting this function from the module.
Take a look at that:
http://nodejs.org/api/modules.html#modules_module_exports
Related
I am trying out Stitch, a serverless/hosted JavaScript environment from MongoDB. My main purpose is to help me learn modern JavaScript, but I am trying to write a useful app as well.
I have written the following function, and saved it in my Stitch app. I believe this follows the documented way to write functions in Stitch, and I have tested it from the Stitch administration console:
exports = function(query){
const http = context.services.get("HTTP");
const urlBase = context.values.get("stackOverflowApiUrl");
const options = [
'order=desc',
'sort=activity',
'site=stackoverflow',
'q=' + encodeURIComponent(query),
'user=472495',
'filter=!--uPQ.wqQ0zW'
];
return http
.get({ url: urlBase + '?' + options.join('&') })
.then(response => {
// The response body is encoded as raw BSON.Binary. Parse it to JSON.
const ejson_body = EJSON.parse(response.body.text());
return ejson_body.total;
});
};
This code is pretty simple - it obtains an http object for making external API fetches, and obtains a configuration value for a URL urlBase to contact (resolving to https://api.stackexchange.com/2.2/search/excerpts) and then makes a call to the Stack Overflow Data API. This runs a search query against my user and returns the number of results.
So far so good. Now, I want to call this function locally, in Jest. To do this, I have installed Node and Jest in a local Docker container, and have written the following test function:
const callApi = require('./source');
test('Simple fetch with no user', () => {
expect(callApi('hello')).toBe(123);
});
This fails, with the following error:
~ # jest
FAIL functions/callApi/source.test.js
✕ Simple fetch with no user (3ms)
● Simple fetch with no user
TypeError: callApi is not a function
2 |
3 | test('Simple fetch with no user', () => {
> 4 | expect(callApi('hello')).toBe(123);
| ^
5 | });
6 |
at Object.<anonymous>.test (functions/callApi/source.test.js:4:12)
Test Suites: 1 failed, 1 total
Tests: 1 failed, 1 total
Snapshots: 0 total
Time: 1.418s
Ran all test suites.
(In fact I was expecting it to fail, since it contains a global object context that Jest does not have access to. I will work out how to mock that later, but for now Jest cannot even see the function at all).
I suspect I can see the reason - in the Jest introduction docs, one has to do this for the SUT:
module.exports = function() { ... }
However the Stitch docs seem to require functions to be defined as:
exports = function() { ... }
I do not have a background in JavaScript to understand the difference. I could try module.exports in Stitch, but I would rather not, since this would either not work now, or cause a breakage in the future. Can Jest be instructed to "see" bare exports without the module prefix?
Incidentally, I have picked Jest because it is popular, and because some of my JavaScript colleagues vouch for it. However, I am not wedded to it, and would be happy to use something else if it is known to be better for Stitch development.
Update
Following the useful answer from jperl below, I find that the following construction is not possible in Stitch:
module.exports = exports = function() {}
I also cannot do this:
exports = function() {}
module.exports = exports
If I try either, I get the following error:
runtime error during function validation
So it looks like I have to get Jest to work without module.exports, or create a glue file that imports the exports version into module.exports, with the main file being used by Stitch, and the glue importer being used by Jest.
I suggest you to read this thread. And you're right in thinking it has to do with modules.exports vs exports. The thing is that module.exports and exports first point to the same thing. So something like this works:
//modify the same object that modules.exports is pointing to
exports.a = {}
exports.b = {}
but this won't:
exports = {}
Why? Because now exports points to something else than module.exports so what you're doing has no effect at all.
Update
Following some updates in the comments, we came to the view that Stitch does not seem to support the export format that Jest requires.
This is an addendum to jperl's answer, to show how I got Jest working while respecting Stitch's limitations.
Firstly, it is worth noting how a Stitch application is laid out. This is determined by the import/export format.
auth_providers/
functions/
function_name_1/
config.json
source.js
function_name_2/
config.json
source.js
...
services/
values/
The config.json file is created by Stitch remotely, and is obtained through a export. This contains ID information to uniquely identify the function in the same folder.
I believe it is common JavaScript practice to mix tests with source code, so I am following that style (I am new to modern JS, and I confess I find this style untidy, but I am running with it nevertheless). Thus I add a source.test.js file in each function folder.
Finally, since there is a discrepancy between what Stitch requires and what Jest requires, I have written a script to create a source code file under _source.js in each function folder.
So, each folder will contain these files (the underscore files will probably be ignored by Git, as they will always be generated):
_source.js
config.json
source.js
source.test.js
In order to create the underscored copies, I am using this shell script:
#!/bin/bash
# Copy all source.js files as _source.js
for f in $(find functions/ -name source.js); do cp -- "$f" "$(dirname $f)/_$(basename $f)"; done
# Search and replace in all _source.js files
for f in $(find functions/ -name _source.js); do sed -i -e 's/exports =/module.exports =/g' $f; done
A bit hacky perhaps, but it works!
I have a mochajs test file and a javascript code file in setup as below:
/js/module/codefile.js
/js/test/testfile.js
The codefile.js contains some javascript functions eg:
function addNumbers(a, b){
return a+b;
}
the testfile.js calls functions in the codefile to test them:
describe("Add numbers test", function() {
it("checks valid result", function() {
var a = 2;
var b = 1;
var result = addNumbers(a, b);
expect(result).to.equal(3);
});
});
From the command line I cd to the js folder (parent of test and module directories) then I run mocha and get the following error: ReferenceError: addNumbers is not defined at Context <anonymous> (test/testfile.js).
I can't actually see how it could be defined as how can mocha know where this function is comming from? (NB I am using client side JS so can't use import, and havent see any way to specificy (in Mocha or Karma or JS in general) where functions are defined as you would in Python or Java). Any ideas on how I can get simple unit tests running in mocha?
I initially tried getting mocha to run in WebStorm but gave up after similar errors.
Well, the mocha command is a nodejs program. This means that you can use Nodejs's module system to load your function.
function addNumbers(a, b){
return a+b;
}
module.exports = addNumbers;
and in your test file you will have
var addNumbers = require('../module/codefile.js');
describe("Add numbers test", function() {
it("checks valid result", function() {
var a = 2;
var b = 1;
var result = addNumbers(a, b);
expect(result).toEqual(3);
});
});
However, you said that you are using your code on the front-end. Well in this case you simply check if the module object exists. If it exists that means that your file is required by mocha for unit testing.
function addNumbers(a, b){
return a+b;
}
if (module && module.exports) {
module.exports = addNumbers;
}
If you want to get rid of this nasty if's, you can bundle your modules using browserify. Browserify helps you code on the front-end using the Nodejs's module system. So your code will remain the same.
I have the following Node.js project (which is a Minimal Working Example of my problem):
module1.js:
module.exports = function() {
return "this is module1!";
};
module2.js:
var module1 = require('./module1');
module.exports = function() {
return module1()+" and this is module2!";
};
server.js:
var module2 = require('./module2');
console.log(module2()); // prints: "this is module1! and this is module2!"
Now I want to create a client.html file that will also use module2.js. Here is what I tried (and failed):
naive version:
<script src='module2.js'></script>
<script>alert(module2());</script> // should alert: "this is module1! and this is module2!"
This obviously doesn't work - it produces two errors:
ReferenceError: require is not defined.
ReferenceError: module2 is not defined.
Using Node-Browserify: After running:
browserify module2.js > module2.browserified.js
I changed client.html to:
<script src='require.js'></script>
<script>
var module2 = require('module2');
alert(module2());
</script>
This doesn't work - it produces one error:
ReferenceError: module2 is not defined.
Using Smoothie.js by #Torben :
<script src='require.js'></script>
<script>
var module2 = require('module2');
alert(module2());
</script>
This doesn't work - it produces three errors:
syntax error on module2.js line 1.
SmoothieError: unable to load module2 (0 )
TypeError: module2 is not a function
I looked at require.js but it looks too complicated to combine with Node.js - I didn't find a simple example that just takes an existing Node.js module and loads it into a web page (like in the example).
I looked at head.js and lab.js but found no mention of Node.js's require.
So, what should I do in order to use my existing Node.js module, module2.js, from an HTML page?
The problem is that you're using CJS modules, but still try to play old way with inline scripts. That won't work, it's either this or that.
To take full advantage of CJS style, organize your client-side code exactly same way as you would for server-side, so:
Create client.js:
var module2 = require('./module2');
console.log(module2()); // prints: "this is module1! and this is module2!"
Create bundle with Browserify (or other CJS bundler of your choice):
browserify client.js > client.bundle.js
Include generated bundle in HTML:
<script src="client.bundle.js"></script>
After page is loaded you should see "this is module1! and this is module2!" in browser console
You can also try simq with which I can help you.
Your problems with Smoothie Require, were caused by a bug (https://github.com/letorbi/smoothie/issues/3). My latest commit fixed this bug, so your example should work without any changes now.
So I'm trying to teach myself Jasmine (for node) by working through a tutorial for a Mongoose project, TDD style, writing tests for what each step is supposed to accomplish, then following the actual tutorial, etc.
Of course, my first test is failing.
app.js at this point is two lines:
var mongoose = require('mongoose');
console.log(mongoose.version);
This runs fine. My test however, still fails:
var app = require('../src/app.js');
describe('App startup', function() {
it('loads mongoose', function() {
expect(app.mongoose.version).toBeDefined();
});
it('loads jasmine-jquery', function() {
expect($).toBeDefined();
});
});
Results in
Failures:
1) App startup loads mongoose
Message:
TypeError: Cannot read property 'version' of undefined
Stacktrace:
TypeError: Cannot read property 'version' of undefined
at null.<anonymous> (/home/jbhelfrich/mongooseBlog/spec/init.spec.js:5:36)
(The jquery test is, of course, expected to fail still at this point.) I've tried it with and without the 'app.' in the expect clause, but I get the same error--the test suite doesn't see the internals of app.js. But I know it's loading the app.js file correctly, because it's running it--the console.log output appears ahead of the test results.
So I suspect I've misunderstood something fundamental about scope, or some other rookie mistake, but I'm not sure what that is.
Node.js is structured into modules. If you want a a module's properties to be accessible, that module's properties must be defined in the module.exports variable. This is what an export might look like (note that exports references module.exports):
var mongoose = require('mongoose');
console.log(mongoose.version);
exports.mongoose = mongoose;
Then when you've used require() on a file with the code shown above, the variables will be set, where app is equivalent to module.exports in the module that is being loaded:
var app = require('../src/app.js');
console.log(app.mongoose.version);
I am trying to understand how to develop stand-alone Javascript code. I want to write Javscript code with tests and modules, running from the command line. So I have installed node.js and npm along with the libraries requirejs, underscore, and mocha.
My directory structure looks like this:
> tree .
.
├── node_modules
├── src
│ └── utils.js
└── test
└── utils.js
where src/utils.js is a little module that I am writing, with the following code:
> cat src/utils.js
define(['underscore'], function () {
"use strict";
if ('function' !== typeof Object.beget) {
Object.beget = function (o) {
var f = function () {
};
f.prototype = o;
return new f();
};
}
});
and test/utils.js is the test:
> cat test/utils.js
var requirejs = require('requirejs');
requirejs.config({nodeRequire: require});
requirejs(['../src/utils'], function(utils) {
suite('utils', function() {
test('should always work', function() {
assert.equal(1, 1);
})
})
});
which I then try to run from the top level directory (so mocha sees the test directory):
> mocha
node.js:201
throw e; // process.nextTick error, or 'error' event on first tick
^
Error: Calling node's require("../src/utils") failed with error: ReferenceError: define is not defined
at /.../node_modules/requirejs/bin/r.js:2276:27
at Function.execCb (/.../node_modules/requirejs/bin/r.js:1872:25)
at execManager (/.../node_modules/requirejs/bin/r.js:541:31)
...
So my questions are:
Is this the correct way to structure code?
Why is my test not running?
What is the best way to learn this kind of thing? I am having a hard time finding good examples with Google.
Thanks...
[sorry - momentarily posted results from wrong code; fixed now]
PS I am using requirejs because I also want to run this code (or some of it) from a browser, later.
Update / Solution
Something that is not in the answers below is that I needed to use mocha -u tdd for the test style above. Here is the final test (which also requires assert) and its use:
> cat test/utils.js
var requirejs = require('requirejs');
requirejs.config({nodeRequire: require});
requirejs(['../src/utils', 'assert'], function(utils, assert) {
suite('utils', function() {
test('should always work', function() {
assert.equal(1, 1);
})
})
});
> mocha -u tdd
.
✔ 1 tests complete (1ms)
The reason your test isn't running is because src/utils.js is not a valid Node.js library.
According to the RequireJS documentation, in order to co-exist with Node.js and the CommonJS require standard, you need to add a bit of boilerplate to the top of your src/utils.js file so RequireJS's define function is loaded.
However, since RequireJS was designed to be able to require "classic" web browser-oriented source code, I tend to use the following pattern with my Node.js libraries that I also want running in the browser:
if(typeof require != 'undefined') {
// Require server-side-specific modules
}
// Insert code here
if(typeof module != 'undefined') {
module.exports = whateverImExporting;
}
This has the advantage of not requiring an extra library for other Node.js users and generally works well with RequireJS on the client.
Once you get your code running in Node.js, you can start testing. I personally still prefer expresso over mocha, even though its the successor test framework.
The Mocha documentation is lacking on how to set this stuff up, and it's perplexing to figure out because of all the magic tricks it does under the hood.
I found the keys to getting browser files using require.js to work in Mocha under Node: Mocha has to have the files added to its suites with addFile:
mocha.addFile('lib/tests/Main_spec_node');
And second, use beforeEach with the optional callback to load your modules asynchronously:
describe('Testing "Other"', function(done){
var Other;
beforeEach(function(done){
requirejs(['lib/Other'], function(_File){
Other = _File;
done(); // #1 Other Suite will run after this is called
});
});
describe('#1 Other Suite:', function(){
it('Other.test', function(){
chai.expect(Other.test).to.equal(true);
});
});
});
I created a bootstrap for how to get this all working: https://github.com/clubajax/mocha-bootstrap
You are trying to run JS modules designed for browsers (AMD), but in the backend it might not work (as modules are loaded the commonjs way). Because of this, you will face two issues:
define is not defined
0 tests run
In the browserdefine will be defined. It will be set when you require something with requirejs. But nodejs loads modules the commonjs way. define in this case is not defined. But it will be defined when we require with requirejs!
This means that now we are requiring code asynchronously, and it brings the second problem, a problem with async execution.
https://github.com/mochajs/mocha/issues/362
Here is a full working example.
Look that I had to configure requirejs (amd) to load the modules, we are not using require (node/commonjs) to load our modules.
> cat $PROJECT_HOME/test/test.js
var requirejs = require('requirejs');
var path = require('path')
var project_directory = path.resolve(__dirname, '..')
requirejs.config({
nodeRequire: require,
paths: {
'widget': project_directory + '/src/js/some/widget'
}
});
describe("Mocha needs one test in order to wait on requirejs tests", function() {
it('should wait for other tests', function(){
require('assert').ok(true);
});
});
requirejs(['widget/viewModel', 'assert'], function(model, assert){
describe('MyViewModel', function() {
it("should be 4 when 2", function () {
assert.equal(model.square(2),4)
})
});
})
And for the module that you want to test:
> cat $PROJECT_HOME/src/js/some/widget/viewModel.js
define(["knockout"], function (ko) {
function VideModel() {
var self = this;
self.square = function(n){
return n*n;
}
}
return new VideModel();
})
Just in case David's answer was not clear enough, I just needed to add this:
if (typeof define !== 'function') {
var define = require('amdefine')(module);
}
To the top of the js file where I use define, as described in RequireJS docs ("Building node modules with AMD or RequireJS") and in the same folder add the amdefine package:
npm install amdefine
This creates the node_modules folder with the amdefine module inside.
I don't use requirejs so I'm not sure what that syntax looks like, but this is what I do to run code both within node and the browser:
For imports, determine if we are running in node or the browser:
var root = typeof exports !== "undefined" && exports !== null ? exports : window;
Then we can grab any dependencies correctly (they will either be available already if in the browser or we use require):
var foo = root.foo;
if (!foo && (typeof require !== 'undefined')) {
foo = require('./foo');
}
var Bar = function() {
// do something with foo
}
And then any functionality that needs to be used by other files, we export it to root:
root.bar = Bar;
As for examples, GitHub is a great source. Just go and check out the code for your favorite library to see how they did it :) I used mocha to test a javascript library that can be used in both the browser and node. The code is available at https://github.com/bunkat/later.