I have a function im trying to test:
vm.clearArray = function(){
for (var id=0; id<vm.copyArray.length;id++){
vm.styleIcon(vm.copyArray[id],'black')
}
vm.copyObjArray = [];
vm.copyArray = [];
}
I'm trying to test it like:
it('should have cleared copyArray on function call', function(){
var ctrl = $componentController('copy', null);
spyOn(ctrl, 'clearArray').and.callThrough();
spyOn(ctrl, 'styleIcon').and.callThrough();
ctrl.copyArray = [123];
ctrl.clearArray();
expect(ctrl.clearArray).toHaveBeenCalled();
// expect(ctrl.styleIcon).toHaveBeenCalled();
expect(ctrl.copyObjArray).toEqual([]);
expect(ctrl.copyArray).toEqual([]);
});
If I uncomment the above expect I get an error and the vm.styleIcon call is never covered in my coverage report. By setting copyArray to contain a value in the array I would think that the for loop would then trigger when running the test. That does not seem to be the case.
Thanks.
I believe there is some kind of inheritance scheme that is causing your error. My assumption is that your controller is extended by a base controller.
From what few code I see, I can make two assumptions:
1) clearArray() is overridden in the child controller eg.
vm.clearArray = function(){
...
vm.copyArray = [];
}
so you are trying to test the wrong clearArray()
or
2) ctrl.copyArray is not writable because of the way inheritance was implemented, eg.
function ParentController() {
var vm = this;
vm.copyArray = [];
vm.copyObjArray = [];
vm.clearArray = function() {
for (var id=0; id<vm.copyArray.length;id++){
vm.styleIcon(vm.copyArray[id],'black')
}
vm.copyObjArray = [];
vm.copyArray = [];
}
vm.styleIcon = function(index, color) {
}
};
function ChildController() {
ParentController.call(this);
}
ChildController.prototype = Object.create(ParentController.prototype, {copyArray:{ value: [] } });
var ctrl = new ChildController();
Using the above will yield the error, copyArray is defined as a non writable property, so line:
ctrl.copyArray = [123];
does not change its value.
Object.defineProperty()
Difference between Configurable and Writable attributes of an Object
Anyway, without more of the code, it is difficult to get what is causing the error.
The code of the loop looks good, so I think that the property vm.copyArray might not be set at all. If you add a console.log(vm.copyArray), what is the result?
Perhaps vm and $componentController('copy', null) are not references to the same object, but call each other's functions through some library? is there any other way to reference vm from the test script, rather than using $componentController('copy', null)?
Your loop must be triggered when you pass the array in the function as an argument. Of course your actually code will fail unless the pass vm.copyArray as an argument in the actual code, but it will show you if the loop is the problem, or the reference to the vm from the test script:
//tested function
vm.clearArray = function(copyArray){
for (var id=0; id<copyArray.length;id++){
vm.styleIcon(copyArray[id],'black')
}
}
//test
ctrl.clearArray([123]);
It's difficult to determine the exact reason because you've shown a sample taken straight from your test, and not a minimal, complete, and verifiable example. Also, you didn't specify what the error(s) or expect results are, so we're going off very limited information.
That said, I strongly suspect vm is undefined/null or not a prototype instantiatable through $componentController. If this is the case, you should be receiving an error at spyOn(ctrl, 'clearArray').and.callThrough() or ctrl.clearArray(), never running the loop and thus never calling vm.styleIcon. In this scenario you'd need to verify that ctrl is in fact an instance of whatever prototype vm is a part of(is it actually a global variable?).
If this is not the case and both the vm prototype is correct and $componentController('copy', null); is creating the object you think it is, perhaps styleIcon is undefined/null, unable to be called and creating essentially the same problem. In this scenario, ensure styleIcon is set and that it's the function you think it is.
When all else fails, debuggers are your friend.
Please specify what the error(s) are and where they're occuring(in more detail) for a better answer.
Related
I am trying to get songs from soundcloud, I am using some input to set value and send it to my factory to get all the related list of songs and display it.
The issue is the the first time all works correctly, but when I am trying to input new values I am getting same results as first time.
My code looks like:
.controller('DashCtrl', function ($scope, SongsService) {
$scope.formData = {};
$scope.searchSong = function () {
SongsService.setData($scope.formData.songName);
};
UPDATE
the factory :
.factory('SongsService', function ($rootScope) {
var List = {};
List.setData = function (tracks) {
var page_size = 6;
SC.get('/tracks', {limit: page_size, linked_partitioning: 1}, function (tracks) {
// page through results, 100 at a time
List = tracks;
$rootScope.$broadcast('event:ItemsReceived');
});
};
List.getItems = function () {
return List;
};
return List;
}).value('version', '0.1');
Thanks for help!
It's hard to tell without a plunkr reproducing the issue and showing all your relevant code, but I think your problem is that you're overwriting the List variable in the async answer, and this List (I assume) is the object you originally returned from your factory.
I see two noteworthy concepts here:
the fact that angular factories are effectively singletons
and that javascript objects are passed by reference-by-value (see call-by-sharing, or one of many stackoverflow discussions).
An angular factory is a singleton, meaning the factory function will only be called once, before the first injection, and every controller it's injected into will work with the same object reference it returned. If you overwrite this object reference, well, the previous value (which the controller has) is still a reference to the original object.
Edit: In fact, by overwriting List you're creating a new object which doesn't even have a setData method anymore!
You probably want to make List private to SongsService, and return a more complex object from the factory that captures List in a closure, and offers some public getter/setter methods for it. (If you insist on replacing the contents of the returned List variable, empty the object and extend it with the new properties - including this method again. But this is much more work, and not a nice solution.)
In Angular Service constructors and Factory methods are singleton objects. You need to return a method that you can call. Your code examples are incomplete so it is hard to tell what is going on. What is returned by your factory method, the List object?
If so, when the first call is completed, it overwrites the List object so that the setData method can't be called a second time. What is the SC object, I can not see in your example how you are injecting it. You probably want to fix that too.
Consider this possible solution.
Service
Songs.$inject = ['$http'];
function Songs($http) {
this.$http = $http;
}
Songs.prototype.getSongs = function(searchTerm) {
return this.$http.get('http://someendpoint/songs', {searchTerm: searchTerm});
}
service('songs', Songs);
Controller
DashController.$inect = ['songs'];
functionDashController(songs) {
this.songs = songs;
this.results = [];
}
DashController.prototype.searchSongs = function(searchTerm) {
var self = this;
this.songs.getSongs(searchTerm).then(function(results) {
this.results = results;
});
}
controller('DashController', DashController);
This is example uses the best practice controllerAs syntax explained here: http://toddmotto.com/digging-into-angulars-controller-as-syntax/
I found the issue,
I got same results all the time because I didnt use cooreclty the api of soundcloud, I didnt send the title on the api... also you are correct, I should not set the list as empty..I should set some value to the list...
In Ruby I think you can call a method that hasn't been defined and yet capture the name of the method called and do processing of this method at runtime.
Can Javascript do the same kind of thing ?
method_missing does not fit well with JavaScript for the same reason it does not exist in Python: in both languages, methods are just attributes that happen to be functions; and objects often have public attributes that are not callable. Contrast with Ruby, where the public interface of an object is 100% methods.
What is needed in JavaScript is a hook to catch access to missing attributes, whether they are methods or not. Python has it: see the __getattr__ special method.
The __noSuchMethod__ proposal by Mozilla introduced yet another inconsistency in a language riddled with them.
The way forward for JavaScript is the Proxy mechanism (also in ECMAscript Harmony), which is closer to the Python protocol for customizing attribute access than to Ruby's method_missing.
The ruby feature that you are explaining is called "method_missing" http://rubylearning.com/satishtalim/ruby_method_missing.htm.
It's a brand new feature that is present only in some browsers like Firefox (in the spider monkey Javascript engine). In SpiderMonkey it's called "__noSuchMethod__" https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/NoSuchMethod
Please read this article from Yehuda Katz http://yehudakatz.com/2008/08/18/method_missing-in-javascript/ for more details about the upcoming implementation.
Not at the moment, no. There is a proposal for ECMAScript Harmony, called proxies, which implements a similar (actually, much more powerful) feature, but ECMAScript Harmony isn't out yet and probably won't be for a couple of years.
You can use the Proxy class.
var myObj = {
someAttr: 'foo'
};
var p = new Proxy(myObj, {
get: function (target, methodOrAttributeName) {
// target is the first argument passed into new Proxy, aka. target is myObj
// First give the target a chance to handle it
if (Object.keys(target).indexOf(methodOrAttributeName) !== -1) {
return target[methodOrAttributeName];
}
// If the target did not have the method/attribute return whatever we want
// Explicitly handle certain cases
if (methodOrAttributeName === 'specialPants') {
return 'trousers';
}
// return our generic method_missing function
return function () {
// Use the special "arguments" object to access a variable number arguments
return 'For show, myObj.someAttr="' + target.someAttr + '" and "'
+ methodOrAttributeName + '" called with: ['
+ Array.prototype.slice.call(arguments).join(',') + ']';
}
}
});
console.log(p.specialPants);
// outputs: trousers
console.log(p.unknownMethod('hi', 'bye', 'ok'));
// outputs:
// For show, myObj.someAttr="foo" and "unknownMethod" called with: [hi,bye,ok]
About
You would use p in place of myObj.
You should be careful with get because it intercepts all attribute requests of p. So, p.specialPants() would result in an error because specialPants returns a string and not a function.
What's really going on with unknownMethod is equivalent to the following:
var unk = p.unkownMethod;
unk('hi', 'bye', 'ok');
This works because functions are objects in javascript.
Bonus
If you know the number of arguments you expect, you can declare them as normal in the returned function.
eg:
...
get: function (target, name) {
return function(expectedArg1, expectedArg2) {
...
I've created a library for javascript that let you use method_missing in javascript: https://github.com/ramadis/unmiss
It uses ES6 Proxies to work. Here is an example using ES6 Class inheritance. However you can also use decorators to achieve the same results.
import { MethodMissingClass } from 'unmiss'
class Example extends MethodMissingClass {
methodMissing(name, ...args) {
console.log(`Method ${name} was called with arguments: ${args.join(' ')}`);
}
}
const instance = new Example;
instance.what('is', 'this');
> Method what was called with arguments: is this
No, there is no metaprogramming capability in javascript directly analogous to ruby's method_missing hook. The interpreter simply raises an Error which the calling code can catch but cannot be detected by the object being accessed. There are some answers here about defining functions at run time, but that's not the same thing. You can do lots of metaprogramming, changing specific instances of objects, defining functions, doing functional things like memoizing and decorators. But there's no dynamic metaprogramming of missing functions as there is in ruby or python.
I came to this question because I was looking for a way to fall through to another object if the method wasn't present on the first object. It's not quite as flexible as what your asking - for instance if a method is missing from both then it will fail.
I was thinking of doing this for a little library I've got that helps configure extjs objects in a way that also makes them more testable. I had seperate calls to actually get hold of the objects for interaction and thought this might be a nice way of sticking those calls together by effectively returning an augmented type
I can think of two ways of doing this:
Prototypes
You can do this using prototypes - as stuff falls through to the prototype if it isn't on the actual object. It seems like this wouldn't work if the set of functions you want drop through to use the this keyword - obviously your object wont know or care about stuff that the other one knows about.
If its all your own code and you aren't using this and constructors ... which is a good idea for lots of reasons then you can do it like this:
var makeHorse = function () {
var neigh = "neigh";
return {
doTheNoise: function () {
return neigh + " is all im saying"
},
setNeigh: function (newNoise) {
neigh = newNoise;
}
}
};
var createSomething = function (fallThrough) {
var constructor = function () {};
constructor.prototype = fallThrough;
var instance = new constructor();
instance.someMethod = function () {
console.log("aaaaa");
};
instance.callTheOther = function () {
var theNoise = instance.doTheNoise();
console.log(theNoise);
};
return instance;
};
var firstHorse = makeHorse();
var secondHorse = makeHorse();
secondHorse.setNeigh("mooo");
var firstWrapper = createSomething(firstHorse);
var secondWrapper = createSomething(secondHorse);
var nothingWrapper = createSomething();
firstWrapper.someMethod();
firstWrapper.callTheOther();
console.log(firstWrapper.doTheNoise());
secondWrapper.someMethod();
secondWrapper.callTheOther();
console.log(secondWrapper.doTheNoise());
nothingWrapper.someMethod();
//this call fails as we dont have this method on the fall through object (which is undefined)
console.log(nothingWrapper.doTheNoise());
This doesn't work for my use case as the extjs guys have not only mistakenly used 'this' they've also built a whole crazy classical inheritance type system on the principal of using prototypes and 'this'.
This is actually the first time I've used prototypes/constructors and I was slightly baffled that you can't just set the prototype - you also have to use a constructor. There is a magic field in objects (at least in firefox) call __proto which is basically the real prototype. it seems the actual prototype field is only used at construction time... how confusing!
Copying methods
This method is probably more expensive but seems more elegant to me and will also work on code that is using this (eg so you can use it to wrap library objects). It will also work on stuff written using the functional/closure style aswell - I've just illustrated it with this/constructors to show it works with stuff like that.
Here's the mods:
//this is now a constructor
var MakeHorse = function () {
this.neigh = "neigh";
};
MakeHorse.prototype.doTheNoise = function () {
return this.neigh + " is all im saying"
};
MakeHorse.prototype.setNeigh = function (newNoise) {
this.neigh = newNoise;
};
var createSomething = function (fallThrough) {
var instance = {
someMethod : function () {
console.log("aaaaa");
},
callTheOther : function () {
//note this has had to change to directly call the fallThrough object
var theNoise = fallThrough.doTheNoise();
console.log(theNoise);
}
};
//copy stuff over but not if it already exists
for (var propertyName in fallThrough)
if (!instance.hasOwnProperty(propertyName))
instance[propertyName] = fallThrough[propertyName];
return instance;
};
var firstHorse = new MakeHorse();
var secondHorse = new MakeHorse();
secondHorse.setNeigh("mooo");
var firstWrapper = createSomething(firstHorse);
var secondWrapper = createSomething(secondHorse);
var nothingWrapper = createSomething();
firstWrapper.someMethod();
firstWrapper.callTheOther();
console.log(firstWrapper.doTheNoise());
secondWrapper.someMethod();
secondWrapper.callTheOther();
console.log(secondWrapper.doTheNoise());
nothingWrapper.someMethod();
//this call fails as we dont have this method on the fall through object (which is undefined)
console.log(nothingWrapper.doTheNoise());
I was actually anticipating having to use bind in there somewhere but it appears not to be necessary.
Not to my knowledge, but you can simulate it by initializing the function to null at first and then replacing the implementation later.
var foo = null;
var bar = function() { alert(foo()); } // Appear to use foo before definition
// ...
foo = function() { return "ABC"; } /* Define the function */
bar(); /* Alert box pops up with "ABC" */
This trick is similar to a C# trick for implementing recursive lambdas, as described here.
The only downside is that if you do use foo before it's defined, you'll get an error for trying to call null as though it were a function, rather than a more descriptive error message. But you would expect to get some error message for using a function before it's defined.
I have this viewmodel, on my web I have a dropdown that updates the sortedallemployees option. It works fine except my table is empty initially. Once I sort the first time I get data. Seems like when the vm is created it doesn't wait for allemployees to be populated.
var vm = {
activate: activate,
allemployees: allemployees,
sortedallemployees:ko.computed( {
return allemployees.sort(function(f,s) {
var ID = SelectedOptionID();
var name = options[ ID - 1].OptionText;
if (f[name] == s[name]) {
return f[name] > s[name] ? 1 : f[name] < s[name] ? -1 : 0;
}
return f[name] > s[name] ? 1 : -1;
});
}
Without the rest of your code, its difficult to tell exactly how this will behave. That being said, you are doing several very odd things that I would recommend you avoid.
First, defining all but the simplest viewmodels as object literals will cause you pain. Anything with a function or a computed will almost certainly behave oddly, or more likely not at all, when defined this way.
I would recommend using a constructor function for your viewmodels.
var Viewmodel = function(activate, allEmployees) {
var self = this;
self.activate = activate;
self.allEmployees = ko.observableArray(allEmployees);
self.sortedEmployees = ko.computed(function() {
return self.allEmployees().sort(function(f,s) {
//your sort function
});
});
};
var vm = new Viewmodel(activate, allemployees);
This method has several advantages. First, it is reusable. Second, you can reference its properties properly during construction, such as during the computed definition. It is necessary for a computed to reference at least one observable property during definition for it to be reactive.
Your next problem is that your computed definition is not a function, but an object. It isn't even a legal object, it has a return in it. This code shouldn't even compile. This is just wrong. The Knockout Documentation is clear on this point: computed's are defined with a function.
Your last problem is that your sort function is referencing things outside the viewmodel: SelectedOptionID(). This won't necessarily stop it from working, but its generally bad practice.
Let's say I have a utility function that, for simplicity's sake (the real thing is complicated and irrelevant), returns the current window's querystring.
var someUtilityFunction = () {
return window.location.search.substring(1);
};
Now I want to unit test this function in qUnit (not sure if the testing harness is relevant or not):
test('#1 someUtilityFunction works', function () {
// setup
var oldQS = window.location.search;
window.location.search = '?key1=value1&key2=value2&key3=value3';
var expectedOutput = 'key1=value1&key2=value2&key3=value3';
// test
equals(someUtilityFunction(),
expectedOutput,
'someUtilityFunction works as expected.');
// teardown
window.location.search = oldQS;
});
The problem here is that setting the window.location.search to a different querystring is causing the page to reload, essentially entering an infinite request loop. Is there any way to mock out the window.location object without making any changes to the someUtilityFunction function?
We run into the same problem a few days ago. Mainly there are 2 approaches:
Rewrite your code
This might not be the best (if any) solution, but consider passing the window object to your function to make mocking easier. Even better, use a closure and encapsulate your code. This has a few more advantages:
You can shadow global vars
You can use private local vars
You can avoid naming collisions
The shadowing makes mocking really easy, just pass in something else
Wrap your code
You can wrap all your code inside a function which mocks the window object into a local variable. You have basically two possibilities there as well:
Suppose this is the mock:
var customWindow = {
location: {
search: "",
hash: ""
}
};
Use a closure
var someUtilityFunction;
(function(window) {
// window is now shadowed by your local variable
someUtilityFunction = () {
return window.location.search.substring(1);
};
})(customWindow);
This shadows the global window with a local window.
Use the with statement
Although I am usually strongly against with, it could really solve a lot of problems here. Since it basically remaps your scope, you can very easily mock your environment.
// first some more preparation for our mock
customWindow.window = customWindow;
with(customWindow) {
// this still creates the var in the global scope
var someUtilityFunction = () {
// window is a property of customWindow
return window.location.search.substring(1);
};
// since customWindow is our scope now
// this will work also
someUtilityFunction = () {
// location is a property of customWindow too
return location.search.substring(1);
};
}
By the way: I don't know if the search property suffers from the same symptoms as the hash property - namely sometimes including the question mark and sometimes not. But you might want to consider using
window.location.search.replace(/^\?/, "");
instead of
window.location.substr(1);
I've had some success using window.history.pushState. See this StackOverflow answer. For each unit test, I call a function setQueryString('var=something') which I then implement like this:
function setQueryString(queryString) {
window.history.pushState({}, '', '?' + queryString);
}
You'll need to clear the query string with the afterEach method of QUnit.module, otherwise you're query string will be set to the value of the final test, and you'll get weird results.
The situation was that I wanted to create an instance of a helper class, but that helper class required initialisation through external scripts, so it was inherently asynchronous. With
var obj = new myObj();
clearly an call to
obj.myMethod();
would yield undefined, as obj would either be empty or undefined until its methods and params were loaded by the external script.
Yes, one could restructure things to have a callback pattern and work with the new object within that, but it gets cumbersome and awkward when working with a large and varied API with many dynamic objects as I've been working with.
My question has been, is there any possible way to cleverly get around this?
I imagine the academically trained programmers out there have a name for this sort of approach, but I put it here in case it's not better written somewhere.
What I've done is modify my loader class to use a placeholder+queue system to instantly return workable objects.
Here are the components. Sorry that there are jQuery bits mixed in, you can easily make this a pure-JS script but I've got it loaded anyway and I'm lazy.
'Client' makes this request, where 'caller' is my handler class:
var obj = caller.use('myObj',args);
In Caller, we have
Caller.prototype.use = function(objname,args) {
var _this = this;
var methods = ['method1','method2'];
var id = someRandomString();
this.myASyncLoader(objname,function(){
var q = [];
if (_this.objs[id].loadqueue) {
q = _this.objs[id].loadqueue;
}
_this.objs[id] = new myRemotelyLoadedClass(args);
//realise all our placeholder stuff is now gone, we kept the queue in 'q'
_this.objs[id].isloaded = true;
//once again, the jquery is unnecessary, sorry
$.each(q,function(a,b){
_this.objs[id][b['f']](b['a']);
});
});
_this.objs[id] = _this.createPlaceholderObj(methods,id);
return _this.objs[id];
}
This function basically initiates the loader function, and when that's done loads a new instance of the desired class. But in the meantime it immediately returns something, a placeholder object that we're going to load with all of our remotely loaded object's methods. In this example we have to explicitly declare them in an array which is a bit cumbersome but liveable, though I'm sure you can think of a better way to do it for your own purposes.
You see we're keeping both the temporary object and future object in a class-global array 'objs', associated with a random key.
Here's the createPlaceholderObj method:
Caller.prototype.createPlaceholderObj = function(methods,id) {
var _this = this;
var n = {};
n.tempid = id;
n.isloaded = false;
$.each(methods,function(a,methodCalled){
n[methodCalled] = function(){
_this.queueCall(id,methodCalled,arguments);
}
});
return n;
}
Here we're just loading up the new obj with the required methods, also storing the ID, which is important. We assign to the new methods a third function, queueCall, to which we pass the method called and any arguments it was sent with. Here's that method:
Caller.prototype.queueCall = function(id,methodName,args) {
if (this.objs[id].isloaded == true) {
this.objs[id][methodName](args);
} else {
if (this.objs[id].loadqueue) {
this.objs[id].loadqueue.push({'f':methodName,'a':args});
} else {
var arr = [{'f':methodName,'a':args}];
this.objs[id].loadqueue = arr;
}
}
}
This method will be called each time the client script is calling a method of our new object instance, whether its logic has actually been loaded or not. The IF statement here checks which is the case (isloaded is set to true in the caller method as soon as the async function is done). If the object is not loaded, the methodName and arguments are added to a queue array as a property of our placeholder. If it is loaded, then we can simply execute the method.
Back in the caller method, that last unexplained bit is where we check to see if there is a queue, and if there is, loop through it and execute the stored method names and arguments.
And that's it! Now I can do:
var obj = caller.use('myObj',args);
obj.someMethod('cool');
obj.anotherMethod('beans');
and while there might be a slight delay before those methods actually get executed, they'll run without complaint!
Not too short a solution, but if you're working on a big project you can just put this in one place and it will pay many dividends.
I'm hoping for some follow-ups to this question. I wonder, for example, how some of you would do this using a deferred-promise pattern? Or if there are any other ways? Or if anyone knows what this technique is called? Input from JS whizzes much appreciated.