How do I run this in the same test? Right now I need to run them separately.
test('Props tests', function () {
var controller = this.subject({
model: createMock()
});
controller.set('foo', false);
controller.set('foo2', false);
equal(controller.get('baa'), true);
});
test('Props tests', function () {
var controller = this.subject({
model: createMock()
});
controller.set('foo', true);
controller.set('foo2', true);
equal(controller.get('baa2'), true);
});
I think I need to wrap some code in an Ember.run function statement.
In my experience, you should wrap your controller.sets in Ember.run.
Ember.run(function() {
controller.set('foo', false);
controller.set('foo2', false);
});
equal(controller.get('baa'), true);
You should be able to have consecutive equal assertions as long as you don't change the state of the subject (without using Ember.run).
Related
I've run into a weird problem writing integration tests for my component. When I run each one individually, they pass. When I run multiple, the first one passes and the other ones fail. I think it has something to do with closure actions but I don't know.
Here's my component code
// components/game-nav-key.js
triggerKeyAction(code) {
if (this.get('prevKeyCode').contains(code)) {
this.sendAction('onPrevKey', true);
} else if (this.get('nextKeyCode').contains(code)) {
this.sendAction('onNextKey', true);
} else if (this.get('openKeyCode').contains(code)) {
this.sendAction('onOpenKey');
}
},
didInsertElement() {
var self = this;
Ember.$('body').keydown(function(e) {
self.triggerKeyAction(e.which);
});
Ember.$('body').keyup(function(e) {
});
}
And my tests
// game-nav-key-test.js
it('tracks key commands and sends an action for K', function() {
let spy = sinon.spy();
this.set('gotoPrev', spy);
this.render(hbs`
{{game-nav-key onPrevKey=(action gotoPrev)}}
`);
triggerKeydown($('body'), 75);
triggerKeyup($('body'), 75);
sinon.assert.calledOnce(spy);
sinon.assert.calledWith(spy, true);
});
it('tracks key commands and sends an action for J', function() {
let spy = sinon.spy();
this.set('gotoNext', spy);
this.render(hbs`
{{game-nav-key onNextKey=(action gotoNext)}}
`);
triggerKeydown($('body'), 74);
triggerKeyup($('body'), 74);
sinon.assert.calledOnce(spy);
sinon.assert.calledWith(spy, true);
});
it('tracks key commands and sends an action for R', function() {
let spy = sinon.spy();
this.set('open', spy);
this.render(hbs`
{{game-nav-key onOpenKey=(action open)}}
`);
triggerKeydown($('body'), 82);
triggerKeyup($('body'), 82);
sinon.assert.calledOnce(spy);
});
I removed all beforeEach's, so it's literally just those three tests. Like I said, each one passes individually, and when it is listed first, but the second two fail when run together. Note that using console.log statements I have verified that the code hits the line directly above each of the this.sendAction calls in their respective tests
seems you need to destroy your listeners created in didInsertElement
willDestroyElement() {
Ember.$('body').off('keydown');
Ember.$('body').off('keyup');
}
I am trying to write a test for an action handler on one of my components. I am stubbing out the save method on one of my models to return a resolved promise using Em.RSVP.Promise.resolve()
in my component, i chain on that promise using then:
return target
.save()
.then(function(){
selected.rollback();
this.sendAction('quicklinkChanged', target);
}.bind(this),this.notify_user_of_persistence_error.bind(this, 'Save As'));
this is a pattern that i use a lot server-side where we use when for our promise library. however, when i do this client-side, i never end up inside the function in the then block so i cannot assert any of the functionality there in my unit tests.
can anyone provide any insight on the best way to do this?
We moved our callbacks out of the method so we could call them separately and verify functionality, or replace them and verify they were called.
Controller Example:
App.IndexController = Em.Controller.extend({
randomProperty: 1,
async: function(fail){
return new Em.RSVP.Promise(function(resolve, reject){
if(fail){
reject('fdas');
}else{
resolve('foo');
}
});
},
doAsyncThing: function(fail){
return this.async(fail).then(this.success.bind(this), this.failure.bind(this));
},
success: function(){
this.set('randomProperty', 2);
},
failure: function(){
this.set('randomProperty', -2);
}
});
Tests
test("async success", function(){
var ic = App.IndexController.createWithMixins();
stop();
ic.doAsyncThing(false).then(function(){
start();
equal(ic.get('randomProperty'), 2);
});
});
test("async fail", function(){
var ic = App.IndexController.createWithMixins();
stop();
ic.doAsyncThing(true).then(function(){
start();
equal(ic.get('randomProperty'), -2);
});
});
test("async success is called", function(){
expect(1);
var ic = App.IndexController.createWithMixins();
ic.success = function(){
ok(true);
};
stop();
ic.doAsyncThing(false).then(function(){
start();
});
});
test("async failure is called", function(){
expect(1);
var ic = App.IndexController.createWithMixins();
ic.failure = function(){
ok(true);
};
stop();
ic.doAsyncThing(true).then(function(){
start();
});
});
test("doAsyncThing returns a promise", function(){
expect(1);
var ic = App.IndexController.createWithMixins();
ok(ic.doAsyncThing(true).then);
});
Example: http://emberjs.jsbin.com/wipo/37/edit
My code looks something like this
App.ItemRoute = Em.Route.extend({
setupController: function(controller) {
var model = this.modelFor('item');
controller.setProperties({
name : model.get('name'),
title: model.get('title')
});
}
});
App.ItemController = Em.ObjectController.extend({
saveOnChange: function() {
console.log('saveOnChange');
}.observes('name', 'title'),
});
From my understanding because i am using setProperties the observe should only fire once , but it fire two times
also wrapping the setProperties with beginPropertyChanges & endPropertyChanges still fires twice
what i ultimately is for it to not fire at all, so what i ended up doing was changing the controller code to be like this
App.ItemController = Em.ObjectController.extend({
load: false,
saveOnChange: function() {
if(!this.get('load')) {
this.set('load', true);
return;
}
console.log('saveOnChange');
}.observes('name', 'title'),
});
this code would work if the change is only fired once, but it won't work if its fired multiple times (that's my case)
The setProperties function doesn't coalesce your observers (unfortunately there's no way to do that), it just groups them into one operation. The source might help you to better see what it does:
Ember.setProperties = function(self, hash) {
changeProperties(function() {
for(var prop in hash) {
if (hash.hasOwnProperty(prop)) { set(self, prop, hash[prop]); }
}
});
return self;
};
So, back to your problem. The best way that I can think of is to debounce your function.
App.ItemController = Em.ObjecController.extend({
load: false,
saveOnChange: function() {
Em.run(this, 'debouncedSave', 150);
}.observes('name', 'title'),
debouncedSave: function() {
if(!this.get('load')) {
this.set('load', true);
}
}
});
If you're not familiar with debouncing, you can read about it here. There are probably some other solutions involving direct manipulation of the properties, but I'm not sure if that's a road you want to go down.
I am using jasmine runner to test angular code.
describe('des1', function() {
var des1Var = function(){};
beforeEach() {
//....
}
describe('test1', function() {
var scope4Compile = $rootScope.$new();
var des2Var = des1Var(scope4Compile); // returns undefined.
beforeEach(function() {
des2Var = des1Var(scope4Compile); // returns des1Var() fine;
})
it('should do ', function(){
//should do...
})
it('should also do', function(){
//should also do...
})
})
})
I need to instantiate something once before the it statements, if run multiple times result is pretty bad. How can I get it done properly?
I believe it you call it once in the first beforeEach it will be run one time for each describe that is below it.
In the code below, des2Var will be set once for the whole test1 describe.
describe('des1', function() {
var des1Var = function () { };
beforeEach(function () {
var des2Var = des1Var();
});
describe('test1', function() {
it('should do ', function(){
//should do...
});
it('should also do', function(){
//should also do...
});
});
});
The unittest:
"use strict";
var usersJSON = {};
describe("mainT", function () {
var ctrl, scope, httpBackend, locationMock,
beforeEach(module("testK"));
beforeEach(inject(function ($controller, $rootScope, $httpBackend, $location, $injector) {
scope = $rootScope.$new();
httpBackend = $httpBackend;
locationMock = $location;
var lUrl = "../solr/users/select?indent=true&wt=json",
lRequestHandler = httpBackend.expect("GET", lUrl);
lRequestHandler.respond(200, usersJSON);
ctrl = $controller("mainT.controller.users", { $scope: scope, $location: locationMock});
httpBackend.flush();
expect(scope.users).toBeDefined();
}));
afterEach(function () {
httpBackend.verifyNoOutstandingRequest();
httpBackend.verifyNoOutstandingExpectation();
});
describe("method test", function () {
it('should test', function () {
expect(true).toBeFalsy();
});
});
});
controller I'm testing (working):
Asynchrone function in init who's giving me trouble (uses ../solr/users/select?indent=true&wt=json):
$scope.search = function () {
var lStart = 0,
lLimit = privates.page * privates.limit;
Search.get({
collection: "users",
start: lStart,
rows: lLimit)
}, function(records){
$scope.users= records.response.docs;
});
};
What I think happens:
1. inform backend what request he will receive
2. inform backend to response on that request with empty JSON
3. create a controller (Search.get get's executed)
4. inform backend to receive all requests and answer them (flush)
Yet I always get the following error:
Error: Unexpected request: GET : ../solr/users/select?indent=true&wt=json
Am I not handling the asynchrone search function well? how should this be done?
That's not really a "unit" test, it's more of a behavioral test.
This should really be a few tests:
Test your service Search.get to make sure it's calling the proper URL and returning the result.
Test your controller method to make sure it's calling Search.get
Test your controller method to make sure it's putting the result in the proper spot.
The code you've posted is a little incomplete, but here are two unit tests that should cover you:
This is something I've blogged about extensively, and the entries go into more detail:
Unit Testing Angular Controllers
Unit Testing Angular Services
Here's an example of what I'm talking about:
describe('Search', function () {
var Search,
$httpBackend;
beforeEach(function () {
module('myModule');
inject(function (_Search_, _$httpBackend_) {
Search = _Search_;
$httpBackend = _$httpBackend_;
});
});
describe('get()', function () {
var mockResult;
it('should call the proper url and return a promise with the data.', function () {
mockResult = { foo: 'bar' };
$httpBackend.expectGET('http://sample.com/url/here').respond(mockResult);
var resultOut,
handler = jasmine.createSpy('result handler');
Search.get({ arg1: 'wee' }).then(handler);
$httpBackend.flush();
expect(handler).toHaveBeenCalledWith(mockResult);
$httpBackend.verifyNoOutstandingRequest();
$httpBackend.verifyNoOutstandingExpectation();
});
});
});
describe('myCtrl', function () {
var myCtrl,
$scope,
Search;
beforeEach(function () {
module('myModule');
inject(function ($rootScope, $controller, _Search_) {
$scope = $rootScope.$new();
Search = _Search;
myCtrl = $controller('MyCtrl', {
$scope: scope
});
});
});
describe('$scope.foo()', function () {
var mockResult = { foo: 'bar' };
beforeEach(function () {
//set up a spy.
spyOn(Search, 'get').andReturn({
then: function (fn) {
// this is going to execute your handler and do whatever
// you've programmed it to do.. like $scope.results = data; or
// something.
fn(mockResult);
}
});
$scope.foo();
});
it('should call Search.get().', function () {
expect(Search.get).toHaveBeenCalled();
});
it('should set $scope.results with the results returned from Search.get', function () {
expect(Search.results).toBe(mockResult);
});
});
});
In a BeforeEach you should use httpBackend.when instead of httpBackend.expect. I don't think you should have an assertion (expect) in your BeforeEach, so that should be moved to a separate it() block. I also don't see where lRequestHandler is defined. The 200 status is sent by default so that is not needed. Your httpBackend line should look like this:
httpBackend.when("GET", "/solr/users/select?indent=true&wt=json").respond({});
Your test should then be:
describe("method test", function () {
it('scope.user should be defined: ', function () {
expect(scope.user).toEqual({});
});
});
Your lUrl in the unit test, shouldn't be a relative path, i.e., instead of "../solr/users/select?indent=true&wt=json" it should be an absolute "/solr/users/select?indent=true&wt=json". So if your application is running at "http://localhost/a/b/index.html", lUrl should be "/a/solr/...".
Note that you can also use regular expressions in $httpBackend.expectGET(), that could be helpful here in case you are not entirely sure how the absolute path will look like later on.