2

Let's suppose I have a controller like:

angular
    .module("app", [])
    .controller("NewsFeedController", [
        "$scope",
        "NewsFeedService",
        function ($scope, NewsFeedService) {
            $scope.news = [
                { stamp: 1 },
                { stamp: 9 },
                { stamp: 0 }
            ];

            $scope.onScroll = function () {
                /*
                    might do some stuff like debouncing, 
                    checking if there's news left to load, 
                    check for user's role, whatever. 
                */

                var oldestStamp = getOldestNews().stamp;
                NewsFeedService.getOlderThan(oldestStamp);

                /* handle the request, append the result, ... */
            };

            function getOldestNews () {
                /* code supposed to return the oldest news */
            }
        }
    ]);

getOldestNews is declared as a local function since there is no point to expose it in the $scope.

How should I deal with it? How can I actually test this function?

describe("NewsFeedController", function () {
    beforeEach(module("app"));

    var $controller, $scope, controller;

    beforeEach(inject(function (_$controller_) {
        $controller = _$controller_;
        $scope      = {};
        controller  = $controller("NewsFeedController", { $scope: $scope });
    }));

    it("should return the oldest news", function () {
        // How do I test getOldestNews?
    });
});

By the way, it'd be great if the solution also works for local functions within services and directives.


Related questions:

Community
  • 1
  • 1
Gabin
  • 920
  • 10
  • 26
  • From where is the getOldestNews called? If there is no entry point, why does it exists? – Ricconnect Feb 18 '15 at 16:17
  • @Ricconnect I added some context in the snippet, let me know if it's still unclear. – Gabin Feb 18 '15 at 16:30
  • Isn't it more logical if there is a `getOldestNewsItem` or `getOldestNewsItemStamp` method on the NewsFeedService? I ask this because, if you want to test private methods, this is most of the time an indication of bad design. – Ricconnect Feb 18 '15 at 16:33
  • Yay, good point! So `NewsService.getOldestNews` should accept an argument, a collection of news items? I am wondering what's more ideal: store items in the service or the `$scope`. Storing those in the service itself doesn't seems like a good idea, right? – Gabin Feb 18 '15 at 16:42
  • I am also wondering if it really deserves to be a `NewsService`'s method as it could be useful only to this particular controller. – Gabin Feb 18 '15 at 16:44

1 Answers1

3

Now I see what you really want to do in your code. I don't think it is necessary to test the private function, because it does not contain enough logic. I would suggest you only create a spy on the NewsFeedService to test that the correct data is send to that service.

describe("NewsFeedController", function () {

    beforeEach(module("app"));

    var $controller, $scope, controller;
    var newsFeedServiceMock = jasmine.createSpyObj('NewsFeedService', ['getOlderThan']);

    beforeEach(inject(function (_$controller_) {
        $controller = _$controller_;
        $scope      = {};
        controller  = $controller("NewsFeedController", { $scope: $scope, NewsFeedService : newsFeedServiceMock });
    }));

    it("should return the oldest news", function () {
         $scope.news = [
            { stamp: 76 },
            { stamp: 4 },
            { stamp: 83 }
         ];

         $scope.onScroll();

         expect(newsFeedServiceMock.getOlderThan).toHaveBeenCalledWith(83);
    });
});

This way you can check if the correct behaviour is done by your onScroll method, without the need to check private methods. You only want to test the public methods, so you have flexibility when you want to create private methods to separate logic, without having to alter your tests.

Ricconnect
  • 1,089
  • 5
  • 25
  • what if pvt function has enough logic..is there anyway i can test them without changing to public func? – Lakshay Dulani Oct 12 '15 at 04:28
  • 1
    If the private function has that much logic in it, it is possible that you should move that private function to a separate angular service. It is generally a bad practice to test private functions in unit tests. It removes the flexibility of your code. – Ricconnect Oct 12 '15 at 10:13
  • But still, what if I want to test Private Function in the Unit Test? Is there a way to test Private Function in Unit Test or Not? – Ankit Prajapati Apr 04 '18 at 17:37
  • No it is not possible to test private functions in unit tests, so you need to expose the functions inside the controller or move it to another service as a public function – Ricconnect May 01 '18 at 09:36