Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Make a module available in a controller that is tested by jasmine via Resharper

I can succesfully test a controller using jasmine via PhantomJs using Resharper 9.2. as a Testrunner.

I followed the instructions on https://blogs.endjin.com/2014/09/unit-testing-angularjs-with-visual-studio-resharper-and-teamcity/ to setup Resharper.

This worked: I can run a test for a controller if i dont specify the modules it depends on:

Controller:

    var moduleName;
(function (moduleName) {
    'use strict';
    var testableController = (function () {
        function testableController($scope) {
            var _this = this;
            this.$scope = $scope;

            $scope.title = "Welcome";
        }
        testableController.className = 'testableController';
        return testableController;
    }());
    moduleName.testableController = testableController;
})(moduleName || (moduleName = {}));

The spec file looks like this

    ///<reference path="~/Scripts/jasmine/jasmine.js"/>
///<reference path="~/Scripts/jasmine/angular.js"/>
///<reference path="~/Scripts/jasmine/angular-mocks.js"/>
///<reference path="~/Scripts/angular-ui/ui-bootstrap.min.js" />
///<reference path="~/Scripts/jasmine/controllers.js"/>
///<reference path="~/Scripts/App/Controllers/testableController.js" />
///<reference path="~/Scripts/App/AppJasmine.js" />
describe("Controllers", function() {

    beforeEach(module("moduleName"));

    describe("Jasmine  testableController", function () {

        var scope,
            controller;

        beforeEach(inject(function ($rootScope, $controller) {
            scope = $rootScope.$new();
            controller = $controller('testableController', { $scope: scope });
        }));

        it('should set the page title as "Welcome"', function () {
            expect(scope.title).toBe('Welcome');
        });

    });
});

The real controller uses angular ui bootstrap "ui.bootstrap". the controller works on the page if I change it to the the next sample but when I try to test it there is an error

Error: [$injector:unpr] Unknown provider: $templateRequestProvider <- $templateRequest <- $uibModal
http://errors.angularjs.org/1.2.24/$injector/unpr?p0=%24templateRequestProvider%20%3C-%20%24templateRequest%20%3C-%20%24uibModal in http://localhost:61032/referenceFile?path=~/webui/trunk/Netvacation.Pegasus.WebUI/Scripts/jasmine/angular.js (line 3802)

Controller with dependency on Bootstrap

angular.module('moduleName', ['ui.bootstrap']);
var moduleName;
(function (moduleName) {
    'use strict';
    var testableController = (function () {
        function testableController($scope, $uibModal) {
            var _this = this;
            this.$scope = $scope;
            this.$uibModal = $uibModal;
            $scope.title = "Welcome";
        }
        testableController.className = 'testableController';
        return testableController;
    }());
    moduleName.testableController = testableController;
})(moduleName || (moduleName = {}));

**EDIT 1 ** I tried

beforeEach(
    function () {
        module("ui.bootstrap");
        module("moduleName");
    }
    );

but have the same error.

Edit 2 I use

http://angular-ui.github.io/bootstrap/ Version: 1.3.3 - 2016-05-22

AngularJS v1.2.24

Edit 3 I dont want to test $uibModal, but mock it away

like image 644
Mathias F Avatar asked Jan 20 '26 03:01

Mathias F


1 Answers

I know two strategies to mock a service, with multiple syntax variants, as everything in Angular... You can simply add an object literal to the controller declaration or you can create you own services and add them to the module using $provider:

If the service is just a wrapper of some data layer or API, you can mock its functionality with a literal object and inject if right in the controller constructor, following your sample syntax this can be done just like this:

var currentAuth;

beforeEach(inject(function ($rootScope, $controller) {
    scope = $rootScope.$new();
    currentAuth = {uid: 1, name: juan, getFriends: function() { ... }};

    controller = $controller('TestableCtrl', {'$scope': $scope, 'currentAuth': currentAuth });
}));

In this case 'currentAuth' is a service that provides the current logged user in the application.

This is only valid if you don't need to inject any service in the functions defined in the object. It would be equivalent to create a value service and inject it in the module. If the methods inside the mocked service need any service themselves, you will have to create a factory or service, add this service to the module and then inject it like any other custom service. See this example that I use to mock angularfire authentication service:

 var $controller, $rootScope, $scope, $location, Auth;

  beforeEach(function(){
    module('planner.login');

    module(function($provide){

      $provide.factory('Auth', function($q){
        return {
          $signInWithEmailAndPassword: function(email, pass) {
            return $q.reject({error: 'ERROR'});
          }
        };
      });

      return null;
    });

  });

  beforeEach(function(){

    inject(function($controller, $rootScope, _Auth_) {
      $scope = $rootScope.$new();
      Auth = _Auth_;

      $controller("TestableCtrl", {
        $scope: $scope,
        Auth: Auth,
        $stateParams: {}
      });
    });

  });

In this sample I am creating a new factory that uses the $q service to return an angular promise (this wouldn't be needed testing on Chrome, but PhantomJS don't have the Promise specification). Note that you need two beforeEach for this, one to add the provider to the module and another one to inject the provider into the controller.

Which one to use depends on what do you want to test and how deeply do you need to mimic the original service behaviour. In your case with uibmodal you probably need to call '.open' at some point and spy that has been called, but you only need to create an object with that property and spy on the object property for that. So the first approach should be enough.

So your code should be something like:

describe("Controllers", function() {

    beforeEach(module("moduleName"));

    describe("Jasmine  testableController", function () {

        var scope,
            controller,
            uibModal;

        beforeEach(inject(function ($rootScope, $controller) {
            scope = $rootScope.$new();
            uibModal = { open: function() { return 'Whatever'; } }
            controller = $controller('testableController', { $scope: scope, $uibModal: uibModal });
        }));

        it('should set the page title as "Welcome"', function () {
            expect(scope.title).toBe('Welcome');
            // you will probably do something like this at some point to verify 
            // that the modal gets opened on click or following any other action:
            // var spy = spyOn(uibModal, 'open');
            // expect(spy).toHaveBeenCalled();
        });

    });
});

I hope it helps, please tell me if something is not clear, I am also learning about testing AngularJS apps and would love to know if this helps with AngularUI.

like image 137
Juanmi Rodriguez Avatar answered Jan 21 '26 19:01

Juanmi Rodriguez



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!