I'm testing backbone view, that have function:
attachSelect: function(id, route) {
console.log(id);
console.log(route);
this.$(id).select2({
ajax: {
url: route,
dataType: 'json',
results: function(data) {
var results = _.map(data, function(item) {
return {
id: item.id,
text: item.title
};
});
return {
results: results
};
},
cache: true
}
});
}
I need to rewrite (mock) this fuction that, the looks like:
attachSelect: function(id, route) {
console.log(id);
console.log(route);
}
How to do that ?
The simplest way to mock a function is to replace the property at runtime.
You can provide your own monitoring function (commonly called a spy), although this is not the most elegant. That would look like:
var called = false;
var testee = new ViewUnderTest();
var originalAttach = testee.attachSelect; // cache a reference to the original
testee.attachSelect = function () {
called = true;
var args = [].concat(arguments); // get an array of arguments
return originalAttach.apply(testee, args);
};
// Perform your test
expect(called).to.be.true;
If you have a test assertion library like chai, you can use the spies plugin and reduce that to:
var testee = new ViewUnderTest();
var spy = chai.spy(testee.attachSelect);
testee.attachSelect = spy;
// Perform your test
expect(spy).to.have.been.called();
Using a spy library will provide some useful features, such as monitoring the number of calls and their arguments to verify low-level behavior. If you're using Chai or Jasmine, I would highly suggest taking advantage of the corresponding support for spies.
Related
I am trying to create an ajaxHelper module that should be able to expose some number of functions, and when they are called, should return a helper object that has either the data retrieved from an AJAX call, or errors associated with that AJAX call.
Here is something along the lines of what I am thinking:
define("helpers-ajaxDataRetriever", ["jquery"], function ($) {
var helper = {};
helper.getIndexData = function() {
fnIndexData();
return helper;
}
var fnIndexData = function () {
$.ajax({
url: nwatchBaseUrl + '/api/HomeApi/NodeSummary'
}).success(function (returnedData) {
helper.success = true;
helper.data = returnedData;
}).fail(function (jqXHR, textStatus) {
helper.success = false;
helper.error.jqXHR = jqXHR;
helper.error.textStatus = textStatus;
});
}
});
I then want other modules that import this ajaxHelper to be able to call the function (such as getIndexData), which would ultimately populate the helper object, and then be able to reference the various properties, such as the boolean success, data, or error objects.
How do I go about doing this?
For it to work the way you are expecting, the module has to return the properties that you like to expose to the outside world ( for it to be used by other modules).
And since ajax is asynchronous, it is better you tackle such scenarios with callbacks instead accessing the variables directly. As you do not know when the ajax call will successfully complete and return you the data.
define("helpers-ajaxDataRetriever", ["jquery"], function($) {
var helper = {};
// you will pass in the options
// which will contains the success and error
// callbacks, along with additional props
// that you wanna pass in and use
helper.getIndexData = function(options) {
fnIndexData(options);
}
var fnIndexData = function(options) {
$.ajax({
url: options.nwatchBaseUrl + '/api/HomeApi/NodeSummary'
}).success(function(returnedData) {
options.success && options.success.apply(null, arguments);
}).fail(function(jqXHR, textStatus) {
options.error && options.error.apply(null, arguments);
});
}
// You return the object, which are the public methods
// or properties which you wanna expose when this module is used
return {
getIndexData: getIndexData
}
});
// This is when you wanna use the above exposed function
// in any module
define("use-ajax", ["helpers-ajaxDataRetriever"], function(customAjax) {
var options = {
success: function(data) {
console.log('success');
// use the data
}, error: function(jqXHR, textStatus) {
console.log('failure');
// you will have access to the
// arguments of the error function here
},
nwatchBaseUrl: 'https://google.com/'
}
customAjax.getIndexData(options);
});
And since we only want to expose getIndexData in the above example, we can completely get rid of the helper namespace and just return the function definition.
You could also achieve the save by using the concept of promise
I have a function, is a function of polymer web component custom.
getListDedications: function(e){
var idDate = e.model.__data__.date.data.id;
var checkId = function(element){
return element.id == idDate;
};
var responseID = this.dedications.filter(checkId);
this.data = JSON.stringify(responseID[0].entries) || [];
console.log(JSON.stringify(responseID[0].entries) || []);
}
This function return a array or an array empty.
I want to test it, I'm using web-component-tester and I run the tests with gulp test:local.
I know that I need to mock e.model.__data__.date.data.id but I do not know how
Web component tester comes with sinon and sinon-chai bundled in it now.
You don't say what e.model._data__.date.date.id is. Obviously if its just data, you set it up and then call getListModifications with the a parameter. However, if its a function then use sinon stub (or spy) with
var sandbox;
beforeEach(function(){
sandbox = sinon.sandbox.create();
});
afterEach(function(){
sandbox.restore();
});
it('should ...',function(){
var e = sandbox.stub().returns('whatever data you want');
var answer = getListDedications(e);
expect(answer).to.be.an('Array');
});
I am writing an Angular app coming from a PHP background. I wanted to know if there are any good assert/validation libraries I can use to help me validation the arguments passed in to my models e.g. Validate.isNumber(data.id) and automatically throw an appropriate exception on failure
app.factory('Something', ['moment', function(moment){
function Something(data) {
// validate data has required properties and they are of the expected type!!!
this.id = data.id;
this.title = data.title;
this.description = data.description;
}
Something.prototype.getId = function () {
return this.id;
};
Something.prototype.getTitle = function () {
return this.title;
};
Something.prototype.getDescription = function () {
return this.description;
};
return Something;
}]);
I personally use this JSON-Validator tv4:
https://github.com/geraintluff/tv4
It relies on a schema you provide to tell it how your passed arguments(JSON-structure) have to be formed and what kind of types are allowed.
And here you find all validation-options it supports:
http://json-schema.org/latest/json-schema-validation.html
Here you find a practical example
I'm not sure if I'm writing the title correct, but here's what I want to do.
I have this code
var callback = function(result) {
if(result.count < 5) {
msg_id = result.msg_id;
MovieService.getMovies(msg_id, result.count).get(callback, error);
}
if(result.movies.length !== 0) {
setDataToDisplay(result);
}
if(result.count === 5) {
$scope.loading = false;
}
}
MovieService.getMovies(msg_id, 0).get(callback, error);
Basically, when user comes in the first time MovieService will be called and it gets called until the count equals to 5 times. It's like a recursive loop. Now if I want to test this code, I don't know how to do chained stub in Jasmine. I could do something similar in Mockito.
Here's my test so far.
it("should give me the lot of movies", function() {
var movie1 = new MovieBuilder().withTweetId('8').build();
var movie2 = new MovieBuilder().withId('3812').withTweetId('8').build();
var movie3 = new MovieBuilder().withId('3813').withTweetId('8').build();
var movie4 = new MovieBuilder().withId('3814').withTweetId('8').build();
movieService = {
getMovies : function() {
return {
get : function(callback, error) {
callback(
{
'msg_id' : '8',
'count' : '5',
'movies' : [movie1, movie2, movie3, movie4]
});
}
}
}
}
ctrl = controller('MovieTwitterCtrl', {$scope : scope, MovieService : movieService});
expect(scope.movie_groups[0].length).toBe(4);
expect(scope.msg_id).toBe('8');
});
But if I want to test the second, third, fourth and fifth call. How do I do that? Does Jasmine offer something like Mockito? Or how do I do that in pure javascript?
Thanks a lot.
You might want to take a look at Sinon, which is a library that provides methods for spys, stubs and mocks and is compatible with Jasmine.
In order to automatically invoke your callbacks, you would use stub.yields() or stub.yieldsTo(). You've also got spy.getCall(n) that will let you verify the way your method was called during the nth time. Sinon is written in a way that stubs are also spies... so if you create a stub for your movieService, you'll have access to both yields() and getCall(n).
I have a resource that returns an array from a query, like so:
.factory('Books', function($resource){
var Books = $resource('/authors/:authorId/books');
return Books;
})
Is it possible to add prototype methods to the array returned from this query? (Note, not to array.prototype).
For example, I'd like to add methods such as hasBookWithTitle(title) to the collection.
The suggestion from ricick is a good one, but if you want to actually have a method on the array that returns, you will have a harder time doing that. Basically what you need to do is create a bit of a wrapper around $resource and its instances. The problem you run into is this line of code from angular-resource.js:
var value = this instanceof Resource ? this : (action.isArray ? [] : new Resource(data));
This is where the return value from $resource is set up. What happens is "value" is populated and returned while the ajax request is being executed. When the ajax request is completed, the value is returned into "value" above, but by reference (using the angular.copy() method). Each element of the array (for a method like query()) will be an instance of the resource you are operating on.
So a way you could extend this functionality would be something like this (non-tested code, so will probably not work without some adjustments):
var myModule = angular.module('myModule', ['ngResource']);
myModule.factory('Book', function($resource) {
var service = $resource('/authors/:authorId/books'),
origQuery = service.prototype.$query;
service.prototype.$query = function (a1, a2, a3) {
var returnData = origQuery.call(this, a1, a2, a3);
returnData.myCustomMethod = function () {
// Create your custom method here...
return returnData;
}
}
return service;
});
Again, you will have to mess with it a bit, but that's the basic idea.
This is probably a good case for creating a custom service extending resource, and adding utility methods to it, rather than adding methods to the returned values from the default resource service.
var myModule = angular.module('myModule', []);
myModule.factory('Book', function() {
var service = $resource('/authors/:authorId/books');
service.hasBookWithTitle = function(books, title){
//blah blah return true false etc.
}
return service;
});
then
books = Book.list(function(){
//check in the on complete method
var hasBook = Book.hasBookWithTitle(books, 'someTitle');
})
Looking at the code in angular-resource.js (at least for the 1.0.x series) it doesn't appear that you can add in a callback for any sort of default behavior (and this seems like the correct design to me).
If you're just using the value in a single controller, you can pass in a callback whenever you invoke query on the resource:
var books = Book.query(function(data) {
data.hasBookWithTitle = function (title) { ... };
]);
Alternatively, you can create a service which decorates the Books resource, forwards all of the calls to get/query/save/etc., and decorates the array with your method. Example plunk here: http://plnkr.co/edit/NJkPcsuraxesyhxlJ8lg
app.factory("Books",
function ($resource) {
var self = this;
var resource = $resource("sample.json");
return {
get: function(id) { return resource.get(id); },
// implement whatever else you need, save, delete etc.
query: function() {
return resource.query(
function(data) { // success callback
data.hasBookWithTitle = function(title) {
for (var i = 0; i < data.length; i++) {
if (title === data[i].title) {
return true;
}
}
return false;
};
},
function(data, response) { /* optional error callback */}
);
}
};
}
);
Thirdly, and I think this is better but it depends on your requirements, you can just take the functional approach and put the hasBookWithTitle function on your controller, or if the logic needs to be shared, in a utilities service.