Hello I got a question regarding mocking JS code with Jasmine.
Imagine having the following situation:
function Test(){
var a = 5;
var b = 3;
Test2(a,b);
}
function Test2(a,b){
var result = a + b;
console.log("result of function Test2: ", result);
}
I want to mock my Test2 function call with Jasmine. I tried the following thing:
describe("1# Test Mocking", function () {
it("test: Mocking Example", function () {
var myMock = new Test();
spyOn(myMock, "Test2").and.returnValue(10,10);
expect(Test2.result).toEqual(20);
});
});
But Jasmine keeps saying: Error: Test2() method does not exist
Does anyone knows why this is and how I could solve this?
Your code doesn't make a lot of sense I'm afraid:
you're telling Jasmine to spy on a method of myMock called Test2, yet it doesn't have such a method (as Jasmine is telling you); Test2 is just a regular function;
even if it did had a method called Test2, you're spying on it after new Test(), at which point the original Test2 would already have been called and the spy would be declared too late;
the original Test2 doesn't return a value, yet you are telling the spy that it should return 10, 10 (it seems to me that you want to call it with those two values, not have it return them);
Test2 doesn't have any side-effects (like returning a value or setting an instance variable or something) apart from creating a local variable (result) and logging it, which makes it pretty untestable;
I think you need to go back to the drawing board to formulate what exactly it is you want the class to do. To help you on your way, here's a possible implementation, including a test to see a) if Test2 gets called with the proper arguments and b) if its return value gets stored properly (again, I don't know what you want the class to do, so I'm just providing some examples):
function Test() {
var a = 5;
var b = 3;
this.result = this.Test2(a, b);
}
Test.prototype.Test2 = function(a, b) {
var result = a + b;
return result;
}
describe("1# Test Mocking", function () {
it("test: Mocking Example", function () {
spyOn(Test.prototype, 'Test2').and.returnValue(20);
var myMock = new Test();
expect(myMock.Test2.calls.argsFor(0)).toEqual([ 5, 3 ]);
expect(myMock.result).toEqual(20);
});
});
Related
While solving a programming challenge, I wrote a function which was intended to take a function as an argument and return a function as well. The returned function was meant to execute the argument function (which was passed to first function). The function's code :-
function func1(f){
let func2 = function(){
if(/*a condition based on a global variable*/){
f();
}
}
return func2;
}
This is currently not working and it raises an Illegal Invocation Type Error. I saw this question but I don't know how to relate it's answers to my code. So, my questions are :-
Why isn't my code working?
What can I do to make it work?
EDIT
I'm invoking the function like this :-
var someFunc = func1(alert);
someFunc("foo");
someFunc("bar");
You need to handle
the context of the function call
the arguments
the returned value
Here's an implementation:
function func1(f, context){
let func2 = function(){
if( some condition ){
return f.apply(context, arguments);
} // there probably should be some "else" behavior...
}
return func2;
}
With some example uses:
var fun = func1(console.log, console);
fun("A", 25); // logs "A", 25
fun = func1(alert);
fun("foo"); // alerts "foo"
Why don't you use var instead of let
function func1(f){
var func2 = function(){
if(/*a condition based on a global variable*/){
f();
}
}
return func2;
}
I've been learning JavaScript and AngularJS and have been seeing functions with an extra set of parentheses. What is this? How does it work?
e.g.: myFunc(args)(moreArgs).
The extra set is for running and returning another function. So, using your example: myFunc will take one argument and return a second function (can be anonymously named):
function myFunc(args) {
return function (moreArgs) {
return args + ' ' + moreArgs;
};
}
var myMsg = myFunc("This")("works!");
alert(myMsg);
In javascript a function can return a function and that returned function can be called immediately. For example:
function a () {
return function () {
console.log('hello');
}
}
One way of calling the returned function is:
var b = a(); // b is now a function returned by a
b(); // logs "hello"
But in javascript you can also do:
a()(); // calls the returned function immediately, logs "hello"
I'm trying to learn how to write better javascript but I'm not sure why this doesn't work. I have two method calls that write to a text field. The first one works fine, but the second one doesn't. Why is the text field variable undefined going thorough a nested call? Any help would be appreciated:
(function () {
var TestObj = function (logEl) {
this.log = logEl;
};
TestObj.prototype = function () {
var log1 = function (text) {
this.log.val(text);
};
var log2 = function (text) {
log1(text);
}
return {
log1: log1,
log2: log2
};
}();
$(function () {
var logEl = $("#log");
var test = new TestObj(logEl);
test.log1("This Works");
test.log2("This Dosen't"); //this.log is undefined
});
})()
In the second case, log1 is being called without any context. The value of this when being called from log2 will be the global object, not an instance of TestObj.
Try changing it to:
var log2 = function (text) {
this.log1(text);
}
demo fiddle
I believe the issue is related to not using this in log2:
var log2 = function (text) {
this.log1(text);
}
A fiddle example: http://jsfiddle.net/y66YT/
As dc5 and Hayes pointed out the value of this is the invoking object. It's the object that comes before the function:
somebutton.click();//this is somebutton
click();//nothing before click, window is assumed or throw exception in strict mode
myObject.doSomething();//this in doSomething is myObject
Since log1 is available through closures it doesn't throw an exception immediately (log1 is undefined) but this in log1 function is window since log2 didn't provide an invoking object.
To set the invoking object you could change code to:
log1.call(this,text);
I'm not a big fan of throwing everything including the kitchen sink in IIFE as it creates unneeded closures for every method. You can wrap your application an an object literal instead and use IIFE where you actually need closures:
var app ={
testObj:function(...
log1 won't be available through closures in log2 but you can call it using this
this.log1(text);
More on prototype, constructor functions, inheritance and the value of this can be found here.
I'm trying to test code using Sinon.js, but I'm unfamiliar with out it's supposed to behave.
I expect that I can create a 'fake' object, wrap it with sinon and pass it to whatever I'm testing, and have it do its thing. However, it seems like every time I try to wrap a sinon object, the function is not there:
var event_api = {
startTime: function() {
return '123';
}
}
var stub = sinon.stub(event_api);
console.log(stub.startTime()) // returns undefined
var mock = sinon.mock(event_api);
console.log(mock.startTime()) // returns undefined
What am I missing?
It depends on what are you trying to do:
If you don't have any expectations on the call then you should use a stub, for example startTime() only has to return a value.
var event_api = {
startTime: sinon.stub().returns('123')
}
console.log(event_api.startTime());
But if what you want is to set some assertions for the call, then you should use a mock.
var event_api = {
startTime: function() {
return '123';
}
}
//code to test
function getStartTime(e) {
return e.startTime();
}
var mock = sinon.mock(event_api);
mock.expects("startTime").once();
getStartTime(event_api);
mock.verify();
Hope this helps.
The function is indeed there, but it's void of any functionality, since it has been stubbed.
If you want to log the function itself in the console, you have to execute:
console.log(stub.startTime) //logs the function itself
instead of:
console.log(stub.startTime()) //logs the result of the function, which is undefined
However, as said, all the methods of a stub object have been "emptied" of their functionality. If you want to make a method of a stubbed object return a value, you can do the following:
var stub = sinon.stub(event_api);
stub.startTime.returns(123);
console.log(stub.startTime) //log the function
console.log(stub.startTime()) //log the result of function, that is now 123
I'm still trying to understand javascript scoping. What is the simplest way to return a value from an anonymous callback function? For instance,
var test = 'outside';
callsFunction(function() { test = 'inside'; });
console.log(test);
where callsFunction calls the anonymous function. I would like this to print 'inside'.
I'm a little bit confused, but I believe this is what you're after
function callsFunction( fn )
{
fn();
}
var test = 'outside';
callsFunction(function() { test = 'inside'; });
console.log(test);
Note that any function can be invoked via the usage of (). However, there are special methods for invoking functions from different contexts, such as Function.call() and Function.apply()
At the risk of pointing out the obvious, the simplest way to have something done after a callback is to simply include it in the callback (and this is, in fact, the way to go):
var test = 'outside';
callsFunction(function() {
test = 'inside';
console.log(test);
});
A real example: let's say you want to update a div with the contents of a file from an Ajax call.
Ajax('file.html', function (data) {
// Update the div here
document.getElementById('d').innerHTML = data;
});
// Not here
Maybe what you are wanting to research is closures in JavaScript?
You didn't define callsFunction in your question, but if you make it a self-invoking anonymous function, you can return the new 'test' value:
var test = 'outside';
(function() { test = 'inside'; })();
console.log(test); // returns 'inside'
If you do define callsFunction:
var test = 'outside';
var callsFunction = function() {
test = 'inside';
return test;
};
console.log(callsFunction()); // returns 'inside'
You can get more complicated with closures.