angular, karma dependency injection, inject failing - angularjs

So, I'm trying to get to grips with testing angular, and I'm a bit stuck... From what I've read (or what I've understood from what I've read) the below should work, but I'm getting the following error:
Error: [ng:areq] Argument 'fn' is not a function, got Object
http://errors.angularjs.org/1.2.26/ng/areq?p0=fn&p1=not%20a%20function%2C%20got%20Object
app = angular.module("MyApp", ["ngMock"])
myService = null
angular.module("MyApp").factory "myDependency", () ->
getSomething: ->
"awesome"
angular.module("MyApp").factory "myService", (myDependency) ->
useDependency: ->
myDependency.getSomething()
describe "myService", ->
beforeEach ->
module "MyApp", ($provide) ->
mockDependency =
getSomething: ->
"mockReturnValue"
console.log "providing"
$provide.value("myDependency", mockDependency)
inject (_myService_) ->
console.log "injecting"
myService = _myService_
it "is there", ->
expect(myService).not.toBeNull()
expect(myService.useDependency()).toEq("mockReturnValue")
Also its worth saying that "provider" appears in the log, but "injecting" doesn't

Aha! I've figured it out!!!
module "MyApp", ($provide) ->
mockDependency =
getSomething: ->
"mockReturnValue"
console.log "providing"
$provide.value("myDependency", mockDependency)
return
This fixes the error! I'm assuming if a module returns something, it must be of a certain type. If its null, angular ignores it. Awesome!

Related

How can I obfuscate AngularJS codes?

How can I obfuscate AngularJS codes?
We have tried gulp-obfuscate, but it does not work. Anyone can help us? Thanks a lot!
We have done like this
someModule.controller('MyController',[ '$scope', function($scope) {",
but after success to get the obfuscated codes like this
function H͇̬͔̳̖̅̒ͥͧẸ̖͇͈͍̱̭̌͂͆͊_C͈OM̱̈́͛̈ͩ͐͊ͦEͨ̓̐S̬̘͍͕͔͊̆̑̈́̅4() {
var H͇̬͔̳̖̅̒ͥͧẸ̖͇͈͍̱̭̌͂͆͊_C͈OM̱̈́͛̈ͩ͐͊ͦEͨ̓̐S̬̘͍͕͔͊̆̑̈́̅1, H͇̬͔̳̖̅̒ͥͧẸ̖͇͈͍̱̭̌͂͆͊_C͈OM̱̈́͛̈ͩ͐͊ͦEͨ̓̐S̬̘͍͕͔͊̆̑̈́̅2, H͇̬͔̳̖̅̒ͥͧẸ̖͇͈͍̱̭̌͂͆͊_C͈OM̱̈́͛̈ͩ͐͊ͦEͨ̓̐S̬̘͍͕͔͊̆̑̈́̅3;
...
H͇̬͔̳̖̅̒ͥͧẸ̖͇͈͍̱̭̌͂͆͊_C͈OM̱̈́͛̈ͩ͐͊ͦEͨ̓̐S̬̘͍͕͔͊̆̑̈́̅3 = H͇̬͔̳̖̅̒ͥͧẸ̖͇͈͍̱̭̌͂͆͊_C͈OM̱̈́͛̈ͩ͐͊ͦEͨ̓̐S̬̘͍͕͔͊̆̑̈́̅1 + H͇̬͔̳̖̅̒ͥͧẸ̖͇͈͍̱̭̌͂͆͊_C͈OM̱̈́͛̈ͩ͐͊ͦEͨ̓̐S̬̘͍͕͔͊̆̑̈́̅2;
return H͇̬͔̳̖̅̒ͥͧẸ̖͇͈͍̱̭̌͂͆͊_C͈OM̱̈́͛̈ͩ͐͊ͦEͨ̓̐S̬̘͍͕͔͊̆̑̈́̅3;
}
all angularjs codes in app-52143d391a.js not worked, it return
Uncaught Error: [$injector:nomod] Module 'client' is not available! You either misspelled the module name or forgot to load it. If registering a module ensure that you specify the dependencies as the second argument.
After run
gulp.task('obfuscate', function () {
return gulp.src('../webapp/scripts/app-*.js')
.pipe(ngAnnotate())
.pipe(obfuscate())
.pipe(gulp.dest('dist'));
});
all are ok, and get below codes:
http://pan.baidu.com/s/1ntXuGbB
then run with below error:
Uncaught TypeError:  _ 206. _ 252 is not a function(anonymous function) # app-6c38d9a2fc.js:2
generated.js:9279Uncaught Error: [$injector:modulerr] Failed to instantiate module client due to:
Error: [$injector:nomod] Module 'client' is not available! You either misspelled the module name or forgot to load it. If registering a module ensure that you specify the dependencies as the second argument.
http://errors.angularjs.org/1.4.7/$injector/nomod?p0=client
From the ngDocs:
Careful: If you plan to minify your code, your service names will get
renamed and break your app.
To make your code work after minification you have to use the inject property, every-time you use dependancy injection, to annotate your components. So even if the variable names change angular will know how to map the mangled variables with the proper service.
e.g:
Here we pass the $scope with dependency injection:
someModule.controller('MyController', function($scope) {
For this line to work you have to change to this:
someModule.controller('MyController',[ '$scope', function($scope) {
Or:
MyController.$inject = ['$scope'];
someModule.controller('MyController', function($scope) {
Or even better.
Use gulp-ng-annotate before the obsfucate task which does all that work for you.
UPDATE
After Obsfucation you should see the $inject string as they where, instead this plugin obsufucates them also. Meaning:
['$http', function($http) {
becomes:
[ "ಠ_ಠ727", function( ಠ_ಠ727 )
instead of:
[ "$http", function( ಠ_ಠ727 )
There is a relevant issue on github
I would suggest you use another plugin. With gulp-uglify I didn't have any issue.

How to mock Angular modules before injecting?

I have the following dependencies in my application:
angular.module 'MyApplication.NetworkingModule', ['ngResource']
.value 'NetworkingValues'
angular.module 'MyApplication.MyModule', []
.factory 'MySpecificResource', ['NetworkingValues', ...]
.factory 'MyService', ['MySpecificResource', ...]
So MySpecificResource depends on NetworkingValues.
The thing is I'd like to mock some of these values before using them in resources in tests.
Doing this:
beforeEach module 'MyApplication.NetworkingModule'
beforeEach module 'MyApplication.MyModule'
causes that factory is invoked immediately and I don't have a chance to replace some of NetworkingValues.
beforeEach inject (#MyService, #NetworkingValues) ->
MyService depends on MySpecificResource, which depends on NetworkingValues that I want to stub before tests.
What are the correct way to do that?
Have you tried something along these lines?
var mockedValues;
beforeEach(module(function($provide) {
$provide.value('NetworkingValues',mockedValues);
}));
beforeEach(inject(function(_NetworkingValues_) {
mockedValues = {....};
}));
The $provide will supply your mockedValues instead of the actual NetworkingValues.
So my final solution was to provide constant and inject it together with module function:
beforeEach module "MyApplication.NetworkingModule", ($provide) ->
$provide.constant 'NetworkingValues', endpointGet: 'my-endpoint/:id'

Mock with jasmine

I want to isolate my angular unit specs.
My code base consists of a factory and a controller:
.factory('ServiceData', ->
{ message: 'im data from a factory service' }
)
.controller('Controller', ['$scope','ServiceData',($scope, ServiceData)->
$scope.serviceData = ServiceData
And here is a spec for the controller:
it 'should get data from a service', ->
spyOn(ServiceData, 'message').andReturn('im from a mock') # this doesn't work
expect($scope.serviceData).toEqual 'im from a mock'
The spec doesn't work, because I get the following error:
ReferenceError: ServiceData is not defined in ...controller_spec.js
I want to isolate my controller spec from my factory. How do I achieve this?
Also, this is a trivial example, but in future I will have very complex factories that get data from the server and all sorts. So I want to know how to stub them out.
I also might want to test how my controller behaves based on different things that are returned by the mocked out function, so I definitely want to use this approach.
I've tried to using $provide, but to much success
beforeEach ->
module 'App', ($provide)->
$provide.value 'serviceData', { message: 'im from a mock' }
results in:
Error: [ng:areq] Argument 'fn' is not a function, got Object
I mean...I just don't even know
Quote:
it 'should get data from a service', ->
spyOn(ServiceData, 'message').andReturn('im from a mock') # this doesn't work
expect($scope.serviceData).toEqual 'im from a mock'
The spec doesn't work, because I get the following error:
ReferenceError: ServiceData is not defined in ...controller_spec.js
/quote
I think it should be something more like this:
it 'should get data from a service', -> //this is coffeescript syntax you're using I assume
$scope.serviceData = ServiceData.serviceData
expect($scope.serviceData).toEqual 'im from a service'
//where ServiceData is defined as a service that returns a Plain Old Javascript Object
//and that service returns an object in format: { serviceData: 'im from a service' }
If I understand what you are trying to test

Getting error when trying to use $provide in jasmine unit test

I'm using AngularJS and trying to test a controller that calls a factory to get some data.
This is the controller code:
'use strict'
angular.module('AngularApp')
.controller 'IndexCtrl', ($scope, session, navigation) ->
session.find().then (response) ->
$scope.session = response.data
$scope.someOtherVariable = {}
Naturally, I'd like to swap the factory with a mock to prevent calling a real API. I'm trying to use $provide.factory to inject the mock copy:
'use strict'
describe 'Controller: IndexCtrl', ->
# load the controller's module
beforeEach module 'mosaicAdminWebClientApp'
beforeEach module ($provide) ->
$provide.factory 'session', ->
true
IndexCtrl = {}
scope = {}
# Initialize the controller and a mock scope
beforeEach inject ($controller, $rootScope) ->
scope = $rootScope.$new()
IndexCtrl = $controller 'IndexCtrl', {
$scope: scope
}
it 'should attach a list of awesomeThings to the scope', ->
expect(true).toBe true
When running this test with Karma, I guess this error:
Chrome 32.0.1700 (Mac OS X 10.9.1) Controller: IndexCtrl should attach a list of awesomeThings to the scope FAILED
Error: [ng:areq] Argument 'fn' is not a function, got Object
http://errors.angularjs.org/1.2.10-build.2176+sha.e020916/ng/areq?p0=fn&p1=not%20a%20function%2C%20got%20Object
at /Users/blaiz/Documents/some_angular_app/app/bower_components/angular/angular.js:78:12
at assertArg (/Users/blaiz/Documents/some_angular_app/app/bower_components/angular/angular.js:1363:11)
at assertArgFn (/Users/blaiz/Documents/some_angular_app/app/bower_components/angular/angular.js:1373:3)
at annotate (/Users/blaiz/Documents/some_angular_app/app/bower_components/angular/angular.js:3019:5)
at Object.invoke (/Users/blaiz/Documents/some_angular_app/app/bower_components/angular/angular.js:3685:21)
at /Users/blaiz/Documents/some_angular_app/app/bower_components/angular/angular.js:3554:71
at Array.forEach (native)
at forEach (/Users/blaiz/Documents/some_angular_app/app/bower_components/angular/angular.js:303:11)
at Object.createInjector [as injector] (/Users/blaiz/Documents/some_angular_app/app/bower_components/angular/angular.js:3554:3)
at workFn (/Users/blaiz/Documents/some_angular_app/app/bower_components/angular-mocks/angular-mocks.js:2144:52)
Chrome 32.0.1700 (Mac OS X 10.9.1): Executed 10 of 10 (1 FAILED) (0.26 secs / 0.068 secs)
Warning: Task "karma:unit" failed. Use --force to continue.
Aborted due to warnings.
I've tried many different permutation, such as removing the label, passing an object or simple value instead of a function and none of them worked.
The documentation (http://docs.angularjs.org/api/AUTO.$provide) shows that I should call the factory() method with as factory(name, $getFn) where name is a string and $getFn is a function. That's what I'm doing but it's not working.
Anything I've missed? Does anyone know how to properly use $provide in Jasmine unit tests?
Thanks
Update:
I found a plunkr similar that solved an issue similar to this one here: stackoverflow.com/questions/19297258/why-is-provide-only-available-in-the-angular-mock-module-function-and-q-onl
I created my own plunkr with this code, but with the test code in JS instead of Coffee and got it to work: plnkr.co/edit/gfBzMXpKdJgPKnoyJy5A?p=preview
Now I manually converted the JS code into Coffee: plnkr.co/edit/qGGMayFjJoeYZyFKPjuR?p=preview
The error is back so the coffee code is wrong but the js code works.
Found the answer to my question.
Since CoffeeScript always returns the result from the last statement, in this case, CoffeeScript returns $provide inside of module(), which is wrong. An easy way to fix that is to just manually add a return statement after $provide, like this:
beforeEach module 'mosaicAdminWebClientApp', ($provide) ->
$provide.service 'session', ()->
return
Hope that will help someone else.

How do I spy on an Angular promise chain using Jasmine

Using AngularJS, CoffeeScript and Jasmine (edited in WebStorm), I would like to unit test a chain of promises.
Lets say I have the following example service:
Angular Service
class ExampleService
stepData: []
constructor: (#$http) ->
attachScopeMethod: (#scope) ->
#scope.callSteps = => #step1().then -> #step2()
step1: ->
#$http.get('app/step/1').then (results) =>
#stepData[0] = results.data
results
step2: ->
#$http.get('app/step/2').then (results) =>
#stepData[2] = results.data
results
This service allows me to attach a the method callSteps() to the scope. This method, when called, executes a series of asynch $http calls to a 3rd party API.
To test that each step is at least called, I have written the following Jasmine spec.
Jasmine Spec
ddescribe 'ExampleService', ->
beforeEach ->
module 'myApp'
beforeEach inject ($rootScope, $injector) ->
#scope = $rootScope.$new()
#exampleService = $injector.get 'exampleService'
#q = $injector.get '$q'
describe 'process example steps', ->
beforeEach ->
#exampleService.attachScopeMethod(#scope)
it "should attach the scope method", ->
expect(#scope.callSteps).toBeDefined()
describe 'when called should invoke the promise chain', ->
it "should call step1 and step2", ->
defer = #q.defer()
#exampleService.step1 = jasmine.createSpy('step1').andReturn(defer.promise)
#exampleService.step2 = jasmine.createSpy('step2')
#scope.callSteps()
defer.resolve()
expect(#exampleService.step1).toHaveBeenCalled()
expect(#exampleService.step2).toHaveBeenCalled()
The results of this test is as follows:
expect(#exampleService.step1).toHaveBeenCalled() - PASS
expect(#exampleService.step2).toHaveBeenCalled() - FAIL
Can you tell me how I can get step2() to succesfully run under test?
Thank you
EDIT
#Dashu below kindly supplied the answer to the problem. The trick is to simply call scope.$apply or scope.$digest to trigger the promise chain resolution.
So here is the working test fragment.
describe 'when called should invoke the promise chain', ->
it "should call step1 and step2", ->
defer = #q.defer()
defer.resolve()
#exampleService.step1 = jasmine.createSpy('step1').andReturn(defer.promise)
#exampleService.step2 = jasmine.createSpy('step2')
#scope.callSteps()
#scope.$apply()
expect(#exampleService.step1).toHaveBeenCalled()
expect(#exampleService.step2).toHaveBeenCalled()
try a $rootScope.$apply() before the second expect
also about defer.resolve(). i don't know if this actually resolves the promise, i think it just setups the value to return when it resolves.
so i would move that up to just underneath the $q.defer() call, before passing the promise to the andReturn()
you could do defer.resolve(true), defer.reject(false), so if you're promise will get rejected insinde callsteps, true or false will be returned

Resources