I have a requirement to verify that certain actions are being logged to the server and would like to know why I'm having trouble stubbing the call to hapi server.log().
In my unit tests I instantiate a hapi server using:
const Hapi = require('hapi');
const server = new Hapi.Server();
I have my stub setup as:
const Sinon = require('sinon');
const logStub = Sinon.stub(server, 'log');
I am using server.inject to call the endpoint that I expect to call server.log. When I debug the test, I have a breakpoint set to verify that server.log is being called in my endpoint and I have a breakpoint in my unit test to verify that the logStub is being called. However, when I call this:
server.inject(localRequest).then(() => {
const spyCall = logStub.getCall(0);
})
spyCall is null and if I inspect logStub, called is false and the callCount is 0, despite server.log being called in my endpoint.
What am I missing here? Does calling server.inject cause an issue with stubbing methods on the hapi server?
The best solution I have been able to find is to use the server.once method on the hapi server.
I neglected to mention in my question that I'm using hapi version 16.6.2 so this solution may not work for the latest version of hapi as it is much different.
server.once({
name: 'log',
filter: 'myTag',
}, (update) => {
Code.expect(update.data).to.equal('expectedMessage');
});
Using this approach allowed me to specify the event 'log' and the specific tag (or tags) for my log event. Hope this helps someone else!
Related
I am building a Node JS application using Express.js framework. My application is sending email using the nodemailer package, https://nodemailer.com/about/. I am writing test using supertest and Jest. I am using nodemailer-mock package, https://www.npmjs.com/package/nodemailer-mock to mock my application.
I can mock the logic for sending email for Jest as mentioned in the nodemailer-mock documentation. The only issue is that it is not resetting the data/ count after each test. For example, I have two tests and both are sending emails and when I do the following assertion, it is failing.
const sentEmails = mock.getSentMail();
// there should be one
expect(sentEmails.length).toBe(1);
It is failing because an email was sent in the previous test. How can I reset it?
Assuming your mock variable is set (or even imported) at the module level, this should cover it:
afterEach(() => {
mock.reset();
});
Keep in mind that this method also seems to reset anything else you have configured on the mock (success response, fail response, etc.), so you may need to perform that setup each time too. That would be a good fit for a beforeEach block.
I'm using amqplib-package for connecting to RabbitMQ in nodeJS and want to know
how I can check/verify my RabbitMQ-connection using Mocha and Chai in nodeJS?
You can try one of the following:
you can create a valid rabbit-mq connection using a docker iamage docker-rabbitmq and make real requests to your application but this is not unit testing, but more like integration testing.
You can mock it using sinon a very good article I can suggest on this topic is Best practices for spies, stubs, and mocks in sinonjs
You can follow this topic on google groups NodeJS + RabbitMQ (amqplib): How to write good Unit Test against them.
If you are using ampqlib package and you want to test whether the connection is established or not then it is not considered as a unit test, we can call it an integration test.
Other answers have good content about integration testing, but for unit testing you can use amqplib-mocks (https://www.npmjs.com/package/amqplib-mocks) which is a simple mocking framework for amqplib.
For a basic unit test you can check if amqp.connect(url) is called and expect the URL you want.
I can also show a basic example how you can get a stubbed connection object:
(ps: This is not a running code, this is just for reference)
subscriber.js
const amqp = require('amqplib');
class Subscriber {
async getConnection(url) {
const connection = await amqp.connect(url);
return connection;
}
}
Subscriber class has a method getConnection which establishes and return a connection object with rabbitmq.
subscriber.test.js
const amqpMock = require('amqplib-mocks');
const sinon = require('sinon');
const mockConnection = await amqpMock.connect('mockurl.com');
const getConnectionStub = sinon.stub(Subscriber.prototype, 'getConnection').returns(mockConnection);
In above code you can either stub ampq.connect directly or stub getConnection method and return a connection object as returned by amqp-mock's connect method.
To know more about sinon used above, click.
I am using xero as my accounting software. I have one requirement that part of my application need to be integrated with xero to perform automation. Using the nodejs sdk seems so easy, but the fact is i cannot connect to xero even using the simplest example. Here is the code:
const xero = require('xero-node');
const config = {
"userAgent": "Firefox",
"consumerKey": "<MY_CONSUMER_KEY>",
"consumerSecret": "<MY_CONSUMER_SECRET>",
"privateKeyPath": "./privatekey.pem"
};
const xeroClient = new xero.PrivateApplication(config);
xeroClient.core.contacts.getContacts()
.then(contacts => {
console.log(contacts);
}).catch(err => {
console.log(err);
});
The code does nothing and prints no error. Anyone ever deal with this problem?
The most likely reason is that your privatekey is invalid. If you put these lines(https://github.com/XeroAPI/xero-node/pull/169/files) into your module then it will check it first.
You could also copy a few of those lines and validate your privateKey.
At the moment the SDK swallows the exception when the key is invalid.
Also, please make sure you are running server side - not browser side.
Solved. I need to add following code:
if (config.privateKeyPath && !config.privateKey)
config.privateKey = fs.readFileSync(config.privateKeyPath);
I am having trouble implementing the isRequestFromAssistant method to verify requests to my fulfillment webhook. Using Node.js, I instantiate the following variables at the start of my index.js file:
const App = require('actions-on-google').ApiAiApp;
const app = new App({ request, response });
I then use "app" with the .ask and .tell and other methods throughout my functions.
The code I see in the docs for implementing isRequestFromAssistant is:
const app = new ActionsSdkApp({request, response});
app.isRequestFromAssistant('my-project-id')
.then(() => {
app.ask('Hey there, thanks for stopping by!');
})
.catch(err => {
response.status(400).send();
});
If I leave out the first line and use my existing app variable, created with the .ApiAi method instead of the .ActionsSdkApp method, it doesn't work. If I create a new variable App1 and app1 using the .ActionsSdkApp method and change the above code to be app1.isRequestFromAssistant, it also doesn't work. I have tried other variations with no luck.
When I say it doesn't work, I mean I receive a 500 Internal Server Error when I call it. I am hosting it with NGROK currently. I am still a beginner with Node.js, although I have managed to get the other 700 lines of code working just fine, learning mostly from Google searches and reading these forums.
You have a few things going on here which, individually or separately, may be causing the problem.
First - make sure you have the most recent version of the actions-on-google library. The isRequestFromAssistant() function was added in version 1.6.0, I believe.
Second - Make sure you're creating the right kind of App instance. If you're using Dialogflow (formerly API.AI), you should be creating it with something like
const App = require('actions-on-google').DialogflowApp;
const app = new App( {request, response} );
or
const { DialogflowApp } = require('actions-on-google');
const app = new DialogflowApp( {request, response} );
(They both do the same thing, but you'll see both forms in documentation.) You should switch to DialogflowApp from ApiAiApp (which your example uses) to reflect the new name, but the old form has been retained.
If you're using the Actions SDK directly (not using Dialogflow / API.AI), then you should be using the ActionsSdkApp object, something like
const { ActionsSdkApp } = require('actions-on-google');
const app = new ActionsSdkApp({request: request, response: response});
(Again, you'll see variants on this, but they're all fundamentally the same.)
Third - Make sure you're using the right function that matches the object you're using. The isRequestFromAssistant() function is only if you are using the Actions SDK.
If you are using Dialogflow, the corresponding function is isRequestFromDialogflow(). The parameters are different, however, since it requires you to set confirmation information as part of your Dialogflow configuration.
Finally - If you're getting a 500 error, then check your logs (or the output from stderr) for the node.js server that is running. Typically there will be an error message there that points you in the right direction. If not - posting that error message as part of your StackOverflow question is always helpful.
Set the secure (randomly generated) auth header & key values in the dialogflow Fulfillment page, then in nodejs:
if (app.isRequestFromDialogflow("replace_with_key", "replace_with_value")) {
console.log("Request came from dialogflow!");
// rest of bot
} else {
console.log("Request did not come from dialogflow!");
response.status(400).send();
}
Also see: https://developers.google.com/actions/reference/nodejs/DialogflowApp#isRequestFromDialogflow
I have written a NodeJS app using express that proxies some calls to external APIs. So I am trying to write a unit test using Mocha and Sinon. My goal is to test the app without any internet connectivity so I am trying to mock the https requests and return mock replies.
I'm having a problem that I can't find any examples or tutorials that fit my case. My node app listens on port 8081 for http requests and then proxies them to another site. I want to test my app without it having to actually send the request to those external servers. I'm trying it below and I put the json replies I want to send back in the server.respondsWith() function.
Am I doing this the right way by making an ajax call with chai? or should I be sending the requests inside my app somehow. Any help is appreciated.
var assert = require('assert');
var chai = require('chai');
var spies = require('chai-spies');
var chaiHttp = require('chai-http');
var https = require('https');
var should = chai.should();
var expect = chai.expect;
var sinon = require('sinon');
chai.use(spies);
chai.use(chaiHttp);
describe('Car Repository', function() {
var server;
before(function() {
server = sinon.fakeServer.create();
});
after(function() {
server.restore();
});
var url = 'http://127.0.0.1:8081';
it('should succeed and return a list of cars', function(done) {
server.respondWith('POST', 'https://api.sandbox.cars.com/v2/token_endpoint', JSON.stringify({"access_token":"1t3E4IykfpJAbuFsdfM2oFAo5raB5vhfOV0hAYe","token_type":"bearer","expires_in":604800}));
server.respondWith('GET', url+'/cars', JSON.stringify({'test':'this works'}));
chai.request(url)
.get('/cars')
.end(function(err, res) {
if (err) {
throw err;
}
res.should.have.status(200);
res.body.should.have.property('test');
console.log(res.body);
done();
});
});
});
Check out the Nock library. It does exactly what you're looking for.
Nock is an HTTP mocking and expectations library for Node.js
Nock can be used to test modules that perform HTTP requests in isolation.
For instance, if a module performs HTTP requests to a CouchDB server or makes HTTP requests to the Amazon API, you can test that module in isolation.
The new solution here is sinon's fake server:
http://sinonjs.org/releases/v2.1.0/fake-xhr-and-server/#fake-server
Take a look at node-tdd and the useNock flag. It builds on top of mocha and nock (mentioned in the accepted answer) and automatically creates and uses a recording file for each test.
We love that it's so easy to use. Basically just "enable and forget" and focus on writing requests / test cases. If requests for a test change, one still needs to delete or adjust the recording file, but at least it's entirely separate from the code.