Creating request stub with sinon in mocha - node.js

I'm using mocha to test some classes and I need to create a stub of request library.
I'm using sinon, and I'm able to create a stub of the request.get method but I'm not able to create a stub of the request method (the http calls try to connect to a server). As I have read, request.get is an alias for request but when I stub request.get it has no effect over request calls.
This code works (using request.get):
In tests:
request = require 'request'
describe "User test", ->
user = {}
before (done) ->
user = new test.user('Ander', 18)
sinon.stub(request, 'get').yields(null, {statusCode: 200}, 'foo')
done()
after (done) ->
request.get.restore()
done()
it "testing server response", ->
user.getData().should.equal 'ander'
In source:
request = require 'request'
class User
contructor(#name, #age): ->
getData: ->
mydata = ''
request.get 'http://127.0.0.1:8080/', (err, response, body) ->
if not err and response.statusCode == 200
mydata = body
else
err = throw new Error "Errorea"
mydata
But this doesn't work (tries to connect to the supplied url):
In tests:
request = require 'request'
describe "User test", ->
user = {}
before (done) ->
user = new test.user('Ander', 18)
sinon.stub(request, 'Request').yields(null, {statusCode: 200}, 'foo')
#Creating the stub this way, doesn't work neither
#sinon.stub(request, 'get').yields(null, {statusCode: 200}, 'foo')
done()
after (done) ->
request.Request.restore()
done()
it "testing server response", ->
user.getData().should.equal 'ander'
In source:
request = require 'request'
class User
contructor(#name, #age): ->
getData: ->
mydata = ''
request 'http://127.0.0.1:8080/', (err, response, body) ->
if not err and response.statusCode == 200
mydata = body
else
err = throw new Error "Errorea"
mydata
Which is the right way to create a stub for request call? Which is the method to be stubed?

Although request is a great library, it is not a good example of well structured API. And because module request is defined as a function with additional methods (similarly like express), as what I know you can't create stub for function request with sinon.
The best thing you can do is to avoid to use request function in your code and use only request.get, request.post, etc., which you can easily stub.
Creating stub for Request in your second example doesn't help because Request is not a method, see source code.

If anyone is still looking for an answer for this, it looks like you can create a stub for request using sinon:
before(function(done){
sinon
.stub(request, 'get')
.yields(null, null, JSON.stringify({login: "bulkan"}));
done();
});
more details can be found here

Another workaround would be generating a stub using sinon module and the request dependency in the corresponding module can be overridden using proxyquire.
var sinon = require('sinon');
var proxyquire = require('proxyquire');
describe('something', function(){
var request;
var overriddenModule;
before(function(){
request = sinon.stub();
// overriding the 'request' dependency with the stub
overriddenModule = proxyquire('path_to_module_using_request', {'request': request});
});
it("should do something",function(done){
// stubbing the request(options,callback) method
request.withArgs(sinon.match.any,sinon.match.any).yields(null,null,responseBody);
overriddenModule.method_which_is_doing_request;
// our logic and assertions before calling done()
});
});
For more info, check this article on Unit Testing with Mocks in Node

As mentioned in one of the answers to the question How to mock request and response in nodejs to test middleware/controllers?, the node-mocks-http package provides a way to build request and response mocks.

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.

How to use class variables in SailsJS?

In my sailsJS project i use a Service as an API wrapper to communicate with an external API.
For example I want to have a init function and pass parameters like baseUrl, apiKey and after that just call sendRequest(endpoint).
However today i figured out that a service cannot be instantiated but is available globally. Because SailsJS works async it just overrides the variables each time.
So my question in short: What's the right way to use a "class" with class variables, where i can have multiple instances, like i know them from the PHP world?
At first i want to instantiate my API wrapper with a project, that contains basic data such as baseUrl and apiKey.
const ApiService = ServiceBase.getApiService(project);
ApiService.getEndpoint(endpoint).then(async(response) => {
// handle response
}
Function get ApiService.
Here i try to set project variables.
getApiService: (project) => {
this.setConfigParam('apiKey', project.api_key);
this.setConfigParam('baseUrl', project.url);
return ShopApiService;
}
Function getEndpoint (Shortcut for GET Requests)
getEndpoint: async function(endpoint) {
if (ShopApiService.isTokenExpired()) {
await this.authorizeRequest();
}
return this.sendRequest(endpoint);
}
authorizeRequest (Sets accessToken for following requests)
authorizeRequest: async function() {
let response = await this.postRequest('auth/login', {apiKey: module.exports.config.apiKey});
if (response && response.statusCode == 200) {
module.exports.token.updated_at = Date.now();
module.exports.config.accessToken = response.body.accessToken;
return true;
}
return false;
}
My problem is that the asynchronous calls override variables like accessToken or baseUrl, so if the request to acquire the access token is finished - it may be for a different request.

Sinon fake server not intercepting requests

Trying to use Sinon for the first time because of its fake server functionality that lets me stub an API response. Test itself is written for Mocha
However, the fake server doesn't seem to be intercepting the requests.
Code:
describe('when integrated', function() {
var server;
beforeEach(function() {
server = sinon.createFakeServer();
});
afterEach(function() {
server.restore();
});
it('can send a message to the notification service', function() {
server.respondWith("POST", new RegExp('.*/api/notificationmanager/messages.*'),
[200,
{ "Content-Type": "application/json" },
'{ "messageId":23561}'
]);
var messageOnly = new PushMessage(initMessageObj);
var originalUrl = PushMessage.serverUrl;
messageOnly.setServerAPI("http://a.fake.server/api/notificationmanager/messages");
console.log("fake server is: ", server);
messageOnly.notify()
.then(function(response) {
messageOnly.setServerAPI(originalUrl);
return response;
})
.then(function(response) {
response.should.be.above(0);
})
console.log(server.requests);
server.respond();
})
});
For reference, PushMessage is an object that has a static property serverUrl. I'm just setting the value to a fake URL & then resetting it.
The notify() function makes a post message using request-promise-native to the serverUrl set in the PushMessage's static property.
What seems to be happening, is that the POST request ends up being properly attempted against the URL of http://a.fake.server/api/notificationmanager/messages, resulting in an error that the address doesn't exist...
Any idea what I'm doing wrong...? Thanks!
There have been several issues on the Sinon GitHub repository about this. Sinon's fake server:
Provides a fake implementation of XMLHttpRequest and provides several interfaces for manipulating objects created by it.
Also fakes native XMLHttpRequest and ActiveXObject (when available, and only for XMLHTTP progids). Helps with testing requests made with XHR.
Node doesn't use XHR requests, so Sinon doesn't work for this use case. I wish it did too.
Here's an issue that breaks it down: https://github.com/sinonjs/sinon/issues/1049
Nock is a good alternative that works with Node: https://www.npmjs.com/package/nock

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);
}
});

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