wardbell / bardjs

Spec helpers for testing angular v.1.x apps with Mocha, Jasmine and QUnit
MIT License
178 stars 34 forks source link

How to test Controller with $scope? #30

Open RobDaPraia opened 8 years ago

RobDaPraia commented 8 years ago

Just starting to use bardjs and I donnot know how to set up the test for this test controller, how to inject $scope.

Controller example

(function () {

    'use strict';

    angular
        .module('test', [])
        .controller("TestCtrl", testController);

    testController.$inject = ["$scope"];

    function testController($scope) {

        console.log("TestCtrl Constructor");

        var vm = this;
        vm.activate = activate;
        vm.activate();

        function activate() {
            console.log("TestCtrl activate");

            angular.extend($scope, {
                mapBounds: [0, 0]
            });

        }
    }

})();

I tried to set up a test in the following way, but I get the following error:

Test example

describe("TestController Test 2", function () {

    var controller;
    var scope;

    beforeEach(function () {
        bard.appModule('test');
        bard.inject('$controller', '$log', '$q', '$rootScope', function ($rootScope) {
            scope = $rootScope.$new();
            return { $scope: scope };
        });
    });

    beforeEach(function () {
        controller = $controller('TestCtrl');
    });

    describe('Test 2', function () {
        it('should be created successfully', function () {
            expect(false).to.be.true;
            expect(controller).to.be.defined;
        });

    });

});

Error

Error: [$injector:unpr] http://errors.angularjs.org/1.4.8/$injector/unpr?
p0=$scopeProvider <- $scope <- TestCtrl
        at Error (native)

How can I setup bardjs correctly to test this type of controllers with $scope?

realph commented 8 years ago

I'm having this same problem. Any help?

attilacsanyi commented 8 years ago

Hi Guys,

I just always use $rootScope in the controllers during inject instead of $scope. Several places in hottowel *.spec.js files you can see this line scope = $rootScope; So it seems we have to inject $rootScope if we need scope in bard.js.

Let me know if I am not right @wardbell thanks in advance.

brantphoto commented 8 years ago

Hi @RobDaPraia Just from looking at it... I would think that you need to inject your locals into the controller. Adjust your second beforeEach to look like this. The second argument of $controller function is an object where you can place mock injectables.

 beforeEach(function () {
        controller = $controller('TestCtrl', { $scope: scope });
    });
RobDaPraia commented 8 years ago

Thanks for the responses. Still cannot get this working

@brantPhoto: tried your option but got the following error:

TypeError: Cannot read property '$$hashKey' of undefined

Also think that using bard.inject() should take care of injecting the scope, so you don't need to do that in the second beforeEach

@attilacsanyi : yes that was as well my first solution using rootscope. The unit tests works, but the since the controller has an $scope.$on() and listens for event. You get a memory leak, when you switch pages and each time the controller is created, but is never destroyed because of the link to rootscope, so I prefer to prevent rootscope when it is not needed

instantaphex commented 8 years ago

I'm having this same issue. Is there a workaround that doesn't involve never using $scope again?

TheDevelolper commented 8 years ago

I'm having the same problem. What's the point of mocking up a controller if you can't even use the $scope variable? seems pretty useless!

TheDevelolper commented 8 years ago

I think I got it working!

After a lot of typing, sweat and tears I managed to get this working. Here's an explanation and example:

$controller is an angular service that allows you to create controllers with the $controller object but you have to supply the scope.

Example:

   var scope       = {}; //empty object (this will be populated by the real controller
   var controller  = $controller('sidebarController',{$scope:scope});

    // now you can test against your scope object. 
     it('Sidebar menu items should exist', function() {
             expect(scope.menuItems).to.not.be.empty;
    });

My initial thoughts were that the scope wouldn't be a real controller object but as the real implementation of the controller manipulates the scope object you inject it's as good as testing a real scope.

Her's the code for for my controller:

app.controller('sidebarController', function($scope) { $scope.menuItems = [{text:'Project', url:'views/project.html', icon:'book'}]; });

I really hope this helps you... let me know if it does... it would make me happy to know that I helped someone in need. :-)

TheDevelolper commented 8 years ago

@wardbell @johnpapa I think I've answered this ticket for you... Perhaps it can be closed?

scottmanny commented 7 years ago

@KiransHub - Thank you very much. You helped me not only solve my problem, but understand why it wasn't working. I had to add a dummy $on method to my scope object because I use that in my controller, but that was the only modification: var scope = { $on: function() {}};

TheDevelolper commented 7 years ago

@scottmanny really glad I helped you.

I've been jumping into the angular 2 world recently. It's very different from angular.

TheDevelolper commented 7 years ago

I tend not to follow anything by John Papa or Ward bell anymore as they never seem to respond to issues or questions (in my personal experience). My guess is that they're too busy making new projects to really support their current onces. It's a shame really because they're obveously extremely innovative and tallented.