In knockout, i want to unit test the value of a computed observable that depends on another observable, using jasmine.
However, it doesn't work, as the value of the computed observable doesn't update when i change the other observable.
Here is my (simplified) view model:
function MarkersViewModel() {
var self = this;
self.name = ko.observable("chad");
self.computedName = ko.computed(function() {
return self.name();
});
Here is my jasmine spec:
describe("string", function() {
var view_model = new MarkersViewModel();
view_model.name = ko.observable("joe");
it("returns the whole array when there is no filter", function() {
expect(view_model.computedName()).toBe("joe");
});
});
When i run this, jasmine fails:
Expected 'chad' to be 'joe'.
Any idea on how i could implement that?
Thanks
You should not recreate observable, just set value:
describe("string", function() {
var view_model = new MarkersViewModel();
view_model.name("joe"); // <- here
it("returns the whole array when there is no filter", function() {
expect(view_model.computedName()).toBe("joe");
});
});
Your computed scoped original observable (with "chad" assigned in constructor function) and used it.
It may be useful to the same solution solution with QUnit :
test(" Test view_model.name ", function () {
var view_model = new MarkersViewModel();
view_model.name("joe");
equal(view_model.computedName(), "joe");
});
https://github.com/thedom85/Javascript_Knockout_QUnit_Example
Related
i'm kinda new to JS and NS but i'm trying to do some unit test with an nativescript app and my onTap function doesn't "start" when called. I think it may come from the arguments of my onTap but i tried to export it and it didn't work. Thanks for helping. (I'm using Jasmine)
My function looks like this :
function onTap(args) {
let a = 10;
exports.a = a;
}
exports.onTap = onTap;
And my test :
var varTest = require('../home/home-page.js');
describe("test_onTap", function() {
beforeAll(function() {
varTest.onTap(arguments);
});
it("should return true", function() {
expect(varTest.a).toEqual(10);
});
});
I would like to stub the save method available to Mongoose models. Here's a sample model:
/* model.js */
var mongoose = require('mongoose');
var userSchema = mongoose.Schema({
username: {
type: String,
required: true
}
});
var User = mongoose.model('User', userSchema);
module.exports = User;
I have some helper function that will call the save method.
/* utils.js */
var User = require('./model');
module.exports = function(req, res) {
var username = req.body.username;
var user = new User({ username: username });
user.save(function(err) {
if (err) return res.end();
return res.sendStatus(201);
});
};
I would like to check that user.save is called inside my helper function using a unit test.
/* test.js */
var mongoose = require('mongoose');
var createUser = require('./utils');
var userModel = require('./model');
it('should do what...', function(done) {
var req = { username: 'Andrew' };
var res = { sendStatus: sinon.stub() };
var saveStub = sinon.stub(mongoose.Model.prototype, 'save');
saveStub.yields(null);
createUser(req, res);
// because `save` is asynchronous, it has proven necessary to place the
// expectations inside a setTimeout to run in the next turn of the event loop
setTimeout(function() {
expect(saveStub.called).to.equal(true);
expect(res.sendStatus.called).to.equal(true);
done();
}, 0)
});
I discovered var saveStub = sinon.stub(mongoose.Model.prototype, 'save') from here.
All is fine unless I try to add something to my saveStub, e.g. with saveStub.yields(null). If I wanted to simulate an error being passed to the save callback with saveStub.yields('mock error'), I get this error:
TypeError: Attempted to wrap undefined property undefined as function
The stack trace is totally unhelpful.
The research I've done
I attempted to refactor my model to gain access to the underlying user model, as recommended here. That yielded the same error for me. Here was my code for that attempt:
/* in model.js... */
var UserSchema = mongoose.model('User');
User._model = new UserSchema();
/* in test.js... */
var saveStub = sinon.stub(userModel._model, 'save');
I found that this solution didn't work for me at all. Maybe this is because I'm setting up my user model in a different way?
I've also tried Mockery following this guide and this one, but that was way more setup than I thought should be necessary, and made me question the value of spending the time to isolate the db.
My impression is that it all has to do with the mysterious way mongoose implements save. I've read something about it using npm hooks, which makes the save method a slippery thing to stub.
I've also heard of mockgoose, though I haven't attempted that solution yet. Anyone had success with that strategy? [EDIT: turns out mockgoose provides an in-memory database for ease of setup/teardown, but it does not solve the issue of stubbing.]
Any insight on how to resolve this issue would be very appreciated.
Here's the final configuration I developed, which uses a combination of sinon and mockery:
// Dependencies
var expect = require('chai').expect;
var sinon = require('sinon');
var mockery = require('mockery');
var reloadStub = require('../../../spec/utils/reloadStub');
describe('UNIT: userController.js', function() {
var reportErrorStub;
var controller;
var userModel;
before(function() {
// mock the error reporter
mockery.enable({
warnOnReplace: false,
warnOnUnregistered: false,
useCleanCache: true
});
// load controller and model
controller = require('./userController');
userModel = require('./userModel');
});
after(function() {
// disable mock after tests complete
mockery.disable();
});
describe('#createUser', function() {
var req;
var res;
var status;
var end;
var json;
// Stub `#save` for all these tests
before(function() {
sinon.stub(userModel.prototype, 'save');
});
// Stub out req and res
beforeEach(function() {
req = {
body: {
username: 'Andrew',
userID: 1
}
};
status = sinon.stub();
end = sinon.stub();
json = sinon.stub();
res = { status: status.returns({ end: end, json: json }) };
});
// Reset call count after each test
afterEach(function() {
userModel.prototype.save.reset();
});
// Restore after all tests finish
after(function() {
userModel.prototype.save.restore();
});
it('should call `User.save`', function(done) {
controller.createUser(req, res);
/**
* Since Mongoose's `new` is asynchronous, run our expectations on the
* next cycle of the event loop.
*/
setTimeout(function() {
expect(userModel.prototype.save.callCount).to.equal(1);
done();
}, 0);
});
}
}
Have you tried:
sinon.stub(userModel.prototype, 'save')
Also, where is the helper function getting called in the test? It looks like you define the function as the utils module, but call it as a method of a controller object. I'm assuming this has nothing to do with that error message, but it did make it harder to figure out when and where the stub was getting called.
I am trying to test a component in my Ember application unit tests, and until now all is good except that I am at a point where the assertions need its template to be rendered.
To do so normally one would call
var comp = App.SomeNamedComponent.create();
var comp.appendTo(App.rootElement);
But while this does create the base element of the component, it does not render its template. After a few research, I ended-up finding out that neither templateName nor template properties are set on the component. So I decided to set the templateName myself, but then it complains that A Component must have a parent view in order to yield..
I then decided to create another custom view in the test with a template using that component, but then I can't access the instance of the component...
I need to access the instance to make the assertions, and I need to have it's template rendered as some properties are calculated depending on the css of some elements in the template.
This is how I typically test a component when a container is not needed (specifically when the template and layout are provided to the component programmatically):
Ember.testing = true;
MyAwesomeComponent = Ember.Component.extend();
function createComponent(componentName, factory, options) {
if (typeof options.template === 'string') {
options.template = Ember.Handlebars.compile(options.template);
}
if (typeof options.layout === 'string') {
options.layout = Ember.Handlebars.compile(options.layout);
}
if (options.template && !options.layout) {
options.layout = options.template;
delete options.template;
}
var component = factory.create(options);
Ember.run(function(){
component.appendTo('#qunit-fixture');
});
return component;
}
module('component testing sample');
test('a component with template', function(){
var options = {layout: 'woot woot{{fullName}}'};
var component = createComponent('my-awesome', MyAwesomeComponent, options);
equal(component.$().text(), 'woot woot');
});
test('a component with custom options and a template', function(){
var options = {
fullName: 'Robert Jackson',
layout: '{{fullName}}'
};
var component = createComponent('my-awesome', MyAwesomeComponent, options);
equal(component.$().text(), 'Robert Jackson');
});
See an example JSBin.
If you need/want to be able to lookup the template you can use something like the following (which creates an isolated container):
Ember.testing = true;
MyAwesomeComponent = Ember.Component.extend();
function isolatedContainer() {
var container = new Ember.Container();
container.optionsForType('component', { singleton: false });
container.optionsForType('view', { singleton: false });
container.optionsForType('template', { instantiate: false });
container.optionsForType('helper', { instantiate: false });
return container;
}
function createComponent(componentName, factory, options) {
var fullName = 'component:' + componentName,
templateFullName = 'template:components/' + componentName;
container.register(fullName, factory);
if (container.has(templateFullName)) {
container.injection(fullName, 'layout', templateFullName);
}
var Component = container.lookupFactory(fullName),
component = Component.create(options);
Ember.run(function(){
component.appendTo('#qunit-fixture');
});
return component;
}
function registerTemplate(name, template){
if (typeof template !== 'function') {
template = Ember.Handlebars.compile(template);
}
container.register('template:' + name, template);
}
var container;
module('component testing sample', {
setup: function(){
container = isolatedContainer();
},
teardown: function(){
Ember.run(container, 'destroy');
}
});
test('a component with template', function(){
registerTemplate('components/my-awesome', 'woot woot{{fullName}}');
var component = createComponent('my-awesome', MyAwesomeComponent);
equal(component.$().text(), 'woot woot');
});
test('a component with custom options and a template', function(){
registerTemplate('components/my-awesome', '{{fullName}}');
var component = createComponent('my-awesome', MyAwesomeComponent, {fullName: 'Robert Jackson'});
equal(component.$().text(), 'Robert Jackson');
});
JSBin of the container version.
I have a script that is failing in IE8 because Date.now() is undefined. I have abstracted out Date.now() into a method and want to unit test it do I know it works when Date.now() is undefined.
Foo.prototype = {
date: function() {
// I think this works. Not sure until I get my unit test working...
if (typeof Date.now === 'undefined') {
Date.now = function () { return +new Date(); }
}
return Date.now(); // fails in IE8. Undefined.
}
}
My test goes something like this
describe('Foo', function() {
it('has the current date time', function() {
// This passes
spyOn(Date, 'now').and.returnValue(1234);
foo = new Foo;
expect(foo.date()).toBe(1234);
});
it('has the current date time for IE8', function() {
// This won't pass
spyOn(Date, 'now').and.returnValue(null);
foo = new Foo;
expect(foo.date()).toBe(1234); // TODO
});
});
How do I stub out undefined? I was thinking returning an undefined method in returnValue.
I know my tests aren't polished as I have only just started working with Jasmine.
You can't spy on an undefined function.
you can do something like this:
it('has the current date time for IE8', function() {
// This won't pass
var x = Date.now;
Date.now = undefined;
foo = new Foo;
expect(foo.date()).toBe(1234); // TODO
Date.now = x;
});
The problem is that a spy still behaves like a function, but you're not actually calling the method when you test if it exists. When you spyOn(Date, 'now') you're replacing the built-in Date.now function with a spy object. If you did something like this:
describe('Foo', function() {
describe("without a Date.now", function() {
beforeEach(function() {
this.now = Date.now;
Date.now = undefined;
});
afterEach(function() {
Date.now = this.now;
});
it('has the current date time for IE8', function() {
spyOn(window, 'Date').and.returnValue(1234);
foo = new Foo;
expect(foo.date()).toBe(1234); // TODO
});
});
});
The typeof check will succeed, and your code will set the Date.now function to your new implementation. Since this isn't a jasmine spy, you have to clean up after yourself (hence the afterEach). This spec still doesn't quite pass because it's not stubbing the Date constructor quite right.
I want a computed property to observe a non-ember global: a specific key in localStorage. Is this possible? The following does not seem to cut it:
someProperty:function(){
//some functionality
}.property('localStorage.someKey')
Is it possible to do what I'm trying to do directly?
In general, you can observe regular JavaScript objects just fine. You just need to use Ember.get and Ember.set to modify them:
var pojo = {};
var MyObject = Ember.Object.extend({
bigEyeballs: function() {
var O_O = this.get('pojo.O_O');
if (O_O) { return O_O.toUpperCase(); }
}.property('pojo.O_O')
});
var obj = MyObject.create({ pojo: pojo });
console.log(obj.get('bigEyeballs'));
Ember.set(pojo, 'O_O', "wat");
console.log(obj.get('bigEyeballs'));
You can see this working in this JSBin.
Local Storage is a bit of a different matter, as it's not really a normal JavaScript object. You can create a small Ember wrapper around local storage, and use that for observation:
var LocalStorage = Ember.Object.extend({
unknownProperty: function(key) {
return localStorage[key];
},
setUnknownProperty: function(key, value) {
localStorage[key] = value;
this.notifyPropertyChange(key);
return value;
}
});
var storage = new LocalStorage();
var MyObject = Ember.Object.extend({
bigEyeballs: function() {
var O_O = this.get('pojo.O_O');
if (O_O) { return O_O.toUpperCase(); }
}.property('pojo.O_O')
});
var obj = MyObject.create({ pojo: storage });
console.log(obj.get('bigEyeballs'));
Ember.set(storage, 'O_O', "wat");
console.log(obj.get('bigEyeballs'));
You can see this live on JSBin.
In both cases, the important thing is that you will have to use Ember-aware setting and getting in order to observe these properties.