0

Working on converting some old broken AngularJS unit tests to TypeScript and came across this error:

Error: [$injector:unpr] Unknown provider: myComponentDirectiveProvider <- myComponentDirective

In every example of AngularJS unit testing I've seen, they have a main module with everything on it, and they inject that main module into the unit test with angular.mock.module('theWholeStinkingApp'). That is nice for a little tutorial, but I'm dealing with a very large application with hundreds of components and dozens of services. Not to mention filters and other directives. It doesn't work to just inject the whole app into a unit test. Nor do I think it's a good idea to start up the whole app just to unit test a component (kind of defeats the purpose of unit testing when you have to include everything you own in every test).

For testing services I can just create my module in the test in a beforeEach like this, so I'm mocking dependencies, but injecting the real service I want to test:

angular.mock.module(($provide) => {
  $provide.service('firstDependency', FirstDependencyMock);
  $provide.service('secondDependency', SecondDependencyMock);
  $provide.service('serviceIWantToTest', ServiceIWantToTest);
});

I can't figure out how to do this and inject a Component. Any help would be appreciated.

Please keep in mind, I do not want to have to angular.mock.module('theWholeApp') to get this working. I just want to create a mock module and attach my component to it.

Here's a slimmed down version of what I'm doing.

Component looks something like this

angular.module('theWholeApp', [])
  .component('myComponent', {
    controller: MyComponentController, // class is defined elsewhere
    templateUrl: 'path/to/my/template.html'
  )
  ... // 100+ other components;

Here's the test:

describe('MyComponent', () => {
  beforeEach(() => {
    // angular.mock.module('theWholeApp'); This is not an option.

    // Creating a mock module with just the dependencies I need for this test
    angular.mock.module(($provide) => {
        $provide.service('firstDependency', FirstDependencyMock);
        $provide.service('secondDependency', SecondDependencyMock);
    });

    // Tried adding this to register the controller, but it doesn't help.  
    angular.mock.module(($controllerProvider) => {
        $controllerProvider.register('MyComponentController', MyComponentController);
    });

    angular.mock.inject(($injector) => {
      $rootScope = $injector.get('$rootScope');
      $componentController = $injector.get('$componentController');
      firstDependency= $injector.get('firstDependency');
      secondDependency= $injector.get('secondDependency');

      $scope = $rootScope.$new();
    });
  });

  describe('doSomething', () => {
    it('exists', () => {
      controller = $componentController('myComponent', {$scope});
        expect(controller.doSomething).toBeDefined();
      });
  });
});

Obviously, this isn't the live code, just a representation. Hopefully I got all the made up names right. The point is, I want to create a mock module and add my component to it so a call to $componentController('myComponent', {$scope}) works.

Thanks!

1 Answer 1

0

Alright, the answer I came up with is deceptively simple, but I've never seen it done anywhere. If anyone can tell me why it's a bad idea and suggest something better, I'm all for it.

The answer is to just create the module you want then immediately add it to the test.

describe('MyComponent', () => {

  // Create a new module with the things you need.
  // Putting it outside the "beforeEach" because it only needs to be created once.
  angular.module('myComponentTestModule', [])
    .component('myComponent', {
      controller: MyComponentController, // class is defined elsewhere
      templateUrl: 'path/to/my/template.html'
    )
    .service('firstDependency', FirstDependencyMock)
    .service('secondDependency', SecondDependencyMock);

  beforeEach(() => {

    // Add the new module to the test suite
    angular.mock.module('myComponentTestModule');

    angular.mock.inject(($injector) => {
      $rootScope = $injector.get('$rootScope');
      $componentController = $injector.get('$componentController');
      firstDependency= $injector.get('firstDependency');
      secondDependency= $injector.get('secondDependency');

      $scope = $rootScope.$new();
    });
  });

  describe('doSomething', () => {
    it('exists', () => {
      controller = $componentController('myComponent', {$scope});
        expect(controller.doSomething).toBeDefined();
      });
  });
});
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.