So this is the (snipped) code for a chatbot. I want to override the sendMessage() function to just echo the message argument. In this case, the original function runs and gives an error at the 2nd line of the function. Obviously, modules aren't loaded and I don't need them to. This is a test for the eventHandler to echo the right messages. Ideas?
var modules = require('./modules');
console.log('[tose] Loading modules: ', Object.keys(modules));
function eventHandler(channel, type, data, react=()=>{}) {
switch (type) {
case 'new_message':
console.log('[tose][new_message]', channel, 'from:', data.cid, 'message:', data.message);
if (regexTemplates.testSearch.test(data.message.toLowerCase())) {
...
} else {
sendMessage(channel, data.cid, data.message); // Basic echo message
}
break;
}
}
// The function to be stubbed
function sendMessage(channel, cid, message) {
console.log('[tose][send_message]', channel, 'to:', cid, 'message:', message);
coms[channel].sendMessage(cid, message); // Getting error here thus not really stubbed
}
exports.eventHandler = eventHandler;
exports.sendMessage = sendMessage
And the test:
describe('Tose core', function() {
describe('Process messages', function() {
before(function() {
var stub = sinon.stub(tose, 'sendMessage').callsFake(function(channel, cid, message) {
assert.equal(message, 'Test message');
return message
});
});
after(function() {
tose.sendMessage.restore();
});
it('should echo messages', function() {
var data = {message: 'Test message'}
tose.eventHandler('test', 'new_message', data)
assert(tose.sendMessage.calledOnce);
});
});
});
The problem here is that when you use Sinon to stub an object's function, you're stubbing that (and only that) object's function.
Your code (the first code block) is using the local definition of the sendMessage function.
When you stub the tose object (in the second code block), you are changing the sendMessage function thats on the tose object and not the local definition of the function.
There are many different ways you could approach this, one of which is:
var modules = require('./modules');
var functions = {
eventHandler: eventHandler,
sendMessage: sendMessage,
};
console.log('[tose] Loading modules: ', Object.keys(modules));
function eventHandler(channel, type, data, react=()=>{}) {
switch (type) {
case 'new_message':
console.log('[tose][new_message]', channel, 'from:', data.cid, 'message:', data.message);
if (regexTemplates.testSearch.test(data.message.toLowerCase())) {
...
} else {
functions.sendMessage(channel, data.cid, data.message); // Basic echo message
}
break;
}
}
// The function to be stubbed
function sendMessage(channel, cid, message) {
console.log('[tose][send_message]', channel, 'to:', cid, 'message:', message);
coms[channel].sendMessage(cid, message); // Getting error here thus not really stubbed
}
module.exports = functions;
Note: functions is not a descriptive name - feel free to change it to something that is more meaningful.
Related
I have a class in jquery that calls a service and returns a promise, which is then executed in my main area via a .done and Im trying to wrap that call in a another class I have that will make sure multiple calls are not made for the same ID. However I am finding this very very difficult to test as I can not accurcately get the promise working in phantomJS/Sinon. Heres what the area Im trying to test is
LOCKER.execute(diagRunId, function (unlock) {
SERVICE_ACCESSOR.makeCall params)
.done(function (data) {
console.log("Success!");
unlock();
})
.fail(function (jqXHR, textStatus, errorThrown) {
console.log("Failed!");
unlock();
});
});
and In my test file I have my setup like so
var setup = function() {
P.mock('service-accessor', function () {
return {
makeCall: sinon.stub().returns({})
};
});
P.mock('locker', function () {
var methods = {
execute: function (lockId, wrapped) {
console.log('locked - ' + lockId)
wrapped()
},
unlock: sinon.stub()
};
return {
execute: methods.execute,
unlock: methods.unlock
};
});
P.start();
}
with finally the test just calling the method
aui.suite('Locker Test', function () {
aui.test('should lock and then unlock', testFile, {
setup: setup,
browser: function () {
P.when('utils', 'service-accessor','locker').execute(
'test', function (SERVICE_ACCESSOR, LOCKER) {
UTILS.makeCall("Identifier")
expect(LOCKER.unlock).to.have.been.called.once;
done();
}
);
},
validate: function () {},
});
});
The locker works and begins execution of the service call, but then the service call fails with
Error: PhantomJS: `TypeError: 'undefined' is not a function (evaluating 'SERVICE_ACCESSOR.callService( params).done')` near
L2492> }).fail(function (jqXHR, textStatus, errorThrown) {
From my understanding my mock should return a just a empty object when its being called, but then I dont understand a) Why its going to fail and b) Whats undefined? My assumption is that its because Im not returning three objects, but Im trying to get it to succeed first! How can I correctly stub/mock this?
In the end I didn't make a promise or use a stub. I used the following function that would call the done and fail in my call instead.
function() {
return { done: function(callback) {
if(window.makeParamountCallSuccess) {
callback({"data": "data"});
return {
fail: function(){}
}
} else {
return {
fail: function(failCallback){ failCallback("jqXHR", "textStatus", "errorThrown")}
}
}
}
}
}```
This question already has answers here:
How to access the correct `this` inside a callback
(13 answers)
Closed 1 year ago.
I'm using Vuejs and i would i have two methods one is to make a call and another one is to hangup
i would like to access to device variable that i have in makeCall methods from hangup
error : Cannot set property 'device' of undefined at eval
this is my code :
export default {
components: {Modal},
data: () => ({
device: '',
showModal: false,
form:{
output: ''
},
collection: {
}
}),
created(){
},
methods: {
init(){
this.showModal = true
},
dialer(digit){
this.form.output += `${digit}`
this.count++
},
clearScreen(){
let screen = document.getElementById('output').value
this.form.output = screen.slice(0, -1)
},
hangUp(){
this.device.disconnectAll();
},
makeCall(){
console.log("Requesting Access Token...");
// Using a relative link to access the Voice Token function
getAPI.get("api/contacts/token/")
.then(function (response) {
console.log("Got a token.");
console.log("Token: " + response.data.token);
// Setup Twilio.Device
this.device = new Twilio.Device(response.data.token, {
// Set Opus as our preferred codec. Opus generally performs better, requiring less bandwidth and
// providing better audio quality in restrained network conditions. Opus will be default in 2.0.
codecPreferences: ["opus", "pcmu"],
// Use fake DTMF tones client-side. Real tones are still sent to the other end of the call,
// but the client-side DTMF tones are fake. This prevents the local mic capturing the DTMF tone
// a second time and sending the tone twice. This will be default in 2.0.
fakeLocalDTMF: true,
// Use `enableRingingState` to enable the device to emit the `ringing`
// state. The TwiML backend also needs to have the attribute
// `answerOnBridge` also set to true in the `Dial` verb. This option
// changes the behavior of the SDK to consider a call `ringing` starting
// from the connection to the TwiML backend to when the recipient of
// the `Dial` verb answers.
enableRingingState: true,
debug: true,
});
this.device.on("ready", function (device) {
console.log("Twilio.Device Ready!");
});
this.device.on("error", function (error) {
console.log("Twilio.Device Error: " + error.message);
});
this.device.on("connect", function (conn) {
console.log('Successfully established call ! ');
// $('#modal-call-in-progress').modal('show')
});
this.device.on("disconnect", function (conn) {
console.log("Call ended.");
// $('.modal').modal('hide')
});
var params = {
To: document.getElementById('output').value
};
console.log("Calling me " + document.getElementById('output').value + "...");
if (this.device) {
var outgoingConnection = this.device.connect(params);
outgoingConnection.on("ringing", function () {
console.log("Ringing...");
});
}
})
.catch(function (err) {
console.log(err);
console.log("Could not get a token from server!");
});
}
}
}
</script>
The error was due to how this works in JS. The function declaration in the promise was creating a different this context than the main function. The function keyword sets this based on where it is called, whereas arrow functions set this based on where it is defined in the code.
All you need to do is replace the function(response){} declaration in the getAPI promise .then with an arrow function, and it will work fine.
makeCall(){
console.log("Requesting Access Token...");
// Using a relative link to access the Voice Token function
getAPI.get("api/contacts/token/")
.then((response) => {
Try replacing these lines - to use an arrow function in the .then
One of my friend has this issue, we are hosting the api using lite-server, and access the api on browser, it says callback is not a function.
'use strict';
(function () {
var StoreAPI = require('Store');
module.exports = {
getBooks: function (event, context,callback) {
var books;
StoreAPI.getAll('books', function(err,data){
console.log(data);
callback({
path: data.path,
error: false,
errorCode: 0,
body: {
books : data
}
});
});
}
};
} ());
url goes as:
http://localhost:3000/getBooks
This issue is likely caused by the code that invokes getBooks.
After the API call is completed, you try to call the function passed in as parameter callback, but as the error message says, what you are trying to call is not a function. To resolve this, ensure that the invoking code actually passes a function as callback.
I have a function that makes an AJAX call to a service. I'm attempting to expect that the displayError function is called on a failure.
I have my function ajaxCall that accepts a url. Upon success I pass the result to displaySuccess and when there's an error I pass the details to displayError.
function ajaxCall(url) {
$.ajax({
method: "GET",
url: url,
data: "json",
beforeSend: function (xhr) {
//Do Stuff
},
error: function(xhr, textStatus, errorThrown) { displayError(xhr, textStatus, errorThrow, url)},
success: function (results) { displaySuccess(result) }
});
}
function displayError(xhr, textStatus, errorThrow, url) {
//Do Stuff//
}
function displaySuccess(results) {
//Do Stuff//
}
In Jasmine I have it successfully verifying the URL. My problem is in testing to insure that the displayError and displaySuccess functions are called.
I have the following for this specific issue so far.
describe('The ajaxCall component', function() {
it('should call the error function when the ajax call fails', function () {
var obj = {};
spyOn(obj, 'displayError');
spyOn($, "ajax").and.callFake(function (options) {
options.error();
});
ajaxCall('/myResource/get');
expect(obj.method).toHaveBeenCalled();
});
}
I'm a little new to unit testing and I've searched trying to find suggestions that would help but they make the unit test fail. Where am I going wrong with this?
This all boils down to how you spy on your objects and writing code that is more testable. Let's work through a few strategies.
Strategy 1
Given your current code is not within an object, you could test that these functions are called by simply testing their implementation directly.
Instead of testing that the functions were called, you would test their implementation directly.
Example
describe("strategy 1", function () {
var ajaxSpy;
beforeEach(function () {
ajaxSpy = spyOn($, 'ajax');
ajaxCall();
});
describe("error callback", function () {
beforeEach(function() {
spyOn(window, 'alert');
var settings = ajaxSpy.calls.mostRecent().args[0];
settings.error();
});
describe("when there is an error", function() {
it("should alert an error message", function() {
expect(window.alert).toHaveBeenCalledWith('Error');
});
});
});
});
Strategy 2
While the above works, it can be cumbersome to write tests. Ideally, you want to test the invocation and implementation separately.
To do so, we can spy on these functions. Since these are in the global namespace, you can spy on them through the window object.
Example
describe("strategy 2", function () {
var ajaxSpy;
beforeEach(function () {
ajaxSpy = spyOn($, 'ajax');
ajaxCall();
});
describe("error callback", function () {
beforeEach(function() {
spyOn(window, 'displayError');
var settings = ajaxSpy.calls.mostRecent().args[0];
settings.error();
});
describe("when there is an error", function() {
it("should alert an error message", function() {
expect(window.displayError).toHaveBeenCalled();
});
});
});
});
Strategy 3 (Recommended)
The final strategy, and what I recommend, has a similar setup to the second strategy, except we encapsulate our implementation into a custom object.
Doing so makes the code more testable by wrapping functionality in objects and avoids the global namespace (i.e. window).
Example
describe("solution 3", function() {
var ajaxSpy;
beforeEach(function() {
ajaxSpy = spyOn($, 'ajax');
ajaxService.ajaxCall();
});
describe("error callback", function() {
beforeEach(function() {
spyOn(ajaxService, 'displayError');
var settings = ajaxSpy.calls.mostRecent().args[0];
settings.error();
});
it("should alert an error message", function() {
expect(ajaxService.displayError).toHaveBeenCalled();
});
});
});
I want to pass a variable from server side to a template in the client side.
In main.html, I have this template:
<template name="hello">
...
<p>{{privateKey}}</p>
</template>
In main.js, I want something like:
if (Meteor.isClient) {
Template.hello.helpers({
privateKey : function () {
return 'call to function makePrivateKey';
}
});
}
if (Meteor.isServer) {
Meteor.methods({
makePrivateKey: function() {
var privKey = bitcoinjs.ECKey.makeRandom();
return privKey.toWIF();
}
});
}
How can I invoque the function makePrivateKey from server side and print
the private key in my template ?
I don't want to use session variable or dynamic variable.
Seems like a strange structure to me. You wouldn't want a helper to generate a private key, I don't think. Every time that template renders, it would generate a key and print it. But you couldn't just call the method like this anyway, as using Meteor.call on the client requires a callback, and so again, this is not the way to do this. However, this could work:
if (Meteor.isClient) {
Template.hello.events({
'click .generate-key': function () {
Meteor.call('makePrivateKey', function (error, result) {
if (!error) {
Session.set('credentials/privKey', result.privKey);
}
else {
// handle error
}
})
}
});
Template.hello.helpers({
privateKey: function () {
return Session.get('credentials/privKey');
}
});
}
if (Meteor.isServer) {
Meteor.methods({
makePrivateKey: function () {
try {
var privKey = bitcoinjs.ECKey.makeRandom();
return {privKey: privKey.toWIF()};
} catch (e) {
throw Meteor.Error('some-error', 'Bad things happened.');
}
}
});
}
In general using the Meteor 'Method' would be the way to go:
if (Meteor.isClient) {
Template.hello.helpers({
privateKey : function () {
return Meteor.call(makePrivateKey);
}
});
}