Angular2 Inject ElementRef in unit test
Asked Answered
W

5

45

I am trying to test a component that receives a reference to ElementRef through DI.

import { Component, OnInit, ElementRef } from '@angular/core';

@Component({
  selector: 'cp',
  templateUrl: '...',
  styleUrls: ['...']
})
export class MyComponent implements OnInit {

  constructor(private elementRef: ElementRef) {
    //stuffs
  }

  ngAfterViewInit() {
    // things
  }

  ngOnInit() {
  }
}

and the test:

import {
  beforeEach,
  beforeEachProviders,
  describe,
  expect,
  it,
  inject,
} from '@angular/core/testing';
import { ComponentFixture, TestComponentBuilder } from '@angular/compiler/testing';
import { Component, Renderer, ElementRef } from '@angular/core';
import { By } from '@angular/platform-browser';

describe('Component: My', () => {
  let builder: TestComponentBuilder;

  beforeEachProviders(() => [MyComponent]);
  beforeEach(inject([TestComponentBuilder], function (tcb: TestComponentBuilder) {
    builder = tcb;
  }));

  it('should inject the component', inject([MyComponent],
      (component: MyComponent) => {
    expect(component).toBeTruthy();
  }));

  it('should create the component', inject([], () => {
    return builder.createAsync(MyComponentTestController)
      .then((fixture: ComponentFixture<any>) => {
        let query = fixture.debugElement.query(By.directive(MyComponent));
        expect(query).toBeTruthy();
        expect(query.componentInstance).toBeTruthy();
      });
  }));
});

@Component({
  selector: 'test',
  template: `
    <cp></cp>
  `,
  directives: [MyComponent]
})
class MyTestController {
}

Both the component and the test blueprint have been generated by Angular-cli. Now, I can't figure out which provider, if any, I should add in the beforeEachProviders for the injection of ElementRef to be successful. When I run ng test I got Error: No provider for ElementRef! (MyComponent -> ElementRef).

Wigging answered 27/7, 2016 at 20:58 Comment(1)
this remains a stickler in angular testing even in 9.1.2Tumbler
S
27

On Angular 2.2.3:

export class MockElementRef extends ElementRef {}

Then in the test:

beforeEach(async(() => {
  TestBed.configureTestingModule({
    providers: [
      //more providers
      { provide: ElementRef, useClass: MockElementRef }
    ]
  }).compileComponents();
}));
Stratus answered 25/12, 2016 at 11:58 Comment(2)
Using Angular 5 I also needed to add the property nativeElement to the MockElementRef: class MockElementRef extends ElementRef { nativeElement = {}; }Indebted
Does not work with components in the component under test. The component under test has a template with other components which have a directive which uses Host ElementRef. I searched the web for a solution, but nothing helped. Error: No provider for ElementRef Directive: constructor(@Host() host: ElementRef) (Angular 5)Flamsteed
A
33

I encounter Can't resolve all parameters for ElementRef: (?) Error using the mock from @gilad-s in angular 2.4

Modified the mock class to:

export class MockElementRef extends ElementRef {
  constructor() { super(null); }
}

resolves the test error.

Reading from the angular source code here: https://github.com/angular/angular/blob/master/packages/core/testing/src/component_fixture.ts#L17-L60 the elementRef of the fixture is not created from the mock injection. And in normal development, we do not explicitly provide ElementRef when injecting to a component. I think TestBed should allow the same behaviour.

Acetate answered 7/3, 2017 at 6:27 Comment(1)
If you're calling -- say, ref.nativeElement.remove() -- in the component, I'd recommend using something like new MockElementRef( document.createElement('div') ).Musician
S
27

On Angular 2.2.3:

export class MockElementRef extends ElementRef {}

Then in the test:

beforeEach(async(() => {
  TestBed.configureTestingModule({
    providers: [
      //more providers
      { provide: ElementRef, useClass: MockElementRef }
    ]
  }).compileComponents();
}));
Stratus answered 25/12, 2016 at 11:58 Comment(2)
Using Angular 5 I also needed to add the property nativeElement to the MockElementRef: class MockElementRef extends ElementRef { nativeElement = {}; }Indebted
Does not work with components in the component under test. The component under test has a template with other components which have a directive which uses Host ElementRef. I searched the web for a solution, but nothing helped. Error: No provider for ElementRef Directive: constructor(@Host() host: ElementRef) (Angular 5)Flamsteed
W
12

To inject an ElementRef:

  1. Create a mock
class MockElementRef implements ElementRef {
  nativeElement = {};
}
  1. Provide the mock to the component under test
beforeEachProviders(() => [Component, provide(ElementRef, { useValue: new MockElementRef() })]);

EDIT: This was working on rc4. Final release introduced breaking changes and invalidates this answer.

Wigging answered 28/7, 2016 at 21:39 Comment(3)
This will no longer work in Angular2 final, because oft breaking changes.Fetch
Indeed. I am updating the answer.Wigging
in order to be AoT compliant, should be provided with {provide: ElementRef, useFactory: mockElementFactory} where mockElementFactory is an exported function returning new MockElementRef().Eugeneeugenia
F
1

A good way is to use spyOn and spyOnProperty to instant mock the methods and properties as needed. spyOnProperty expects 3 properties and you need to pass get or set as third property. spyOn works with class and method and returns required value.

Example

const div = fixture.debugElement.query(By.css('.ellipsis-overflow'));
// now mock properties
spyOnProperty(div.nativeElement, 'clientWidth', 'get').and.returnValue(1400);
spyOnProperty(div.nativeElement, 'scrollWidth', 'get').and.returnValue(2400);

Here I am setting the get of clientWidth of div.nativeElement object.

Floyfloyd answered 18/10, 2017 at 19:55 Comment(1)
Not working on Angular 8.2. Mocked values are not returned.Familiarize
P
1

It started showing up after I did a package update in my Angular project.

I tried all the solutions above and none of them worked for me. The problem occurred when I ran the npm run test command.

I managed to solve by updating all jest dependencies. In this case, the dependencies I updated were @briebug/jest-schematic, @types/jest, jest-preset-angular and jest

Payload answered 5/4, 2022 at 19:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.