Testing: How can I verify what's injected into a controller definition function?

43 views
Skip to first unread message

Michael Kelly

unread,
Apr 13, 2014, 4:42:39 PM4/13/14
to ang...@googlegroups.com
I'd like to be able to verify what's being injected into my controller, and further, to use that information in additional tests.

I'd expect to be able to do something like this:

    var app = angular.module('MyApp',[]);
    app.controller('MyController', ['$scope', '$q', function($scope, $q) {
        $scope.myFn = function () {};
    }]);

    describe('MyController', function () {

        it('should require $scope and $q', function () {
            expect(app.controller("MyController").requires).toBe(['$scope', '$q']);
        });

    });

But when I execute this test I get:

    Expected [  ] to be [ '$scope', '$q' ]

Any help would be appreciated,

-michael

Michael Kelly

unread,
Apr 14, 2014, 11:29:16 AM4/14/14
to ang...@googlegroups.com
Alright, I have a solution. If I separate out the controller definition function like this:

    var app = angular.module('MyApp',[]),
         myControllerFn = function($scope, $q) {
             $scope.myFn = function () {};
         };
    app.controller('MyController', ['$scope', '$q', myControllerFn]);

Then I can get the injected bits thusly:

     var injectedBits = angular.injector().annotate(myControllerFn);

If I could somehow get the controller definition function from the controller object itself, then it wouldn't be necessary to separate out the definition function. But this'll meet my needs.

Thanks,

-michael

Witold Szczerba

unread,
Apr 14, 2014, 11:56:10 AM4/14/14
to ang...@googlegroups.com
Hi,
why would you need this? Testing if something was injected into
controller sounds like an anti-pattern. Who cares how was the
controller created if it can (green light) or cannot (red light) do
it's job?

Regards,
Witold Szczerba
> --
> You received this message because you are subscribed to the Google Groups
> "AngularJS" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to angular+u...@googlegroups.com.
> To post to this group, send email to ang...@googlegroups.com.
> Visit this group at http://groups.google.com/group/angular.
> For more options, visit https://groups.google.com/d/optout.

Michael Kelly

unread,
Apr 14, 2014, 12:36:52 PM4/14/14
to ang...@googlegroups.com
You make a good point. And in truth, my primary interest is not in verifying what's injected. I'm exploring the creation of a jasmine extension that will greatly assist in the creation of mocks for all dependencies injected into controllers, services, filters, etc. 

The concept is this: 

* mock all dependencies,
* in your "given" statements, specify how these mocks will respond when called,
* only in your "then" statements do you passThrough to the actual code.

This gives the code under test total isolation, and forces your tests to be explicit about the conditions for the test. If you follow the AngularJS mandate that dependencies be injected, and you have access to those dependencies in the setup for the test, then you should be able to mock all dependencies with a single line of code. Something like:

     dep = spyOnControllerDependencies(MyController);

Where "dep" is an object containing the mocked/spied on dependencies. 

Perhaps I should have been more forthcoming at the get go, but I was worried about verging into tl;dr territory.

-michael

Witold Szczerba

unread,
Apr 14, 2014, 6:33:43 PM4/14/14
to ang...@googlegroups.com
I am not sure, but did you try shadowing the $controllerProvider? It
is used to instantiate, but also to register controllers, so maybe, in
your test, you could wrap the service, so all the registrations would
go through your "man-in-the-middle" exposing constructors to you.

BTW: for historical reasons, the controller pointer can be also a
global constructor function instead of a string, so you could also
implement this. See source code for reference.

Regards,
Witold Szczerba

Michael Kelly

unread,
Apr 15, 2014, 1:20:40 AM4/15/14
to ang...@googlegroups.com
Thanks Witold. I'll take a look at the man-in-the-middle approach. I've been looking at a similar approach for changing the names of jasmine expect matchers to make them read better in the given/when/then style of specifications.

But I think I'll start with passing in a global constructor function as the easiest thing, and assume that I'll be able to leverage that code when/if I try the man-in-the-middle approach as a means to get the constructor without it being global. I don't want to get too fixated on this issue. I still need to prove that I can achieve the larger goal: create a relatively simple and maintainable jasmine extension that will greatly simplify the writing of AngularJS unit tests. To accomplish this, the tool will need to be opinionated about both the writing of the AngularJS code and the writing of the tests. By being opinionated, you limit what you support which simplifies the requirements and simplifies the code for the extension. That's the theory, at least. Now I just need to prove it.

Thanks for your help,

-michael

-
Michael


You received this message because you are subscribed to a topic in the Google Groups "AngularJS" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/angular/NWiP0HtJzG4/unsubscribe.
To unsubscribe from this group and all its topics, send an email to angular+u...@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages