AngularJS, Testing

Angular Testing: Understanding $componentConstructor

Angular testing can be confusing. To make it less confusing, lets talk about a major part: $componentController.

First, what is necessary when setting up a test?

Say that we have this as component controller, and we want to test methodA:

class MyComponentCtrl {
  constructor($element, serviceB) {}

  methodA() {
    return this.$element;
  }

  methodB() {
    return serviceB.run();
  }
}

methodA does not involve serviceB, so does that mean that we don’t need to inject serviceB?

Correct.

However, that does not guarantee that serviceB will not blow up.

Even though no method will be called on the service, its class constructor will still run. If we did anything funky in its constructor, it will choke up our test even though we are not injecting it.

Why? Because under the hood, $conponentConstructor calls the actual $controller, which runs all the normal injection setups. With the way services are injected, angular will grab an instance of serviceB, and store that instance in its internal cache, as a singular.

So make sure that constructors of services are free of anything that can go wrong. E.g., things that does not exist when angular is initialising.

Second, how do we inject $element?

Answer: not through angular.mock.inject.

So in spec, instead of putting it in the place for normal injections, pass it in as variable to $componentController:

beforeEach(inject((_$componentController_) => {
  $componentController = _$componentController_;
}));

it("shall pass", () => {
  const $ctrl = $componentController("myComponent", {$element: angular.element("div")});
  ...
});

And this.$element in our actual code will be referring to this angular.element("div") that we passed in.

Standard