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.
Related
:) I chose for automated testing a tool Cypress.io.
I need some tests for my sitemap.xml document and I dont know how to do that :(
I have tried install an npm package libxmljs
npm install libxmljs --save
and load it as plugin in cypress/plugins/index.js
const libxmljs = require('libxmljs');
But there is a problem with this. It shows an error
The plugins file is missing or invalid.
Your pluginsFile is set to /home/my-app/cypress/plugins/index.js, but
either the file is missing,
it contains a syntax error, or threw an error when required.
The pluginsFile must be a .js or .coffee file.
Please fix this, or set pluginsFile to false if a plugins file is not
necessary for your project.
Error: The module '/home/my-app/node_modules/libxmljs/build/Release/xmljs.node'
Please help me, how can I use libxmljs in Cypress.io or how i should write tests for Sitemap.xml in this end-to-end testing tool.
Thanks for your time! :)
Although #NoriSte's answer is correct, I found a simpler alternative without the need for any 3rd party code.
Cypress API exposes all the necessary methods to:
load a file (the sitemap.xml in your case): cy.request.
parse XML file (it exposes the jQuery API): Cypress.$
check if a page successfully loads (with a 200 status code): cy.visit
This is the following test that I use to test if all of the pages declared in the sitemap are loading (and make sure it doesn't point to any 404):
describe('Sitemap', () => {
// initialize the url array
let urls = []
// be sure to get the url list before executing any tests
before(async () => {
// getch the sitemap content
const response = await cy.request('sitemap.xml')
// convert sitemap xml body to an array of urls
urls = Cypress.$(response.body)
// according to the sitemap.xml spec,
// the url value should reside in a <loc /> node
// https://www.google.com/sitemaps/protocol.html
.find('loc')
// map to a js array
.toArray()
// get the text of the <loc /> node
.map(el => el.innerText)
})
it('should succesfully load each url in the sitemap', () => {
urls.forEach(cy.visit)
})
})
If you want to use libxmljs to parse your sitemap you should
read the sitemap itself with cy.request
add a custom task to Cypress (because libxmljs is a node library, cy.task is the only way to consume Node.js scripts from your Cypress tests)
returns the parsed data from your task
assert about it in a Cypress test
Those are the high-level steps you need to do 😉
To add to a great answer by gion_13, here’s his solution refactored to utilize Cypress promise-like-commands instead of async calls.
describe('Sitemap', () => {
let urls = [];
before(() => {
cy.request('sitemap.xml')
.as('sitemap')
.then((response) => {
urls = Cypress.$(response.body)
.find('loc')
.toArray()
.map(el => el.innerText);
});
});
it('should succesfully load each url in the sitemap', () => {
urls.forEach(cy.visit);
});
});
Using async in Cypress may raise error ‘Cypress detected that you returned a promise in a test, but also invoked one or more cy commands inside of that promise’.
describe('Sitemap', () => {
let urls = [];
before(() => {
const parser = new DOMParser();
cy.request('/sitemap.xml').then((response) => {
const document = parser.parseFromString(response.body, 'application/xml');
const parsedUrls = document.getElementsByTagName('loc');
urls = Array.from(parsedUrls).map((item) => item.innerHTML);
});
});
it('Should load each url from the sitemap', () => {
urls.forEach(cy.visit);
});
});
I am trying to cover redux-saga that gets data from RxDB with Jest tests.
export function* checkUnsavedData(action) {
const { tab } = action;
try {
const db = yield getDB().catch(e => {
throw new Error(e);
});
const currentUser = yield select(makeSelectCurrentUser());
const unsavedData = yield db[USER_COLLECTION].findOne(currentUser)
.exec()
.then(data => data && data.unsavedData)
.catch(e => {
throw new Error(e);
});
} catch (error) {
yield showError(error);
}
}
Everything is fine in live run. But testing the generator I get:
UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 2): Error: Error: RxError:
RxDatabase.create(): Adapter not added. Use RxDB.plugin(require('pouchdb-adapter-[adaptername]');
Given parameters: {
adapter:"idb"}
If anyone has done this, please, tell me how to test such cases with RxDB in redux-saga with Jest.
It looks like you did non add the adapter to RxDB. Can you paste the code where you create the database? This would help more in finding the error.
When running tests, you should not use the idb-adapter. Use the in-memory-adapter, it's faster and also you can be sure that you start on a clean state on each testrun.
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?
I have recently started writting unit tests using Karma + Karma-jasmine but I am having problems with the following tests:
describe("WEBSERVICE:", function () {
var webservice,
$httpBackend,
authRequestHandler,
webserviceURL = "http://localhost:8006/";
beforeEach(inject(function (Webservice, $injector) {
webservice = Webservice;
$httpBackend = $injector.get("$httpBackend");
authRequestHandler = $httpBackend
.when("GET", webserviceURL + "users/login")
.respond(200, "ok");
}));
afterEach(function() {
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
});
it("should EXISTS", function () {
expect(webservice).toBeDefined();
});
it("should throw a WebserviceError if we are not logged in" , function () {
expect(function () {
webservice.item("negs", "RPT");
}).toThrow(webserviceAuthenticationError);
});
it("should NOT HAVE credentials when instantiated", function () {
expect(webservice.hasCredentials()).toBeFalsy();
});
it("should log in when valid credentials are given", function () {
$httpBackend.expectGET("users/login");
webservice.withCredentials("sam", "password");
});
});
It appears to be the following which creates the problem since all tests pass when I remove it:
afterEach(function() {
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
});
I was just wondering if anyone could help me with this.
Thanks a lot.
The reason you having problems is with
$httpBackend.verifyNoOutstandingExpectation();
is due to your last test
it("should log in when valid credentials are given", function () {
$httpBackend.expectGET("users/login");
webservice.withCredentials("sam", "password");
});
having unsatisfied requests which you can see in this jsfiddle
Error: Unsatisfied requests: GET users/login
If you comment out
$httpBackend.verifyNoOutstandingExpectation()
your first three tests pass but the last one is amber as there is no expectations, see this fiddle.
WEBSERVICE:
should EXISTS
should throw a WebserviceError if we are not logged in
should NOT HAVE credentials when instantiated
SPEC HAS NO EXPECTATIONS should log in when valid credentials are given
In the AngularJS documentation it says
verifyNoOutstandingExpectation();
Verifies that all of the requests defined via the expect api were made. If any of the requests were not made, verifyNoOutstandingExpectation throws an exception.
You will need to restructure that test so that
webservice.withCredentials("sam", "password");
makes a request through $httpBackend
I'm using intern with node.js, trying to set up intern for ajax testing. The server code below serves direct GET requests, but XHR request by Intern doesn't seem to reach to it. I suspect the problem is something to do with proxyUrl setup for the Node.
server/main.js: Node/express
...
app.get('/data', function(request, response, next){
if(request.xhr)
{
var data = dfs.readFileSync("src/server/data.json");
response.render(data, {
root: root,
error: new Error('Cant read file')
});
}
else
next();
});
...
app.listen(8001);
http_proxy.createServer(function(req,res, proxy){
proxy.proxyRequest(req,res, {host:'localhost', port:8001});
}).listen(9000);
intern.hello.js: (unit test code)
...
'async test': function () {
var dfd = this.async(1000);
request('/data').then(dfd.callback(function (data) {
assert.strictEqual(data, 'hello world');
}, dfd.reject.bind(dfd)));
}
intern config:
...
proxyPort:9000,
proxyUrl: 'http://localhost:8001/',
...
intern error:
Warning: FAIL: async test (1015ms)
Error: Timeout reached on main - wait - async test
at Error (<anonymous>)
at new ErrorCtor (.../node_modules/intern/node_modules/dojo/errors/create.js:13:21)
at null._onTimeout (.../node_modules/intern/lib/Test.js:164:21)
at Timer.listOnTimeout [as ontimeout] (timers.js:110:15)
2/3 tests passed
2/3 tests passed Use --force to continue.