Hapi JS and Jest mocking a customer module is not working - jestjs

I am building a web API using Hapi JS. I am also writing integration tests for my endpoint. In my tests, I am trying mock a function of a service class. This is the scenario I am trying to test.
In the beginning of controller, I am importing a customer module/ file like this:
import * as userService from '../services/userService';
Then, inside the controller's handler/ action method, it is calling a function of userService as follows:
userService.updateAddress(data);
Then inside the userService.js, I am importing another module called, notificationService.js as follows:
import * as notificationService from './notificationService'
export const updateAddress = (data) => {
// update the address
notificationService.notifyAddressUpdated(data);
}
Now, I am trying to write a test for when the endpoint for updating the address is invoked, it will update the address and send the notification. For "sending the notification" part, I am trying to mock the notifyAddressUpdated function.
This is my test:
import * as notificationService from '../../src/services/notificationService';
describe('User Controller Test', function() {
test(`should send notification when the user's address is updated`, async () => {
const notificationServiceSpy = jest.spyOn(notificationService, 'notifyAddressUpdated');
notificationServiceSpy.mockImplementationOnce((data) => {
console.log('Mock function is called');
})
// this will call server.inject function. This is basically the same as supertest request
makeUpdateAddressRequest()
})
});
As you can see in my code, I am trying to mock the notifyAddressUpdated function of notificationService. But it is not invoking the mock implementation when I run the test. It is still calling the actual implementation of the function. What is wrong with my code and how can I fix it?

Related

Trying to spyOn a response inside a fire-and-forget method with jest

I'm working on a project in node.js where I'm setting up contract testing
So I want to test my API requests and response
here is the method I want to test
import rp from 'request-promise';
const foo = async () : Promise<void> => {
await rp(options).then(resp => {
return resp;
})
}
here in my test I want to check the resp (which will be provided by the pact when running the test)
The problem is I do not know how I can capture this variable to check it...
I've tried to use spies with no success and a mock won't help...
import requestPromise from "request-promise";
const response = jest.spyOn(requestPromise(options),"then")
await foo()
expect(response).toReturnWith({test:"test"})
I also tried to spyOn requestPromise() "post"
but no results
Is there a way to test this ?
It's not clear what you're trying to attempt here. You mention Pact but there is no use of it in your example.
Why do you need to spy on the request at all? Just return something from foo itself (instead of void) and assert on the response.
If you're using Pact, it will do all of the http request checks for you. And ensure the request was made.
You then just assert on the response.

Middleware Pipelining in Nodejs

I am writing a backend service that makes API requests to a public API endpoint. The response from the API is sent as a JSON. I am using request.js for making the API calls and, am returning the instance, request.Request to any code (in this case, a route handler in Express.js). The route handler is simply "piping" the response from the API call back to client who, requested the route.
I have the following concerns regarding the above scenario:
What's the best way to implement business logic that implements the Stream interface so, I can directly pass the API returned value to the middleware function (basically, invoke pipe method on the return value and, pass the middleware for various logic before streaming it to the client)?
I know that the Express.Response instance passed to every route handler in Express is consumed only once and, must be duplicated if they are to passed as arguments to other functions. Is this approach better in comparison to the method described in (1)?
To avoid cluttering up the discussion, I am providing a snippet of the code that I am working on (the code has no syntax errors, runs properly too):
APIConnector.ts:
import * as Request from 'request';
export class APIConnector{
// All the methods and properties pertaining to API connection
getValueFromEndpoint(): Request.Request{
let uri = 'http://someendpoint.domain.com/public?arg1=val1';
return Request.get(uri);
}
// Rest of the class
}
App.ts
import * as API from './APIConnector';
import * as E from 'express';
const app = E();
const api = new API();
app.route('/myendpoint)
.get((req: E.Request, res: E.Response) => {
api.getValueFromEndpoint().pipe(res);
// before .pipe(res), I want to pipe to my middleware
});
One of the pattern encouraged by express is to use the middleware as a decorator of the request object, in your case you would be adding the api connector to the request via a middleware before using it in the route.
app.js
import * as apiConnectorMiddleware from './middleware/api-connector';
import * as getRoute from './routes/get-route'
import * as E from 'express';
app.use(apiConnectorMiddleware);
app.get('/myendpoint', getRoute);
middleware/api-connector
import * as request from 'request-promise'
(req, res, next) => {
req.api = request.get('http://someendpoint.domain.com/public?
arg1=val1');
next();
}
routes/get-route
(req, res) => req.api
.then(value => res.send(value))
.catch(res.status(500).send(error))

How to stub SOAP client request with sinon in Node JS?

I am using strong-soap module to get data from SOAP request.
var soap = require('strong-soap').soap;
soap.createClient(url, options, function (err, client) {
var method = client.GetInfoSOAP;
method(requestQuery, function (err, info) {
// bla bla
}
}
I am getting the required data. Now
I want to write unit test case to mock the SOAP request using sinon stub, but didn't get any success. Any help would be appreciated.
What you want is controlling the soap object's createClient. You can do that using techniques that fall into one of two categories:
Dependency injection - expose a setter through which you can inject a fake module you control yourself for testing
Using link seams - hook into the import/require mechanism and override what the module is getting.
The Sinon project has a nice page on using link seams through proxyquire, and I have also detailed how to do DI on the issue tracker.
To achieve the first, all you need is to do something like this in the module:
module.exports._injectSoap = (fake) => soap = fake;
Then in your test code:
const fakeSoap = { createClient : sinon.stub().returns(/* ? */) }
myModule._injectSoap(fakeSoap);
...
assert(fakeSoap.createClient.calledOnce);
assert(...)
Hi i have solved my problem with the following code :
sinon.stub(soap, 'createClient').yields(null, {
GetInfoSOAP: function (request, cb) {
return cb(null, myDesiredData);
}
});

Why we need nock to do http request unit test?

Below is the sample code from redux document
describe('async actions', () => {
afterEach(() => {
nock.cleanAll()
})
it('creates FETCH_TODOS_SUCCESS when fetching todos has been done', () => {
nock('http://example.com/')
.get('/todos')
.reply(200, { body: { todos: ['do something'] }})
const expectedActions = [
{ type: types.FETCH_TODOS_REQUEST },
{ type: types.FETCH_TODOS_SUCCESS, body: { todos: ['do something'] } }
]
const store = mockStore({ todos: [] })
return store.dispatch(actions.fetchTodos())
.then(() => { // return of async actions
expect(store.getActions()).toEqual(expectedActions)
})
})
})
Why we nee to use nock for this unit test?
I did not see any where use the data from nock in this sample code.
Nock is used to mock http requests - if you mock http request it means that your code doesn't perform real http requests to the server.
Nock (and any other http mocking library) overrides native http requests methods so that real http requests will be never sent. It has many benefits - for example you don't have to wait for actual server response because mocked request returns response in no time and of course your test are independent of server. You can focus on testing application code and don't worry about server - even if server doesn't work you test can be run.
You don't have to explictly use data returned by mocked request if you don't need to test it - the main reason of using nock in your code sample is to prevent actual http request to the server that FETCH_TODOS_REQUEST action would normally sent. Besides, even if mocked response data is not explicily used in tests it's probably used in the application code (probably FETCH_TODOS_SUCCESS action expects todos array to be returend) so you have to mock response data so that your application gets data it expects.
If nock wasn't used the test would take much more time because real http request to the server would be sent.
Mainly because in this test we're interested in the actions that get produced by actions.fetchTodos(). This action will make a call to the /todos endpoint, thus returning actions with some data. Since we're just interested in the data contained in the actions, we just mock it.
Nock internally intercepts the real fetch call to /todos and returns a successful 200 code, making it possible for the redux store to continue.
The data you're looking for is
{ todos: ['do something'] }
This is mocked and also expected later on

How to mock external service when testing a NodeJS API

I have JSON API built with koa which I am trying to cover with integration tests.
A simple test would look like this:
describe("GET: /users", function() {
it ("should respond", function (done) {
request(server)
.get('/api/users')
.expect(200, done);
});
});
Now the issue comes when the actions behind a controller - lets say saveUser at POST /users - use external resources. For instance I need to validate the users phone number.
My controller looks like this:
save: async function(ctx, next) {
const userFromRequest = await parse(ctx);
try {
// validate data
await ctx.repo.validate(userFromRequest);
// validate mobile code
await ctx.repo.validateSMSCode(
userFromRequest.mobile_number_verification_token,
userFromRequest.mobile_number.prefix + userFromRequest.mobile_number.number
);
const user = await ctx.repo.create(userFromRequest);
return ctx.data(201, { user });
} catch (e) {
return ctx.error(422, e.message, e.meta);
}
}
I was hoping to be able to mock the ctx.repo on the request object but I can't seem to able to get a hold on it from test, which means that my tests are actually hitting the phone number verification service.
Are there any ways I could go around hitting that verification service ?
Have you considered using a mockup library like https://github.com/mfncooper/mockery?
Typically, when writing tests requiring external services, I mock the service client library module. For example, using mocha:
mockery = require('mockery');
repo = require('your-repo-module');
before(function() {
mockery.enable();
repo.validateSMSCode = function() {...};
mockery.registerMock('your-repo-module', repo);
}
This way, every time you require your-repo-module, the mocked module will be loaded rather than the original one. Until you disable the mock, obviously...
app.context is the prototype from which ctx is created from. You may
add additional properties to ctx by editing app.context. This is
useful for adding properties or methods to ctx to be used across your
entire app, which may be more performant (no middleware) and/or easier
(fewer require()s) at the expense of relying more on ctx, which could
be considered an anti-pattern.
app.context.someProp = "Some Value";
app.use(async (ctx) => {
console.log(ctx.someProp);
});
For your sample your re-define app.context.repo.validateSMSCode like this, assuming that you have following setup lines in your test:
import app from '../app'
import supertest from 'supertest'
app.context.repo.validateSMSCode = async function(ctx, next) {
// Your logic here.
};
const request = supertest.agent(app.listen())
After re-defining app.context.repo.validateSMSCode method that your will define in your test, will work, instead of original method.
https://github.com/koajs/koa/blob/v2.x/docs/api/index.md#appcontext
https://github.com/koajs/koa/issues/652

Resources