How to access in `this.owner` as a property in Ember js test? - ember.js

I added an ember adapter with some extra logic, then I would like to test that logic in ember test.
In the test file, I see this line repeated multiple times let adapter = this.owner.lookup('adapter:assessment'); inside each test.
I would like to move that line into a class level attribute for accessing my adapter.
How do I do that?
I think it has something to do with this.owner.lookup is only accessible within test().
(Using ember js 3.2.6)
import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';
module('Unit | Adapter | assessment', function (hooks) {
setupTest(hooks);
let myVar = 'abc';
let adapterVar = this.owner.lookup('adapter:assessment'); // This fails! How to do this correctly?
test('it exists', function (assert) {
let adapter = this.owner.lookup('adapter:assessment');
console.log(myVar); // This works!
assert.ok(adapter);
});
test('Test 1', function (assert) {
let adapter = this.owner.lookup('adapter:assessment');
console.log(myVar); // This works!
console.log(adapterVar); // This fails!
assert.ok(adapter);
});
test('Test 2', function (assert) {
let adapter = this.owner.lookup('adapter:assessment');
console.log(myVar); // This works!
console.log(adapterVar); // This fails!
assert.ok(adapter);
});
test('Test 3', function (assert) {
let adapter = this.owner.lookup('adapter:assessment');
console.log(myVar); // This works!
console.log(adapterVar); // This fails!
assert.ok(adapter);
});
});

How about that:
module('Unit | Adapter | assessment', function (hooks) {
setupTest(hooks);
let adapterVar;
hooks.beforeEach(function () {
adapterVar = this.owner.lookup('adapter:assessment')
});
test('Test 1', function (assert) {
console.log(adapterVar);
assert.ok(adapterVar);
});
});

Related

Testing if a record and it’s relations have been destroyed

I've created an Ember service with a method to destroy a record and it's related data (defined by relationships in the model).
This is my delete-user.js service.
import Promise from 'rsvp';
import Service, { inject as service } from '#ember/service';
export default Service.extend({
store: service(),
deleteUserAndAssociatedData(userRecord) {
return userRecord.get('posts')
.then(postRecords => this._deletePosts(postRecords))
.then(() => userRecord.destroyRecord());
},
_deletePosts(postRecords) {
return Promise
.all(postRecords.invoke('destroyRecord'));
}
});
My question is how am I supposed to test this functionality? I would like to verify that the user and it's posts have been marked as destroyed.
This is what I have so far in delete-user-test.js:
import { moduleFor, test } from 'ember-qunit';
import { run } from '#ember/runloop';
import { A } from '#ember/array';
import EmberObject from '#ember/object';
moduleFor('service:delete-user', 'Integration | Service | delete-user', {
integration: true
});
let generateDummyUser = container => {
const store = container.lookup('service:store');
return run(() => {
return store.createRecord('user', { name: 'Dummy User'});
});
};
let generateDummyPost = container => {
const store = container.lookup('service:store');
return run(() => {
return store.createRecord('post');
});
};
test('it exists', function (assert) {
let service = this.subject();
assert.ok(service);
});
test('should delete the user', function (assert) {
let service = this.subject();
run(() => {
const userRecord = generateDummyUser(this.container);
service.deleteUserAndAssociatedData(userRecord);
assert.equal(userRecord.get('isDeleted'), true, 'the user is deleted');
});
});
test('should delete posts of the user', function (assert) {
let service = this.subject();
run(() => {
const userRecord = generateDummyUser(this.container);
const postRecord = generateDummyPost(this.container);
userRecord.get('posts').pushObject(postRecord);
return run(() => {
service.deleteUserAndAssociatedData(userRecord);
assert.equal(postRecord.get('isDeleted'), true, 'the post is deleted');
})
});
});
But the tests fail on the first assertion already with the following error:
Assertion Failed: calling set on destroyed object: .isSynced = false"
Is there anyone who can point me in the right direction? Maybe I should stub the adapter? Spy on the destroyRecord method?

run initializer before ember unit test

I want to write a unit test for a route's method.
routes/tickets
addTicketUserAssoc(ticket, ticketUserAssoc) {
let copy = ticketUserAssoc.copy();
copy.set('ticket', ticket);
ticketUserAssoc.reset();
},
It uses copy and reset on an ember-data record. They are methods which are added during initialization.
initializers/model
export default {
name: 'model',
initialize: function() {
if (alreadyRun) {
return;
} else {
alreadyRun = true;
}
DS.Model.reopen(isValidated, {
copy: function(options){
// some code ...
},
reset() {
// some code ...
}
});
}
};
If I try to import the initializer to the unit test, it does not even appears on the qunit's module list.
Solution
I ended up doing this:
moduleFor('route:tickets', 'Unit | Route | tickets', {
// Specify the other units that are required for this test.
needs: [
// ...
],
beforeEach() {
Ember.run(function() {
application = Ember.Application.create();
application.deferReadiness();
});
}
});
test('assign ticket', function(assert){
let route = this.subject();
let store = route.get('store');
ModelInitializer.initialize(application);
// ...
})

how to call variables scoped in describe constructor in jasmine

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...
});
});
});

Testing asynchrone function gives Unexpected request

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.

Faking a Angular Factory in a directive in jasmine

Question: How do I fake my pointFactory so I can Jasmine Unit Test it.
I have the Following Directive.
It takes the html sends it to a factory and the uses the response for some logic
CommonDirectives.directive('TextEnrichment',['PointFactory','appSettings', function (pointFactory,settings) {
return {
restrict: 'A',
link : function (scope, element, attrs) {
var text = element.html();
pointFactory.getPoints(text).then(function(response){
})}}}]);
So far my unit tests looks like this, however it doesn't work since I'm not injecting the factory.
beforeEach(module('app.common.directives'));
beforeEach(function () {
fakeFactory = {
getPoints: function () {
deferred = q.defer();
deferred.resolve({data:
[{"Text":"Some text"}]
});
return deferred.promise;
}
};
getPointsSpy = spyOn(fakeFactory, 'getPoints')
getPointsSpy.andCallThrough();
});
beforeEach(inject(function(_$compile_, _$rootScope_,_$controller_){
$compile = _$compile_;
$rootScope = _$rootScope_;
}));
it('Factory to have been Called', function () {
var element = $compile('<div data-text-enrichment=""> Text </div>')($rootScope)
expect(getPointsSpy.callCount).toBe('1');
});
Update
Following advice from Felipe Skinner I have updated the test with the following
beforeEach(function(){
module(function($provide){
$provide.factory('PointFactory',getPointsSpy)
})
});
However I get the following error:
TypeError: 'undefined' is not a function (evaluating
'pointFactory.getPoints(text)')
You can use the $provide to inject your controller dependencies.
Here's my beforeEach for example:
describe('MyCtrl', function() {
var $controller,
$scope,
$httpBackend,
windowMock,
registerHtmlServiceMock,
mixPanelServiceMock,
toastMock;
beforeEach(function() {
windowMock = { navigator: {} };
registerHtmlServiceMock = {};
mixPanelServiceMock = jasmine.createSpyObj('mixpanel', ['track']);
toastMock = jasmine.createSpyObj('toast', ['error']);
module('myModule');
module(function($provide) {
$provide.value('$window', windowMock);
$provide.value('RegisterHtmlService', registerHtmlServiceMock);
$provide.value('MixPanelService', mixPanelServiceMock);
$provide.value('ToastService', toastMock);
});
inject(function(_$controller_, _$rootScope_, _$httpBackend_) {
$scope = _$rootScope_.$new();
$controller = _$controller_('CourseSelectionCtrl', { $scope: $scope });
$httpBackend = _$httpBackend_;
});
});
// my test cases
});
I haven't tried mocking a function that returns some value. Those two mocks (mixpanel-track and toast-error) are for "void" functions.
UPDATE:
Try changing the previous $provide with this type of injection then.
Change from this:
module(function($provide) {
$provide.value('$window', windowMock);
$provide.value('RegisterHtmlService', registerHtmlServiceMock);
$provide.value('MixPanelService', mixPanelServiceMock);
});
inject(function(_$controller_, _$rootScope_, _$httpBackend_) {
$scope = _$rootScope_.$new();
$controller = _$controller_('CourseSelectionCtrl', { $scope: $scope });
$httpBackend = _$httpBackend_;
});
To this:
beforeEach(inject(function(_$controller_, _$rootScope_, _$httpBackend_) {
mixPanelService = mixPanelServiceMock;
$scope = _$rootScope_.$new();
$controller = _$controller_('MyCtrl', { $scope: $scope, MixPanelService: mixPanelService });
$httpBackend = _$httpBackend_;
}));
The rest of the code should be the same, except for that. Let me know if this works