Testing Ember (v1.0.0-rc.3) nested controllers using Mocha and Chai - ember.js

I am trying to write test cases for controllers of an Ember (v1.0.0-rc.3) Application using Mocha and Chai. One of my controller is making use of another controller as follows
App.ABCController = Em.ObjectController.extend({
needs: ['application'],
welcomeMSG: function () {
return 'Hi, ' + this.get('controllers.application.name');
}.property(),
...
});
I wrote testCase as below:
describe 'ABCController', ->
expect = chai.expect
App = require '../support/setup'
abcController = null
before ->
App.reset()
ApplicationController = require 'controllers/application_controller'
ABCController = require 'controllers/abc_controller'
applicationController = ApplicationController.create()
abcController = ABCController.create()
describe '#welcomeMSG', ->
it 'should return Hi, \'user\'.', ->
msg = abcController.get('welcomeMSG')
expect(msg).to.be.equal('Hi, '+ applicationController.get('name'))
support/setup file is as follows
Em.testing = true
App = null
Em.run ->
App = Em.Application.create()
module.exports = App
Now whenever i try to run testcase i face error
"Before all" hook:
TypeError: Cannot call method 'has' of null
at verifyDependencies (http://localhost:3333/test/scripts/ember.js:27124:20)
at Ember.ControllerMixin.reopen.init (http://localhost:3333/test/scripts/ember.js:27141:9)
at superWrapper [as init] (http://localhost:3333/test/scripts/ember.js:1044:16)
at new Class (http://localhost:3333/test/scripts/ember.js:10632:15)
at Function.Mixin.create.create (http://localhost:3333/test/scripts/ember.js:10930:12)
at Function.Ember.ObjectProxy.reopenClass.create (http://localhost:3333/test/scripts/ember.js:11756:24)
at Function.superWrapper (http://localhost:3333/test/scripts/ember.js:1044:16)
at Context.eval (test/controllers/abc_controller_test.coffee:14:47)
at Hook.Runnable.run (test/vendor/scripts/mocha-1.8.2.js:4048:32)
at next (test/vendor/scripts/mocha-1.8.2.js:4298:10)
Please help me to resolve this issue. I will appreciate if someone provide me few links where i can study for latest ember.js application testing with mocha and chai.

Reading trough the docs I guess your problem is that your expect fails due to this to.be.equal. Try changing the assertion chain to this:
expect(msg).to.equal('Hi, '+ applicationController.get('name'))
Update
After reading your comment I guess the problem in your case is that when you do Ember.testing = true is equivalent to the before needed App.deferReadiness(), so it' obviously necessary to 'initialize' your App before using it, this is done with App.initialize() inside your global before hook. And lastly you call App.reset() inside your beforeEach hook's.
Please check also this blog post for more info on the update that introduced this change.
Hope it helps

Related

Stencil, Leaflet, unit testing component, gives TypeError: Cannot read property 'deviceXDPI' of undefined

So we are developing a Stenciljs component which wraps leaflet map and adds some additional functionality.
Now obviously we don't want or need to test Leaflet, but instead, just the parts in our wrapper components.
So, using the test examples, we create our tests,
import { LeMap } from "./le-map";
describe("Map component tests", () => {
it("Should build the map component", async () => {
const map = new LeMap();
expect(map).not.toBeNull();
});
});
try and load the components and test the public functions, but we get
TypeError: Cannot read property 'deviceXDPI' of undefined
> 1 | import {Component, Element, Listen, Method, Prop, Watch} from
'#stencil/core';
> 2 | import L from 'leaflet';
| ^
3 |
4 | #Component({
5 | shadow: false,
We believe this message is because the test is trying to render leaflet, and because it's not a true browser, it can't detect a view so throwing this error, so we've tried to mock leaflet in the tests, but still get the same problem.
We're tried to mock the leaflet module by using jest mocking
jest.genMockFromModule('leaflet');
but this made no diffrence
Only idea I've had is to separate the logic from the components, but that feels wrong, as we'd just be doing this for purpose of testing.
Versions in use are: leaflet: 1.3.4, #stencil: 0.15.2, jest: 23.4.2
Any other suggestions?
Further investigation with, thanks to #skyboyer 's suggestions, leads me to this line of the leaflet core browser.js file
leads me to this line of the leaflet core browser.js file
export var retina = (window.devicePixelRatio || (window.screen.deviceXDPI/window.screen.logicalXDPI)) > 1;
But I'm unable to mock the screen property of window as I get the following error
[ts] Cannot assign to 'screen' because it is a constant or a read-only property,
so I try the following.
const screen = {
deviceXDPI:0,
logicalXDPI:0
}
Object.defineProperty(window, 'screen', screen);
Object.defineProperty(window, 'devicePixelRatio', 0);
Same error, completely ignores this, so I try over riding the export.
jest.spyOn(L.Browser,'retina').mockImplementation(() => false);
No joy either, so tried
L.Browser.retina = jest.fn(() => false);
but get it tells me it's a constant and can't be changed (yet the implication stats var so ¯_(ツ)_/¯ )
Anything else I can try?
Further update,
I've managed to mock the window, but this sadly doesn't solve it.
const screenMock = {
deviceXDPI: 0,
logicalXDPI: 0
}
const windowMock = {
value: {
'devicePixelRatio': 0,
'screen': screenMock
}
}
Object.defineProperty(global, 'window', windowMock);
If I console log this, I get the right properties but as soon as I test the instantiation of the component it fails with
TypeError: Cannot read property 'deviceXDPI' of undefined
Reading around it seems Leaflet doesn't check for a DOM and just tries to render anyway, I can't see anyway around this, I saw a leaflet-headless package, but I don't know how I could swap them out just for testing.
Think I will need to look at another strategy for testing, probably protractor.
Found a solution, not fully tested yet, but the tests pass.
I did it by creating a
__mocks__
directory at the same level as the node_modules directory.
created a file called leaflet.js in it. It's a simple file it just contains.
'use strict';
const leaflet = jest.fn();
module.exports = leaflet;
then in my test file (le-map.spec.ts) I just added
jest.mock('leaflet')
before the imports
and now my test passes.
I tried doing this in the test itself but that just gave me the same error, it must be something in the loading sequence which means it has to be manually mocked beforehand.
Hope this helps others, it's been driving me mad for weeks.

Handling typeError through Ember.onerror

I would like to be able to be able to handle javascript errors in my Ember application and display a generic modal defined in my application's templates. I'm able to process errors by defining a function for Ember.onerror, but haven't been able to find a way to trigger an event or action against my application for certain error types, for instance a TypeError.
Below is a sample of how I've approached defining Ember.onerror
App.report_errors = (error) ->
console.log "error", error
# Would like to be able to use something like the below line
# to call an action on the application route
#send "showError"
# Log to api
Em.onerror = App.report_errors
Here is a full example fiddle illustrating what I would like to accomplish: http://jsfiddle.net/mandrakus/c8E3x/1/
Thanks!
This solution (courtesy Alex Speller) adds an errorReporter object which is injected during initialization with the ability to access the router and therefore router actions.
App.initializer
name: 'errorReporter'
initialize: (container) ->
container.injection 'reporter:error', 'router', 'router:main'
container.injection 'route', 'errorReporter', 'reporter:error'
reporter = container.lookup 'reporter:error'
Em.onerror = (error) ->
reporter.report error
App.ErrorReporter = Em.Object.extend
report: (error) ->
console.log "error", error
#Would like to be able to use something like the below line
#to call an action on the application route
#router.send "showError"
App.ApplicationRoute = Ember.Route.extend
actions:
error: (error) -> #errorReporter.report error
showError: ->
console.log "displaying error"
#the final application generate a modal or other notification
alert "Generic Error Message"
How about this?
App.report_errors = function (error) {
App.__container__.lookup("route:application").send("showError", error);
};
Two potential problems:
Using global App object. Not a big deal, unless you're trying to host two ember apps side-by-side
Using the hidden container property. This might change name/location in the future, but as I see it used all over the place, I doubt Ember team will remove it completely.

Unit testing a directive that defines a controller in AngularJS

I'm trying to test a directive using Karma and Jasmine that does a couple of things. First being that it uses a templateUrl and second that it defines a controller. This may not be the correct terminology, but it creates a controller in its declaration. The Angular application is set up so that each unit is contained within its own module. For example, all directives are included within module app.directive, all controllers are contained within app.controller, and all services are contained within app.service etc.
To complicate things further, the controller being defined within this directive has a single dependency and it contains a function that makes an $http request to set a value on the $scope. I know that I can mock this dependency using $httpBackend mock to simulate the $http call and return the proper object to the call of this function. I've done this numerous times on the other unit tests that I've created, and have a pretty good grasp on this concept.
The code below is written in CoffeeScript.
Here is my directive:
angular.module('app.directive')
.directive 'exampleDirective', [() ->
restrict: 'A'
templateUrl: 'partials/view.html'
scope: true
controller: ['$scope', 'Service', ($scope, Service) ->
$scope.model = {}
$scope.model.value_one = 1
# Call the dependency
Service.getValue()
.success (data) ->
$scope.model.value_two = data
.error ->
$scope.model.value_two = 0
]
]
Here is the dependency service:
angular.module("app.service")
.factory 'Service', ['$http', ($http) ->
getValue: () ->
options.method = "GET"
options.url = "example/fetch"
$http _.defaults(options)
Here is the view:
<div>
{{model.value_one}} {{model.value_two}}
</div>
I've simplified this quite a bit, as my goal is only to understand how to wire this up, I can take it from there. The reason I'm structuring it this way is because I did not initially create this. I'm working on writing tests for an existing project and I don't have the ability to configure it any other way. I've made an attempt to write the test, but cannot get it to do what i want.
I want to test to see if the values are being bound to the view, and if possible to also test to see if the controller is creating the values properly.
Here is what I've got:
'use strict'
describe "the exampleDirective Directive", ->
beforeEach module("app.directive")
beforeEach module("app/partials/view.html")
ServiceMock = {
getValue : () ->
options.method = "GET"
options.url = "example/fetch"
$http _.defaults(options)
}
#use the mock instead of the service
beforeEach module ($provide) ->
$provide.value "Service", ServiceMock
return
$httpBackend = null
scope = null
elem = null
beforeEach inject ($compile, $rootScope, $injector) ->
# get httpBackend object
$httpBackend = $injector.get("$httpBackend")
$httpBackend.whenGET("example/fetch").respond(200, "it works")
#set up the scope
scope = $rootScope
#create and compile directive
elem = angular.element('<example-directive></example-directive>')
$compile(elem)(scope)
scope.$digest()
I don't know how close I am, or if this is even correct. I want to be able to assert that the values are bound to the view correctly. I've used Vojtajina's example to set up html2js in my karma.js file to allow me to grab the views. I've done a lot of research to find the answer, but I need some help. Hopefully a programmer wiser than I can point me in the right direction. Thank you.
Create the element in karma, then use the .controller() function with the name of your directive to grab the controller. For your example, replace the last couple of lines with these:
elem = angular.element('<div example-directive></div>');
$compile(elem)($rootScope);
var controller = elem.controller('exampleDirective');
Note, that given how you defined your directive, it should be by attribute, and not as an element. I'm also not 100% sure, but I don't think you need the scope.$digest; usually I just put anything that needs to be applied into a scope.$apply(function() {}) block.

MVCContrib TestHelper and User.Identity.Name, Server.MapPath and Form Collection

I am new to MVCContrib Testhelper and mocking with Rhino.
I am needing assistance with unit testing a controller which relies on User.Identity.Name, Server.MapPath and Form Collection.
I started off with
var controller = new SubmitController();
var builder = new TestControllerBuilder();
builder.InitializeController(controller);
I found this post for setting User.Identity.Name
controller.ControllerContext = TestHelper.MockControllerContext(controller).WithAuthenticatedUser("domain\\username");
At this point, in my controller i am now able to get to the User.Identity. The problem then became how to i set Form Collection variables. Setting
builder.Form.Add("testvar","1");
no longer worked. It seemed that now I had to access via
controller.HttpContext.Request.Form.Add("testvar","1)
This seemed to work, but at this point, i was no longer using builder(TestControllerBuilder) above.
I then had to mock Server which raised up more issues. How can I continue to use builder but use mocks or stubs for httpContext, HttpRequest, Server etc. I was sort of expecting that builder would have methods for setting expected values for HttpRequest, Server etc.
Thanks
When you replaced the controller's ControllerContext that removed the MVCContrib context. Try something like this:
using MvcContrib.TestHelper;
using MvcContrib.TestHelper.Fakes;
using Rhino.Mocks;
...
var builder = new TestControllerBuilder();
builder.Form.Add("testvar", "1");
builder.HttpContext.User = new FakePrincipal(new FakeIdentity("UserName"), new string[] { "Role" });
builder.HttpContext.Server.Stub(x => x.MapPath(Arg<string>.Is.Anything)).Return("Value");
builder.InitializeController(controller);

AngularJS - basic testing with injection

So I'm new to the whole testing thing (I've been one of those people who has said 'I should write unit tests...' but never ended up ever doing it :-p).
I'm now writing unit tests for this project.  I'm using testacular + Jasmine, with browserify to compile things.  I was having no problems until I started trying to do a lot AngularJS injection-stuff.
Right now I'm simply trying to do a test of ng-model to get my head around all of it.
I have a testacular.conf file which includes everything necessary:
files = [
'../lib/jquery.js',
'../lib/angular.js',
'./lib/jasmine.js',
'./lib/angular-mocks.js',
JASMINE_ADAPTER,
'./tests.js' //compiled by browserify
];
I have my controller defined (MainCtrl.coffee)
MainCtrl = ($scope, $rootScope) ->
$scope.hello = 'initial'
module.exports = (angularModule) ->
angularModule.controller 'MainCtrl', ['$scope', '$rootScope', MainCtrl]
return MainCtrl
And I have my test itself: (_MainCtrlTest.coffee, in same directory as MainCtrl.coffee)
testModule = angular.module 'MainCtrlTest', []
MainCtrl = require('./MainCtrl')(testModule)
describe 'MainCtrlTest', ->
scope = null
elm = null
ctrl = null
beforeEach inject ($rootScope, $compile, $controller) ->
scope = $rootScope.$new()
ctrl = $controller MainCtrl, $scope: scope
elm = $compile('<input ng-model="hello"/>')(scope)
describe 'value $scope.hello', ->
it 'should initially equal input value', ->
expect(elm.val()).toBe scope.hello
it 'should change when input value changes', ->
scope.$apply -> elm.val('changedValue')
expect(scope.hello).toBe elm.val()
The test fails immediately, with the input's elm.val() returning blank, and scope.hello returning the intended value ('initial', set in MainCtrl.coffee)
What am I doing wrong here?
To get this working, you need to do scope.$apply():
it 'should initially equal input value', ->
scope.$apply()
expect(elm.val()).toBe scope.hello
Don't test the framework, test your code
Your test is trying to test whether Angular's binding, and ng-model works. You should rather trust the framework and test your code instead.
Your code is:
the controller (setting initial scope.hello value)
html templates (and all the binding, directives in there)
You can test the first one very easily, without even touching any DOM. That's the beauty of AngularJS - strong separation of view/logic.
In this controller, there is almost nothing to test, but the initial value:
it 'should init hello', ->
expect(scope.hello).toBe 'initial'
To test the second one (template + binding), you want to do an e2e test. You basically want to test, whether the template doesn't contain any typos in binding etc... So you wanna test the real template. If you inline a different html during the test, you are testing nothing but AngularJS.