I'm new to testing, and I understand it's not a good practice to write unit tests that test third-party packages. But I'm curious if the following test would constitute testing the AngularJS framework itself:
describe("TestController", function () {
it("should set up the $scope correctly", function () {
expect($scope.foo).toBe("bar");
});
});
Is it a good idea to test that your $scope got initialized correctly, or since that's AngularJS's job and not yours, is that something you should avoid writing a test for? Sorry if this has an obvious answer, but I want to make sure I'm teaching correct principles when I teach this to my students.
In your example, you are testing the behaviour of TestController, which is a class that you wrote, i.e. you are testing your own code.
You are not testing if Angular can set the state properly if you tell it to (which would indeed be a bit redundant, as it is already covered by Angular tests), you are asserting that your code does the things your application requires it to do (that this involves calling Angular functions is secondary).
So that's a good test to write.
Depending on your application, it may be possible to check the same behaviour in a more "high-level" fashion than asserting what exact value a given state variable has. For some applications that could be considered an implementation detail and not the most appropriate way to validate correct behaviour. You should not be testing internal state, but externally visible behavior. In this case, though, since you are testing a controller, and all a controller does is update the state, it's probably appropriate.
If you find that all you are doing in the controller is unconditionally set state, without any logic involved, then you may not really have a need to test the code at that level of granularity (maybe test bigger units that in combination do something "interesting"). The typical example here is testing setter/getter methods: Yes, there is a chance that you get these one-liners wrong, but they make really boring tests, so you might want to skip those (unless they can be automatically generated).
Now, if this test fails, it could be for three (not mutually exclusive) reasons:
1) your code is broken (either some state setup is missing or you are not doing it right). Detecting this is the main purpose of unit testing.
2) Angular is broken (you set the state properly, but somehow Angular lost it). That is unlikely, but if it does happen, you now have a test case to attach to your bug report to Angular. Note that you did not set out to write a test case for Angular, but you got one "by accident".
3) your code as well as Angular are correct, but your test code is wrong. This happens frequently when you update the code that is being tested and test code also needs to be adjusted because its assumptions have been too narrow, or the expected behaviour has changed and the test is now simply outdated.
I just started a gulp-angular Yeoman project and opened it in brackets, and nearly everywhere I'm getting 'something' was used before it was defined, specifically 'angular' was used before it was defined. I've messed around with extensions and other fixes, but I honestly don't know what I'm doing and I haven't been able to find any good documentation.
What can I do to silence these warnings across the entire project? In other words, how can I avoid using /*global angular*/ in pretty much every file in my project?
Here's a quick example of one of my controller declarations:
/*global console*/
(function () {
'use strict';
angular
.module('sbk')
.controller('MainController', MainController);
/** #ngInject */
function MainController() {
console.log('Woo!');
}
})();
Which results in the following JSLint warnings:
'angular' was used before it was defined.
'MainController' was used before it was defined.
'MainController' was used before it was defined.
Move the invocation into the parens that contain the function.
Edit: updated to make my issue a little clearer
http://jshint.com/docs/options/#globals
Provide a list of globals, and you're done.
It's not clear if you're using jshint or jslint; your tags say one, your text another.
Okay, so remember that JSLint is just a JavaScript file, and most JSLint plugins I've used has the source shoved in the install somewhere (though the Visual Studio plugin embeds it into a dll!), so you can always get it to do what you want [within reason].
In your case, the absolute worst case is that you initialize the list of predefined variables with the namespaces you want JSLint to assume are always considered to be in scope. I'm going to show you how to set that globally within Brackets, which might not always be the best thing to do, and you need to keep in mind if you're linting another project that doesn't have Angular, well, you've screwed up JSLint.
(Honestly, I'm disappointed that Brackets doesn't have global as a global in its preferences.json file. Guess I should submit a patch.)
There are lots of ways we could do this. I'm going to add a namespace to the standard object. You could also limit its exposure by adding to the browser collection of globals (line 497 in my Bracket install's jslint file). We could also create a new JSLint directive option, similar to browser. That's not quick 'n' dirty, but is probably The Right Way to do this without submitting a Brackets patch. But for now, let's just do the quick 'n' dirty fix.
I'm using Windows. If you're not, YMWV. I do not use Brackets daily, so I haven't tested this in a production coding environment. If anything doesn't work, I'd be interested to hear it, however, and I'll see if I can find what's wrong.
TL;DR. Just the code, ma'am.
The jslint.js file you want is here, if you went with the default install location:
C:\Program Files (x86)\Brackets\www\extensions\default\JSLint\thirdparty\jslint
(Just for fun, notice that the version of JSLint we're using is from 2011!!?! Come on, Brackets guys!)
standard is, in my file, on line 974. Here's what it had originally:
// standard contains the global names that are provided by the
// ECMAScript standard.
standard = array_to_object([
'Array', 'Boolean', 'Date', 'decodeURI', 'decodeURIComponent',
'encodeURI', 'encodeURIComponent', 'Error', 'eval', 'EvalError',
'Function', 'isFinite', 'isNaN', 'JSON', 'Math', 'Number', 'Object',
'parseInt', 'parseFloat', 'RangeError', 'ReferenceError', 'RegExp',
'String', 'SyntaxError', 'TypeError', 'URIError'
], false),
Here's a test JavaScript snippet I want to lint that uses my global object, util:
/*jslint sloppy:true, white:true */
utils.spam();
utils.spammySpam();
smutils.spam();
All we need to do is to add , 'utils' to standard, and we're done.
standard = array_to_object([
'Array', 'Boolean', 'Date', 'decodeURI', 'decodeURIComponent',
'encodeURI', 'encodeURIComponent', 'Error', 'eval', 'EvalError',
'Function', 'isFinite', 'isNaN', 'JSON', 'Math', 'Number', 'Object',
'parseInt', 'parseFloat', 'RangeError', 'ReferenceError', 'RegExp',
'String', 'SyntaxError', 'TypeError', 'URIError', 'utils'
], false),
To be overly clear, you'd add 'angular' and 'MainController' to your standard object in place of 'utils'.
Btw, Crockford (JSLint's creator and maintainer) would hate that we did that. I don't really blame him.
Now I get an error for smutils, but not utils. Luckily, the global directive still works with this setup:
/*jslint sloppy:true, white:true */
/*global smutils */
utils.spam();
utils.spammySpam();
smutils.spam();
And we're JSLint friendly again.
Hacking JSLint directly might not have most favored option status, but it works.
Btw, re: Dave's comment, "IMO JSLint has been popularly deprecated in favor of JSHint or JSCS," I'm not so sure. Just as an example, which does Brackets use out of the box? The best part about JSLint is how much less subjective discussion has to happen to get a large team on the same page. And trust me, Crockford's not going to tell you to do something that's indefensibly wrong.
He might hurt your feelings, and he might ask you to do something that hurts at first, but I really think JSLint can teach a team a lot about JavaScript best practices. It's worth living in JSLint for a while before jumping to JSHint.
I'm working on a a large scale angular project with a team of devs.
the problem we run into is if you have several files for a component, say a directive.
some-directive.js
some-directive-controller.js
in the definition of both files you would have to attach them to a module, however one file must create the module with []. If the developer forgets to poke around they will add [] in the second file called will actually overwrite the module. so now it becomes a memory game. Each developer has to remember to only declare the module in one file []
some-directive.js
angular.module('some-module',['some-dependencies']).directive('some-directive',function(){});
some-controller.js
angular.module('some-module',[]).controller('some-controller',function(){});
we have been using the following approach. Is there a better way?
some-directive.js
some-directive-module.js
some-directive-controller.js
where some-directive-module only contains the module creation, includes any dependencies, and does any .config needed. Still the dev needs to remember to
angular.module('some-directive') in all the other files without the square brackets.
some-directive-module.js
angular.module('some-directive',[])
.config(//someconfig stuff);
some-directive-module.js
angular.module('some-directive).directive(//declare directive);
some-directive-controller.js
angular.module('some-directive).controller(//declare contrller used by directive);
I suggested that instead we should do the following, it eliminates the issue of overwriting modules, but I received some negative feedback from one of the other devs
some-directive-module.js
angular.module('some-directive',['some-directive.directive','some-directive.controller'])
.config(//someconfig stuff);
some-directive-module.js
angular.module('some-directive.directive',[]).directive(//declare directive);
some-directive-controller.js
angular.module('some-directive.controller',[]).controller(//declare contrller used by directive);
Is there a better way? Or is one of the above options correct?
The recommended way (by multiple competent people) is to use the setter-getter-syntax (creating once with angular.module("someModule",[]) and accessing with angular.module("someModule") from there on). Putting the module definition and configuration into one file seems very clean and is common practice between a lot of developers. But make sure not to create a module for every single directive - group services, directives, constants and so on into reasonable functionality-modules instead.
Making clear what a file contains by its name is also a good idea in my opinion, so your some-directive-module.js approach seems fine to me. If developers "poke around" and "wildly add []", they should get a slap on the wrist follwoed by an explanation how modules work in angular, so they stop doing it ;-)
Traditionally I have managed my Angular code like this
//File 1
angular.module('name',[])
//File 2
function TestController(){
}
TestController.prototype.// inherited stuff
angular.module('name').controller('testController',TestController);
This worked great and allowed me to partition my files easily. Now I try to upgrade to 1.3 and get the infamous...
Error: [ng:areq] Argument 'TestController' is not a function, got undefined
Of course this is due to this change which claims a desire to clean up the way people write code. What about this pattern is more complex? Is there a way to maintain this pattern without changing the global settings?
There is actually a comment on the page you linked to that had a fairly solid explanation.
Global controllers refer to your controllers being defined as function
on the window object. This means that they are openly available to
conflict with any other bit of JavaScript that happens to define a
function with the same name. Admittedly, if you post-fix your
controllers with ...Controller then this could well not happen but
there is always the chance, especially if you were to use a number of
3rd party libraries. It is much safer to put these controller
functions inside the safety of a module. You then have more control
over when and where this module gets loaded. Unfortunately controller
names are global across an individual Angular app and so you still
have the potential for conflict but at least you can't clash with
completely different code in the JavaScript global namespace.
So the idea is that global controller functions could conflict with any other global function in any javascript you use. So to eliminate the chance of a conflict with your own code or a third-party script, not using global controllers makes your code safer and more consistent.
As mentioned in the comments by #Brett, you can use IIFE around your prototyping. Here is an update of your plunk that uses that. The main change just looks like this.
(function() {
TestController.prototype.name = 'World'
})();
What comes to my mind is 2 things:
1) in that way functions wont be kept in memory more than they should.
2) if you minify your code, minifyer will have to generate new names for all global objects, which is sfine when you have small project, but will be a problem when it's not.
Also it should prevent tests to modify unnecessary data.
Necessitating the injection of a service in a controller in order to have access to data and/or functions can be thought of as both a pro and a con.
What I'm trying to determine is what are the objective "pitfalls" of utilizing the $rootScope for often used bits of data and or functions?
(Note: I'm not trying to start a religious war here, but instead be able to make well informed decisions.)
It's like polluting of global namespace in plain JavaScript. You are polluting your application's global space. It's never good to do that (in any of the languages).
But there are some reasonable usages of rootScope...Angular says:
Of course, global state sucks and you should use $rootScope sparingly, like you would (hopefully) use with global variables in any language. In particular, don't use it for code, only data. If you're tempted to put a function on $rootScope, it's almost always better to put it in a service that can be injected where it's needed, and more easily tested.