Isolating with Angular on Karma Jasmine

I have a suite of tests that are distributed across 2 modules.

The first module has tests and for its dependencies I declare mocks to test it without any influence from the dependent module, for example:

        beforeEach(function(){
            angular.mock.module(function ($provide) {
                $provide.value("mockServiceFromModule1", mockServiceFromModule1);
                $provide.value("mockServiceFromModule2", mockServiceFromModule2);
            });

            angular.module('module1', []);
            angular.module('module2', []);
            angular.mock.module('moduleThatIAmTesting');

            angular.mock.inject(function (_$rootScope_, _$q_, _$httpBackend_, ..., somethingFromTestModule) {

            });

    })

      

The second module has a series of tests, and all of them are passed when I only run them.

        beforeEach(function(){

            angular.mock.module('module1');

            angular.mock.inject(function (_$rootScope_, _$q_, _$httpBackend_, ..., somethingFromModule1) {

            });

    })

      

Both tests when run with f (only works with them) work, but when I run the whole test case I get errors, especially regarding module declaration or $httpBackend

.

How can I get jasmine to run each test as if they were the only tests?

I seem to be messing around with angular / modules / $ httpBackEnd in every test and the changes are propagated when a new test is run.

Update 1 I have a jsFiddle presenting the issue. Problem structure:

  • Some tests are done with a module-specific module
  • Later another test wants to test the actual mocked module
  • Since the first graph has already been loaded, we cannot overwrite it and the test fails.

In the JSFiddle the error about $ httpBackend without any glitches is because the request for the expected Get never gets, and it never gets because of a previously loaded empty module

It's important to note that the ORDER of the tests is the only thing that has to do with the failure, as in this JSFiddle with the same tests they go over.

I could of course place a test order and get around this, but I am aiming to find a way to make tests with isolation without worrying about other side effects of the test.

+3


source to share


1 answer


The problem you are facing has to do with how it $compileProvider

handles a new directive registered with a pre-existing name.

Shortly speaking; You are not overriding your old directive , you are creating a secondary with the same name. So the original implementation starts up and tries to grab baz.html

and $httpBackend

, since you haven't configured it to wait for that call.

See the updated script that changed two from the original fiddle .

  • Do not enter parentModule

    in the specification childModule

    . This line is unnecessary and this is part of the reason you are seeing these errors. Oh, and angular.module

    evil in the land of trials. Try not to use it.
  • Decorate the original directive if you want to roll with the same name as the original, or call it something else. I decided to call it something else in the fiddle, but I put the code at the end of my answer to show the decorating method.

Here's a screenshot of what happens in the following scenario:

  • Module A registers the directive baz

    .
  • Module B depends on the modulus of A .
  • Module B registers a directive called baz

    .

As you can imagine, in order to prevent the modular system from becoming on its own, allowing people to rewrite eachothers directives - it will $compileProvider

simply register another directive with the same name. And both will work .

Take this ng-click example or article to describe how we can use multiple directives with the same name.

See the screenshot below for how your situation looks like. multi-dir-same-name

The code on lines 71 through 73 is where you could implement solution # 2 that I mentioned at the beginning of my answer.


Finishing baz

In your beforeEach for, testModule

replace the following:

$compileProvider.directive('baz', function () {
    return {
        restrict: 'E',
        template: '{{value}}<div ng-transclude></div>',
        controllerAs: 'bazController',
        controller: function ($scope, fooService) {
            $scope.value = 'baz' + fooService.get()
        },
        transclude: true
    };
});

      

Wherein:



$provide.decorator('bazDirective', function ($delegate) {
  var dir = $delegate[0];

  dir.template = '{{value}}<div ng-transclude></div>';

  dir.controller = function ($scope, fooService) {
      $scope.value = 'baz' + fooService.get();
  };

  delete dir.templateUrl;

  return $delegate;
});

      

jsFiddle showing the decorator approach


How about a call angular.module('parent', [])

?

You shouldn't be calling angular.module('name', [])

in your specs unless you are using angular-module gist. And even then, it doesn't do much for you in the testing country.

Use only .mock.module

or window.module

, as otherwise you will kill your upcoming specs pertaining to the specified module, since you effectively killed the module definition for the rest of the spec to work.

In addition, the definition of the directive baz

from parentModule

'll automatically have access to your specification testModule

because of the following:

angular.module('parent', []).directive('baz', fn());

angular.module('child', ['parent']);

// In your test: 

module('child'); // Will automatically fetch the modules that 'child' depend on. 

      

So even if , we kill the call angular.module('parent', [])

in your spec, the original baz

definition will be loaded.

So the HTTP request flies away due to the nature of $compileProvider

supporting multiple directives of the same name, and that's the reason your spec package is failing.


Also, as a last note; You set up modules undefined

in your blocks beforeEach

. If the goal is to customize your test module, you are wrong.

The syntax is as follows:

mock.module('child', function ($compileProvider, /** etc **/) {
});

// Not this:
mock.module('child'); 
mock.module(function ($compileProvider, /** etc **/) {
});

      

This can be seen in the screenshot I posted. The property of $$moduleName

your mocked baz

definition is undefined

, whereas I assume you want it to be child

.

+1


source







All Articles