
How to Unit Test Controllers In AngularJS Without Setting Your Hair On Fire - zenlikethat
http://nathanleclaire.com/blog/2013/12/13/how-to-unit-test-controllers-in-angularjs-without-setting-your-hair-on-fire/
======
eeeschwartz
I've found that mocking $httpBackend in unit tests is usually an indication
that the system could benefit from an abstraction.

In the article's scraping example, a service that works like
`Scrape.url('success.com')` could be mocked and sidestep the $http boundary
with its flushing messiness. It also would make the system easier to reason
about.

For more of this line of thinking take a look at the "Don't mock what you
don't own" guideline which I've found very useful.

------
Cthulhu_
[pedantic mode on] In this particular case, or at least the first test case,
the unit test doesn't actually need the actual $location service; a mock will
suffice to set the expectation (i.e. the path method returning a path)

For clarity, I'd pull the createController function out of the beforeEach
method. Actually, in this case you don't even need to have it as a separate
method; simply calling $controller will be enough, since one, the controller
does not have any behavior on initialization (besides adding a function to the
scope), and second because all public methods are assigned to the scope.

Third, the injector can be skipped in favor of just having the appropriate
service names in the call to inject.

I took the liberty to clean up as I'd do it:
[http://pastebin.com/Vv63J3P5](http://pastebin.com/Vv63J3P5)

Same applies to the second unit test. I also wouldn't use the $http service
directly from a controller, or mock out http calls using $httpBackend; either
use a service that wraps $http calls itself, or ngResource/$resource. Those
two can then easily be mocked out in the controller spec
(slurpService.scrape(url).andReturn(neatlyParsedResult)) [/pedantic]

~~~
_mikz
In this case it might not be needed, but some controllers have calls that are
made on initialisation (like NgResource) which makes harder to use
$httpBackend (you have to mock all calls if you want to verify that no calls
were left at the end). I took the article more like best-practice for unit
testing controllers. But truth to be told, it would be nicer if author would
explain why to initialise controller in a method etc.

------
Ycros
In our project we've ended up with our controller code being defined outside
of the .controller call (we're using TypeScript, and someone wanted to use the
class syntax). This means that our controllers are accessible directly, and
for a _unit_ test, they're just functions we can call.

So we just pass our own mocks in, it's much simpler than trying to use
Angular's injector mechanism.

------
iagooar
This is a really nice article, it seems you found a similar solution to mine.

The only thing that is more or less difficult about testing Angular ist the
boilerplate one has to write to instantiate everything correctly. When writing
the code, Angular handles the bootstrapping of the application, but in the
tests one has to do it manually.

What's positive about it is that one learns how Angular works internally. I
would even say that doing TDD with Angular is actually a lot of fun.

~~~
awinder
Definitely agree with this. The function passed to the inject() method where
you set up your controller and inject mocks seems to have a bunch of
repetition in the unit test suites I've been writing. You can use $provide
([http://docs.angularjs.org/api/AUTO.$provide](http://docs.angularjs.org/api/AUTO.$provide))
to swap dependencies at the injector level and I'm starting to wonder if doing
that would be a lot more repeatable across unit test suites.

------
agrias
Jasmine tests are still hard for me to master (especially the httpBackend
stuff), so I really really appreciate posts like this, I guess at the end of
the day posts like these also encourage me to write code thats easier to test
as well. Thanks :D

