I can get the test to run on the cloud, but it is failing. However, it works local. I think it is because I don't have the right server address. I tried myserver.bluemix.net, localhost:5001 and the null that works local. I can't seem to find the address.
my unit test:
process.env.NODE_ENV = 'test';
var chai = require('chai');
var chaiHttp = require('chai-http');
var app = require('../index');
var cfenv = require('cfenv');
var should = chai.should();
var expect = chai.expect;
chai.use(chaiHttp);
describe('Conversation', function() {
var serviceBaseUrl = '';
if (process.env.test_env == 'cloud') {
serviceBaseUrl
= 'http://' + '127.0.0.1:5001';
}
it ('should return message', function(done){
chai.request(app)
.post(serviceBaseUrl + '/api/v1/conversation')
.send({input: "test", ConverationId: ""})
.end(function (err, res) {
res.status.should.equal(200);
console.log(res.body);
done();
});
});
});
this is the error:
Server running at http://127.0.0.1:5001
Conversation
1) should return message
double callback!
0 passing (33ms)
1 failing
1) Conversation
should return message:
Uncaught TypeError: Cannot read property 'status' of undefined
at test/test-conversation.js:27:12
at Test.Request.callback (/home/pipeline/79a4adb4-e686-494a-9974-3c5860240fcb/node_modules/superagent/lib/node/index.js:615:12)
at ClientRequest.<anonymous> (/home/pipeline/79a4adb4-e686-494a-9974-3c5860240fcb/node_modules/superagent/lib/node/index.js:567:10)
at Socket.socketErrorListener (_http_client.js:309:9)
at emitErrorNT (net.js:1281:8)
at _combinedTickCallback (internal/process/next_tick.js:74:11)
at process._tickCallback (internal/process/next_tick.js:98:9)
If this is a unit test, you should use mocks in order to isolate your testing environment. The problem is that your localhost is going to have a different base url than when your app is deployed to the cloud.
Consider using a library like nock to mock your api requests.
If you are doing and/or want to do integration tests, you can set the base url with something like this:
const base = process.env['ROUTE'] || 'http://localhost:3000/route';
(Where process.env['ROUTE'] could be something like 'https://app.mybluemix.net/route'.)
Related
Summary of Problem
Receiving : Timeout - Async callback was not invoked within the 5000ms timeout specified by jest.setTimeout.Timeout when trying to run a test with Jest and supertest.
Specs
Koa2 project, Jest/Supertest testing, Babel 7.9.0 recommended configuration
What I've tried
I have a simple test from the same file running which I omitted from the code below for brevity. I've also tried sending an HTTP request from the browser - this file is imported & 'listen'ed in a server file. The request is failing because it is blocked by a CORS policy - I think this is a problem for another day and isn't affecting my test timing out.
I also tried removed .callback() from the supertest(..) call:
const response = await supertest(app).post('/save-material');
at which point I get TypeError: app.dress is not a function.
Here is the content of my test file:
process.env.NODE_ENV = 'test';
const app = require('../../src/server/app.js')
const supertest = require('supertest')
test('save-material returns response', async() => {
const response = await supertest(app.callback()).post('/save-material');
expect(response.status).toBe(200);
expect(response.body.status).toBe('success');
expect(response.body.msg).toBe('Material saved')
});
Here is the content of the imported file (app.js) from above:
require('#babel/register'); // not entry point - but is entry point for some tests
const Koa = require('koa');
var Router = require('koa-router')
const app = new Koa();
const router = new Router();
router
.post('/save-material', async(ctx) => {
ctx.response = {
status: 'success',
msg: 'Material saved'
}
return ctx;
})
app.use(router.routes());
app.use(router.allowedMethods());
module.exports = app;
I wish to render a page using Nuxt's renderAndGetWindow in order to test the values returned by my API.
Here's how I do it:
// Nuxt instance
let nuxt = null;
// Our page to test
let homePage = null;
beforeAll(async (done) => {
// Configuration
const rootDir = resolve(__dirname, '../..');
let config = {};
config = require(resolve(rootDir, 'nuxt.config.js'));
config.rootDir = rootDir; // project folder
config.env.isDev = false; // dev build
config.mode = 'universal'; // Isomorphic application
nuxt = new Nuxt(config);
await new Builder(nuxt).build();
nuxt.listen(3001, 'localhost');
homePage = await nuxt.renderAndGetWindow('http://localhost:3001/');
});
Which gives me 2 distinct errors:
console.error node_modules/jest-jasmine2/build/jasmine/Env.js:157
Unhandled error
console.error node_modules/jest-jasmine2/build/jasmine/Env.js:158
TypeError: setInterval(...).unref is not a function
And
Timeout - Async callback was not invoked within the 5000ms timeout specified by jest.setTimeout.
at mapper (node_modules/jest-jasmine2/build/queue_runner.js:41:52)
I get this ever since I switched from Ava to Jest.
Am I handling my rendering wrong?
unref
The default test environment for Jest is a browser-like environment through jsdom.
unref is a special function provided by Node. It is not implemented in browsers or in jsdom, but it is implemented in the "node" test environment in Jest.
It looks like testing a Nuxt app requires both a Node environment to start a server, and a jsdom environment to test the resulting UI.
This can be done by setting the test environment to "node" and initializing a window using jsdom during the test setup.
timeout
Jest will "wait if you provide an argument to the test function, usually called done". This applies to test functions and setup functions like beforeAll.
Your beforeAll function has an argument done that is never called. Jest will wait until either done is called or the timeout configured with jest.setTimeout expires (defaults to 5 seconds).
You are using an async function and are using await on what looks to be the asynchronous part of the function so you don't need done. Change your beforeAll function to not take any parameters and that will prevent Jest from waiting for done to be called.
In my tests starting the Nuxt server takes quite a while so you can pass a timeout value as an additional parameter to beforeAll to increase the timeout for just that function.
Here is an updated test with these changes:
/**
* #jest-environment node
*/
// TODO: Set the environment to "node" in the Jest config and remove this docblock
// TODO: Move this to a setup file
const { JSDOM } = require('jsdom');
const { window } = new JSDOM(); // initialize window using jsdom
const resolve = require('path').resolve;
const { Nuxt, Builder } = require('nuxt');
// Nuxt instance
let nuxt = null;
// Our page to test
let homePage = null;
beforeAll(async () => {
// Configuration
const rootDir = resolve(__dirname, '../..');
let config = {};
config = require(resolve(rootDir, 'nuxt.config.js'));
config.rootDir = rootDir; // project folder
config.env.isDev = false; // dev build
config.mode = 'universal'; // Isomorphic application
nuxt = new Nuxt(config);
await new Builder(nuxt).build();
nuxt.listen(3001, 'localhost');
homePage = await nuxt.renderAndGetWindow('http://localhost:3001/');
}, 20000); // Give the beforeAll function a large timeout
afterAll(() => {
nuxt.close();
});
describe('homepage', () => {
it('should do something', () => {
});
});
Background
I was thrown on this project to help alleviate some stress. The trouble is no one else has done this either so I'm pioneering the cause.
What I know
I can get lambda function output locally with:
serverless invoke local -f getArticlesById -p localfile.json -s dev
and it returns a JSON article as expected.
Question
I'm using Jasmine to test my javascript lambda functions. How can I unit test these serverless environment functions locally?
Current Attempt
My lambda function is in articles/articleHandler.js. I have a test/articles.js that runs jasmine tests leveraging lambda-tester functions. Whenever I run one of these tests I get the error
TypeError: Invalid hosts config. Expected a URL, an array of urls, a host config object, or an array of host config objects.
at new Transport (/Users/Jackson/Sites/serverless-content/node_modules/elasticsearch/src/lib/transport.js:59:13)
at new EsApiClient (/Users/Jackson/Sites/serverless-content/node_modules/elasticsearch/src/lib/client.js:57:22)
at Function.Client (/Users/Jackson/Sites/serverless-content/node_modules/elasticsearch/src/lib/client.js:101:10)
at Object.<anonymous> (/Users/Jackson/Sites/serverless-content-distribution-api-v2/elasticSearch.js:6:42)
at Module._compile (module.js:635:30)
at Object.Module._extensions..js (module.js:646:10)
at Module.load (module.js:554:32)
at tryModuleLoad (module.js:497:12)
at Function.Module._load (module.js:489:3)
at Module.require (module.js:579:17)
I've found that this is caused by including the lambda function into the test. When I comment out that line I don't get the error. I'm guessing that because this is not a serverless call, Elasticsearch knows nothing of my environment.
test/article.js
console.log("testing articles")
const LambdaTester = require("lambda-tester");
const articleHandler = require("../articles/articleHandler.js");
describe("articles getID()", function() {
it("test success", function() {
return LambdaTester(articleHandler.getID)
.event({pathParameters:{id:5633415102001}})
.expectResult(result => {
expect(result.body.data.id).to.be(5633415102001)
});
});
})
describe("articles getList()", function() {
it("test success", function() {
return LambdaTester(articleHandler.getList)
.event()
.expectResult(reset => {
expect(result.body.data.length).to.be(10);
});
});
});
** ADDITIONAL **
It's looking like lambda-tester is supposed to alleviate the problem I'm encountering. Will find out more today.
Use lamba-tester, there are examples on the github page.
I wrote a simple lambda test function and then tested the output with jasmine + lambda-tester
As for my code, I'll need to refactor the handler someone else wrote before it will work. My simple test looks like:
Serverless yml
testLambda:
handler: test/testLambda.getValueOfA
role: arn:aws:iam::367839381035:role/CodeStarWorker-fx-srch-api-v1-Lambda
events:
- http:
path: test/testLambda/{a}
method: get
Lambda Function
module.exports.getValueOfA = (event, context, callback) => {
let a = 2;
if(event
&& event.pathParameters
&& !isNaN(event.pathParameters.a)
) a = event.pathParameters.a;
a = a+a;
let ret = "the value of a is " + a;
callback(null, ret);
}
Test
const LambdaTester = require("lambda-tester");
const TestLambda = require("./testLambda.js");
describe("testLambda()", function() {
it("test success", function() {
let ret;
LambdaTester(TestLambda.getValueOfA)
.event()
.expectResult(result => {
console.log(result);
expect(result).toEqual("the value of a is 4");
});
});
});
I was going to set this up for parameters but didn't get there. Granted this is enough to get anyone moving forward.
another option you have is to call the function directly from your test. In the end, it's nothing more than a function, so you can import the module and call it, passing the right parameters.
I built a yeoman generator that grabs data from a rest api and populates html, css, and js files in an app dir with those files. I am trying to learn how to build the respective mocha test but keep running into an error on my file exist test.
I pass the "does the generator load" test, but when i try the "do files exist" test, I get an error at the console that:
1)creates files creates expected files:
Uncaught TypeError: cb is not a function
at null.<anonymous> (node_modules/yeoman-generator/lib/base.js:400:5)
at Queue.<anonymous> (node_modules/grouped-queue/lib/queue.js:68:12)
My test is as follows
/*global describe, beforeEach, it*/
'use strict';
var path = require('path');
var helpers = require('yeoman-generator').test;
describe("creates files", function(){
beforeEach(function (done){
helpers.testDirectory(path.join(__dirname, 'temp'), function(err){
if(err){
return done(err);
}
this.app = helpers.createGenerator('snow:app',['../../app']);
done();
}.bind(this));
});
it("creates expected files", function(done){
helpers.mockPrompt(this.app,{
hostname : "empasiegel1",
username : "password",
password : "password",
solution : "testSnow"
});
this.app.run({}, function() {
helpers.assertFile('package.json');
done();
});
});
});
Could it have soemthing to do with the asynchronous nature of the rest calls?
The way to start locomotive.js 0.3.x no longer works in 0.4.x, as the signature of app.boot is different. I have:
before(function(done) {
var self = this;
var lcm = new locomotive.Locomotive();
lcm.boot('test', function() {
self.app = lcm.express;
self.app.listen(4000, done);
});
});
It throws "Error: connect ECONNREFUSED" when I tried to connect with supertest:
request(this.app)
.post('problems/' + problemId + '/solution_ratings')
.set('Content-Type', 'application/json')
.send({access_token: playerId, solution_group_id: 1, rate: 4})
.expect(200, done);
What is the proper way to start a locomotive server for functional testing?
[Update]
I have to start the server in the same process as the test in order to use sinon.js to stub/spy calls to models.
I've answered this on Locomotive's github for an issue, however I wanted to share here to get some feedback, as in better ways, cleaner, or any other input you might have.
I require multiple servers in an OAuth2 environment (auth, resource, dashboard..) of which mostly are Locomotive framework based.
I like functional tests since they best represent OAuth2 based authentication as opposed to only focusing my tests on a resource service where the authentication token might not best represent the user.
Here is the setup I've come up with for starting up the locomotive servers:
For your tests, say like in test/util.severs.js
var fork = require('child_process').fork,
async = require("async");
var authApp;
var serverStatus = [];
var start = function(done) {
authApp = fork( __dirname + '/../../authorization/server.js');
serverStatus.push(function(callback){
authApp.on('message', function(m){
//console.log('[AUTHORIZATION] SENT MESSAGE: ', m);
if (m.status == 'listening') return callback();
});
});
// add others servers if you swing that way!
async.parallel(serverStatus, function(){
done();
});
}
var stop = function(done) {
authApp.kill();
done();
}
module.exports = {
start: start,
stop: stop
};
Note that I'm using async here since I'm working with multiple servers (Environment is in OAuth2 which requires many servers to startup (IE resource, dashboard...)), ignore async if all you'll ever have is one server.
require the above file in your Mocha tests and do
servers = require('./util/servers');
...
before(servers.start);
// tests away!!!
after(servers.stop);
Then in each of my locomotive-project/server.js I do the following...
// if being started as a parent process, send() won't exist, simply define
// it as console.log or what ever other thing you do with logging stuff.
if (process.send === undefined){
process.send = function(msg){
console.log("status: ", msg.status);
};
}
process.send({status: 'starting'});
...
app.boot(function(err) {
if (err) {
process.send({status: 'error', message: err});
return process.exit(-1);
} else {
// notify any parents (fork()) that we are ready to start processing requests
process.send({status: 'listening'});
}
});
Using Locomotive 0.4.x here, could be different based on your version.
Is this the best way to go? Ideally I would like to move this over to Grunt, that is test server initialization (which is quite hefty as we build lots of data into the test DB), and then functional tests could run. So wondering if anyone has delved into grunt to do this instead of a before() task in Mocha.