I am new to Jasmine, but would like to do more TDD in Javascript. I've been using the Angular library from Google, and I know that Testacular was specifically created for testing Angular apps.
I have read through both the Jasmine documentation and watched the Testacular setup video, but I can't get the most basic testing to work. Assume I have three files:
modules.js
controller.js
appSpec.js
modules.js has my module definition with a few .factory(...) services and a few .directive(...) custom directives. controller.js houses my controllers for wiring up the modules to the html view.
So far, so good. Next I have added appSpec.js. Let's say I want to use it to test a service in my module called, "Data" that has a method, "getData()" which returns a resource.
In testactular init I have told it to watch all three files. I run Testacular and it tells me it is watching the correct files. Super.
What I don't understand is how I get the Jasmine spec know how to look at the module and controller so that they can be tested. If I simply say:
describe('Data Service', function(){
it ('should retrieve two items from the database', function(){
data = Data.getData() //my angular service
expect(data.length).toBe(2);
});
});
Not surprisingly, it has no idea what Data.getData() is.
It seems obvious that somehow I am supposed to bring my module definition and controllers into the spec before I begin writing suites. It must be so obvious that I don't see in the documentation how people are doing it. Tutorials just seem to start writing specs in the spec.js file and assume all is well.
I have seen other posts here where similar questions are being asked, but admittedly they all have a foundation I seem to be lacking. For example, one post talks about not manually creating an instance of the controller, but rather inject dependencies. Why is he creating a new $rootScope object, how is his module being referenced, etc...
I understand that my question is probably just a lack of basic understanding of the Jasmine framework, but I can't seem to squeeze any more understanding from the Jasmine readme file. Can someone point me to a basic explanation of how this is supposed to work?
Thanks.
Try doing module('myModule') in the jasmine test.
Here are some open source angular projects that have great tests to look at:
angular-app
angular-ui
bootstrap
The way that you include your definitions for your tests is through the karma.config.js file
files = [
JASMINE,
JASMINE_ADAPTER,
'../app/lib/icg/object.js',
'../app/lib/icg/geometry.js',
'../app/lib/icg/ubiquity.js',
'../test/unit/icgUbiquitySpec.js',
'../test/unit/icgObjectSpec.js'
];
Here you define which files get loaded into your browser, the first files that don't include the word 'Spec' are the definitions and the ones that do include are my test files. You need to include your definitions BEFORE your tests so that they are defined before you run the tests, which is why I include all my spec files last.
Related
I'm a newbie when it comes to unit-testing, I've tried to do a unit-test with Karma and Jasmine from an app that already existed.
Basically the app has a lot of dependencies from different third-party libraries being used. So when I tried to create a unit-test a stumbled upon a lot of errors from Karma/Jasmine. One of them is the screenshot below:
From the screenshot, I'm getting an unknown provider: socketFactoryProvider, which I've traced down and found out that it belongs to the btford.socket-io module. So what I've did was to have a code like this to mock the dependencies:
// Set the app module
beforeEach(function () {
angular.module('btford.socket-io', []);
module('opensportsAdmin');
});
But I'm still getting an error (based on the screenshot).
So my question is, how can you work with third-party libraries for your unit-test? I'm kind of new and didn't find any articles that can help me with my problem.
Here's a reference to my code.
The best practice is to use one internal module for your app and one module for dependencies.
detailed explanation and example here:
Truly mocking providers
I cannot get the Breeze Angular service (http://www.getbreezenow.com/documentation/breeze-angular) to configure Breeze to use Angular promises, i.e., I can never get the useNgPromises() function within breeze.bridge.angular.js to log a message to the console and thus I assume Breeze is never configured to use Angular promises.
My JS files are loaded as follows:
angular.js (v 1.3.14)
angular-route.js (v.1.3.14)
q.js (v 1.1.2) - This is here because I kept getting errors that Q was undefined if I left this out. I don't know if it is necessary to load Q.js if I use Angular promises instead.
breeze.js (v 1.5.3)
breeze.bridge.angular.js (v 1.1.0)
app.js and other app-specific JS files
So far, that satisfies steps #1 and #2 from the "Install It" section of that Breeze page.
Per steps #3 and #4 on that Breeze page and comments in breeze.bridge.angular.js, this is what my app.js looks like:
window.myApp = angular.module("myApp", [
"breeze.angular"
])
.value("breeze", window.breeze)
.config(["$routeProvider", function ($routeProvider) {
// Routing code
}])
.run(['breeze', function (breeze) {}]);
As far as I can tell, that code matches the Example #1 code on that Breeze page and satisfies steps #3 and #4 on that Breeze page.
Then I have some basic views that use controllers and datacontext JS files, the latter of which inject the breeze object as a dependency. To test whether Breeze is actually executing the configuration to work with Angular promises, I edited the breeze.bridge.angular.js file such that the following statement is included as the first line in the useNgPromises() function:
console.log("Using Angular promises!");
When I run my app, I never see that in the console, so I assume the configuration is not happening.
Am I doing something wrong in the setup? If so, what?
You absolutely do NOT need Q.js; the fact that you see errors relating to that library is an indication of a configuration mistake.
I think you found the problem when you added .value("breeze", window.breeze).
That one line wipes out the definition of the breeze service established by the 'breeze.angular' module (defined in breeze.bridge.angular.js), thus preventing the configuration of breeze for $http and $q. Without that configuration, breeze looks for a promises implementation elsewhere ... hence the complaint about missing Q.js. So ... yes ... that line was a disaster.
I wondered where you got the idea for that line. Thanks for referencing the Microsoft ASP Breeze/Angular template documentation. Wow that is old (2013). I'd kill it ... and the other defunct Visual Studio Breeze templates ... if I could (maybe I can). I thought we had made them hard to find. Looks like you found them. How?
That certainly isn't how we teach Breeze + Angular these days.
The "Todo Angular" sample is a far better guide (even if it is a year old).
I resolved the issue by commenting out the line:
.value("breeze", window.breeze)
I had included that line per the "Dependency Injection" section here: http://www.getbreezenow.com/ng-spa-template#module as well as the "Value Recipe" section here: https://docs.angularjs.org/guide/providers, but apparently it does not play well with the Breeze Angular service.
I am not sure exactly why this is the case but my best guess is that the issue was caused by Angular values not being configurable, per this explanation: https://gist.github.com/demisx/9605099, and I assume the Breeze Angular service requires the Breeze object to be configurable and therefore it should not be injected as a dependency for the app via the value recipe.
I should also note that after removing that troublesome line of code, I no longer receive an error indicating that Q.js is required since the Breeze Angular service successfully configures Breeze to use Angular's $q instead.
Il go strait to it. Im writing unit tests for Angular front end application.
This is what I have so far...
app.js:
var app = angular.module('app', ['dependency1', 'dependency2', 'dependency3']);
Then to create a service I have:
app.factory('myService', ['serviceDependency', function(serviceDependency) {
...
});
How do I create/inject myService to be tested? When I do:
beforeEach(module("app"));
Jasmine screams at me about not finding dependencies, and I dont know how to mock them. (there are more than 3, like way more. :) )
beforeEach(inject(function(myService){}));
Does not work without doing the 'module' one first. Im stuck on this for 3 days googleing watching videos and I just cant find what I need, or I cant see it.
First time writing for help, so do ask any questions if you feel I missed something, and you need more info.
ktnxbye
Edit:
Custom providers are created like so:
app.config(['customProvider', function(customProvider){
...
}]);
any suggestions on how to mock this?
ktnxbye
Well, I've 'solved' it!
What I did is I included all dependencies required by the app in the test project and now it runs fine. But I don't quite like that solution. I would like it more if I could some how mock all those dependencies. I will still try to mock them and if I'm successful I will post here for all to see. Also I would appreciate any help from all :)
ktnxbye
I keep seeing jasmine specs testing angular controllers/directives/filters employing these two functions but where do they come from? Where can I find their documentations?
Both come from Angular itself:
Angular.Mock.Inject documentation
Angular.Mock.Module documentation
I believe both are defined in angular-mocks.js, which puts module and inject as properties on window.
Thanks to the great article from Dan Wahlin, I managed to implement lazy loading of Angular's controllers and services. However, there does not seem to be a clean way to lazy load independent modules.
To better explain my question, assume that I have an app would be structure as below without RequireJS:
// Create independent module 'dataServices' module with 'Pictures' object
angular.module("dataServices", []).factory("Pictures", function (...) {...});
// Create 'webapp' ng-app, with dependency to 'dataServices', defining controllers
angular.module("webapp", ['dataServices'])
.controller("View1Controller", function (...) {...})
.controller("View2Controller", function (...) {...});
Here is the sample app with RequireJS in Plunker:
http://plnkr.co/aiarzVpMJchYPjFRrkwn
The core of the problem is that Angular does not allow adding dependency to ng-app post instantiation. As result, my solution is to use angular.injector to retrieve the instance of Picture object to be used in my View2Controller. See js/scripts/controllers/ctrl2.js file.
This creates 2 problems for me:
The injected services runs outside of angular and therefore all async call must end with $scope.$apply()
Messy code where some object can be injected using standard angular syntax while others require the explicit use of injector.
Have any of you figured out how to lazy load independent module using RequireJS and somehow hook this module in angular so normal angular dependency injection syntax can be used?
Note:
The question is on lazy loading of independent module. One simple solution to this specific example is to create "Pictures" object using cached $providers during ng-app.config but that is not what I am looking for. I am looking for solution that works with 3rd party module such as angular-resource.
I finalized my own implementation called angularAMD and here is the sample site that uses it:
http://marcoslin.github.io/angularAMD/
It handles config functions and out of order module definitions.
Hopefully this can help other looking for something to help them with RequireJS and AngularJS integration.
Take a look at my project in GitHub: angular-require-lazy
This project is intended to demonstrate an idea and motivate discussions. But is does what you want (check expenses-view.js, it loads ng-grid lazily).
I am very interested in comments, ideas etc.
(EDIT) The ng-grid Angular module is lazy loaded as follows:
expenses-view.js is loaded lazily, when the /expenses route is activated
expenses-view.js specifies ng-grid as a dependency, so RequireJs loads ng-grid first
ng-grid is the one that calls angular.module(...)
In order to accomplish this, I replaced (proxied actually) the real angular.module method with my own, that supports laziness. See bootstrap.js and route-config.js (the functions initLazyModules() and callRunBlocks()).
This implementation has its drawbacks that you should be aware of:
Config functions are not implemented (yet). I do not know if it is possible to lazily provide config-time dependencies.
Order matters in definitions. If service A depends on B but A is defined after B in your module, DI wil fail. This is because the lazyAngular proxy executes definitions immediately, unlike real Angular that makes sure dependencies are resolved before executing the definitions.
It looks like the Node.js module ocLazyLoad defines a way of doing this lazy-loading, though I'm not sure how it fares, performance-wise, compared to the methods in the other answers or hard-coding the dependencies. Any info on this would be appreciated. One interesting thing is that the other answers need RequireJS to operate, while ocLazyLoad doesn't.
It looks like ocLazyLoad defines another provider that injects the dependency after the containing module has already been instantiated. It seems to do this by essentially replicating some low-level Angular behavior, like module loading and providing, hence why it looks so complicated. It looks like it adds just about every core Angular module as a dependency: $compileProvider, $q, $injector, ng, and so many more.