Where is this object now? - javascript

I have to test some RequireJS JavaScript code and don't understand how to replace a special object. This is the code (simplified) of one function within a RequireJS Module:
changeView: function (view) {
function setView(view) {
this.currentView = view;
}
setView(view);
},
Now I want to test, if currentView is view.
But where is the currentView object saved?
I tried several things in my tests:
expect(view.currentView).toBe(...);
expect(view.changeView.currentView).toBe(...);
I also tried to save the inner function as an extra function in the module but I could not access this currentView property from within my test. How do I need to do that?

The answer is:
expect(window.currentView).toBe(...);
An object called via this in a nested function seems to be stored in the window object.

Related

Bind a function defined in an anonymous object

I'm writing a SAPUI5 application that have a sap.m.Table loaded at runtime. I want to use a factory method defined inside the controller (is this the best way?). To do this, after the aquiring of the model, I have to bind my table's aggregation (items) to that model:
tableSeason.bindAggregation("items", {
path: "/results",
factory: this.tableFactory
});
Sadly this piece of code doesn't work, becouse the function tableFactory have inner calls to this.
The tableFactory's prototype is tableFactory: function(sId, oContext){...}.
I dont want to put the whole code inside the tableFactory function, can someone suggests a way to resolve this problem?
Thanks,
Gabriele
If you wanna attach the function to the factory:
this.tableFactory.bind(this)
If you want to the function be called immediately
this.tableFactory.call(this)
this.tableFactory.apply(this)

Export objects vs functions NodeJs

Export Objects {} vs Export function
I'm developing an exercise application, I came across to the question When do I need to exports an object {} instead of a function class?
Scenario example:
I'm building a simple authentication module using the object style.
// file auth.js
module.exports = {
login: function() {
// code login
},
logout: function() {
// code logout
},
register: function() {
// code register
}
}
Here I'm using the anonymous function style
module.exports = function() {
return {
login: function() {
// code login
},
logout: function() {
// code logout
},
register: function() {
// code register
}
}
}
Requiring
When I want to require this module I just do:
var auth = require('auth');
auth.login(); // trigger login function via object
auth().login() // trigger login function via function
It will work with both the approaches, but I'm confused to choose which fit better and why.
Questions
How do you understand in your design of a module, when is appropriate to exports, an object, anonymous function, named function to Instantiate?
Which are the difference and how the require method behave, when requiring these functions or Objects ?
How do you understand in your design of a module, when is appropriate to exports, an object, anonymous function, named function to Instantiate?
true minimalists aim to export a single function if that will suffice. This is based on the scope of your module and assumes a small and focused purpose.
Export an object of related functions when you need a set of functions to have a meaningful set of functionality. For example, a tempConversion module might have both cToF and fToC functions.
If you are doing OO, exporting the constructor function is fine.
a closure function that returns the real payload (which can be an object or a function) is useful for configuration options. For example var tip = require('computeTip')(18); could store 18 in closure and return a function that would calculate 18% tip when called with a number.
Here's a rule of thumb: if you export only one named function a la require('average').average(listOfNumbers), it's redundant, so just export the average function directly to make it more concise. If you have more than one function you want to expose, use an object whose properties are the functions.
Which are the difference and how the require method behave, when requiring these functions or Objects ?
There are no differences. require has a single behavior that accommodates each of these techniques.
In both your examples you actually return an object and not a function class. In the second case you get the object by executing the function. In order to use the function as a class you should return a function which acts as the constructor, and then instantiate with the new operator.
That being said, I would prefer an object over a class if I consider my module as a singleton, something that would be common to every file that would include it. An example would be a module that implements a common access layer to my database.
A class makes more sense if you intent to instantiate it multiple times. An example would be a specific model class for your database schema, like a User class.

jasmine jquery test to check if correct arguments have been passed to a method

I am new to javascript testing.
I am using jasmine and need to test if correct arguments have been passed to a method.
This is my method:
function myView(){
if($('.view').is('.list')){
myWindow('list');
}else{
myWindow('random');
}
$('.view').toggleClass('my-list');
}
function myWindow(list) {
var url = /test.json;
$.post(url, {"list": list});
}
Here are my tests:
describe('#myView', function() {
beforeEach(function() {
fixture.load('myview.html');
});
it('sets window to list', function(){
expect(window.myWindow).toHaveBeenCalledWith('list');
});
});
I get the following error.
Error: Expected a spy, but got Function.
If i add this line before the expect(which seems wrong because I am specifying the correct param which should be identified by test)
spyOn(window, myWindow('list'));
I get the following error:
undefined() method does not exist
Can someone show my a good way to write the above tests?
spyOn's second parameter is the name of the property you need to spy on. When you call spyOn(window, myWindow('list'));, your second parameter is the return value of myWindow('list') which is undefined => throws error: undefined() method does not exist
In your code, simply doing this should work:
describe('#myView', function() {
beforeEach(function() {
fixture.load('myview.html');
});
it('sets window to list', function(){
spyOn(window, "myWindow");//spy the function
myView();//call your method that in turn should call your spy
expect(window.myWindow).toHaveBeenCalledWith('list');//verify
});
});
In software unit testing, there are concepts called stub and mock objects. These are dependencies of the method under test. spyOn is to create your fake objects to test your methods.
You're accessing the global window object directly, that's really a problem in unit testing. Although Javascript is a dynamically typed language, we're still able to mock your window object (this is not possible with some statically-typed languages like c#). But to create a good unit-testable code, I recommend you should redesign your code to inject it from outside.
function myView(awindow){ //any dependency should be injected, this is an example to inject it via parameter
if($('.view').is('.list')){
awindow.myWindow('list');
}else{
awindow.myWindow('random');
}
$('.view').toggleClass('my-list');
}
Try this:
describe('#myView', function() {
beforeEach(function() {
fixture.load('myview.html');
});
it('sets window to list', function(){
var spy = {myWindow:function(list){}};
spyOn(spy, "myWindow"); //create a spy
myView(spy); //call your method that in turn should call your spy
expect(spy.myWindow).toHaveBeenCalledWith('list'); //verify
});
});
One more thing, jQuery code like this is not quite a good candidate for unit testing as it involves DOM manipulation in your code. If you have time, you should take a look at angularjs framework that separates your View (DOM) from your model (logic), uses dependency injection to make your code testable.

variables with requieJS

My call to requireJS in on my index page, what I want to know is it possible for me to get a variable out of a function that is on my index page and use it as a condition in my main.js file? I am trying to have a script be used based on a variable on my index page.
If you want your wariable to be accesible on both index and main pages, then create separate requireJS module, put yuor variable in there and reference this module wherever you need it.
So, assuming that you already have two modules: index.js and main.js, you can create third module, let's call it "myVariables.js". Create that file and inside put something like this:
define(function() {
var vm = {
myGlobalVariable = "Hello"
};
return vm;
});
Then in your other modules you can reference to this module, for example like this:
var myVariabledModule = require('myVariables');
alert(myVariablesModule.myGlobalVariable);
This should give you and allert with the "Hello" string and this code should work in any module of your application.

Backbone and RequireJS conflicts - instances or constructors?

Could someone explain the fundamental difference between:
define(['backbone'], function(Backbone) {
MyModel = Backbone.Model.extend({
});
});
define(['backbone', 'models/mymodel'], function(Backbone){
var app = Backbone.View.extend({
initialize: function() {
var model = new MyModel();
}
});
});
and:
define(['backbone'], function(Backbone) {
var MyModel = Backbone.Model.extend({
});
return MyModel;
});
define(['backbone', 'models/mymodel'], function(Backbone, MyModel){
var app = Backbone.View.extend({
initialize: function() {
var model = new MyModel();
}
});
});
In the former, the first module simply defines MyModel. In the latter, it's created as a variable and returned, and the second module needs to have it put in the parameters when imported.
RequireJS examples I see around seem to vary between the two, but I don't really understand the difference - does one return an instance and the other a constructor?
In my application I didn't even notice that I was actually using both ways in different places, and I think it was causing problems. I was using a lot of
self = this
self.model.doSomething
inside my views and models, and as my app got bigger, I started getting errors because there were conflicts with definitions of self.
Short Version: 1st version == wrong.
Medium Version: The first one bypasses Require entirely by using global variables, while the second one actually uses Require.
Long version:
The way Backbone modules work is that you run "define", pass it a function (and usually an array of dependencies also), and whatever gets returned from that function is defined as that module. So if I do:
// Inside foo.js
define([], function() {
return 1;
});
I've defined the "foo" module to be 1, so if elsewhere I do:
define(['foo'], function(foo) {
alert(foo); // alerts 1
});
Your first version doesn't return anything, so it's not actually creating a Require module at all.
How does it work then? Well, in that version you do:
MyModel = Backbone.Model.extend({
NOT:
var MyModel = Backbone.Model.extend({
So that's really the same as doing:
window.MyModel = Backbone.Model.extend({
Then when the second part of the code runs, it access window.MyModel, and works ... but it's completely bypassing Require.js in the process.
I think the most important thing to takeaway is: ALWAYS DECLARE (ie. var) YOUR JAVASCRIPT VARIABLES. I don't agree with everything Crockford says, but he's dead right on this one. You will get lots of bugs (with Require and without) if you don't make this a habit.
Beyond that, the next most important thing is probably: ALWAYS RETURN SOMETHING FROM THE FUNCTION YOU PASS TO define. There are certain special cases where you don't want to return anything, but unless you are deliberately trying to solve one of those cases you should always return something to define the module.
Finally, if you're using Require, every variable in your code should either:
Come from the define function (ie. it should be an argument variable from the function that you pass to define), or
It should be declared (ie. var-ed ) inside that file
If you use JSLint or 'use strict'; (as Valentin Nemcev suggested), or if you use an editor like Eclipse, your tools can help you ensure this (and in fact make it easy to ensure).
MyModel = Backbone.Model.extend({});
Here you are not returning a constructor, you are defining a global variable and accessing it later in different module.
Actually it is wrong, it works by accident. You should return your modules from define and access them via parameters in other modules.
Like this:
return Backbone.Model.extend({});
You should use strict mode to avoid problems with global variables in JS.
Also, constructor in JS is just a function that is meant to be run with new. Backbone extend always returns a constructor function, and you create a model instance by calling the constructor with new, like you are doing in both examples.

Categories