tuff
tuff

Reputation: 5153

How to change constant value in a Karma test

I have an Angular directive which sets the value of some $scope property based on the value of an injected constant. I want to test that this value is being correctly initialized from the constant, so I would like to change the constant's value within an individual it block. (Preferably within one, but changing the value between multiple blocks would be OK too)

Is this possible, and how can I do it?

simplified example:

//////// directive ////////
angular.module('myApp.directives', [])
.constant('THE_CONSTANT', 'hello world')
.directive('myDirective', ['THE_CONSTANT', function (THE_CONSTANT) {

    return {
        restrict: 'E',
        link: function ($scope) {
            $scope.propertyBasedOnConstant = THE_CONSTANT;
        }
    };
}]);

//////// test ////////
describe('myDirective', function () {
    var $element, $scope;

    beforeEach(module('myApp.directives'));

    beforeEach(module(function ($provide) {
        $provide.constant('THE_CONSTANT', 'foo');
    }));

    beforeEach(inject(function ($rootScope, $compile) {
        $scope = $rootScope;
        $element = angular.element('<my-directive></my-directive>');
        $compile($element)($scope);
        $scope.$digest();
    }));

    afterEach(function () {
        $scope.$destroy();
        $element.remove();
    });

    it("should correctly reflect the constant's value", function() {
        expect( $scope.propertyBasedOnConstant ).to.equal('foo');

        // now I want to change the constant's value and re-initialize the directive
    });    
});

Upvotes: 3

Views: 2887

Answers (2)

user1364910
user1364910

Reputation:

You are providing the constant into an undefined module.

Change your beforeEach block to something like this and it should just work™:

var $scope, $element, MOCKED_CONSTANT;

beforeEach(function () {
  MOCKED_CONSTANT = 'foo';

  module('myApp.directives', function ($provide) {
    $provide.constant('THE_CONSTANT', MOCKED_CONSTANT);
  });

  inject(function ($rootScope, $compile) {
    $scope       = $rootScope.$new(); // Don't forget to call .$new()!
    var template = angular.element('<my-directive></my-directive');
    $element     = $compile(template)($scope); // Store the reference to the compiled element, not the raw string.
    $scope.$digest();
  });
});

it("should correctly reflect the constant's value", function() {
  expect( $scope.propertyBasedOnConstant ).to.equal(MOCKED_CONSTANT);
  // expect( $scope.propertyBasedOnConstant ).to.equal('foo');
});

If you need to change the constant's value between it's, I would extract the call to module into a helper function, aswell as the inject. Such as:

function setupModule (constant) {
  module('myApp.directives', function ($provide) {
    $provide.constant('THE_CONSTANT', constant);
  });
}

function injectItAll () {
  inject(function ($rootScope, $compile) {
    $scope       = $rootScope.$new(); // Don't forget to call .$new()!
    var template = angular.element('<my-directive></my-directive');
    $element     = $compile(template)($scope); // Store the reference to the compiled element, not the raw string.
    $scope.$digest();
  });
}

And then in your spec you would do:

it('equals banana', function () {
  setupModule('banana');
  injectItAll();
  expect($scope.propertyBasedOnConstant).to.equal('banana');
});

Upvotes: 5

ryeballar
ryeballar

Reputation: 30098

By definition a constant is an identifier with an associated value which cannot be changed. Instead of changing the constant value why not inject the constant itself and use it within your expectations.

describe('myDirective', function () {
    var $element, $scope,
      THE_CONSTANT;

    beforeEach(module('myApp.directives'));

    beforeEach(module(function (_THE_CONSTANT_) {
      THE_CONSTANT = _THE_CONSTANT_;
    }));

    beforeEach(inject(function ($rootScope, $compile) {
        $scope = $rootScope;
        $element = angular.element('<my-directive></my-directive>');
        $compile($element)($scope);
        $scope.$digest();
    }));

    afterEach(function () {
        $scope.$destroy();
        $element.remove();
    });

    it("should correctly reflect the constant's value", function() {
        expect( $scope.propertyBasedOnConstant ).to.equal(THE_CONSTANT);
    });    
});

Upvotes: 1

Related Questions