Mock standalone directive in a standalone component
Asked Answered
S

2

6

I'm trying to write a unit test for a standalone component and mock its dependency. This standalone component has the following content:

import { Component, OnInit } from '@angular/core';
import { CommonModule } from '@angular/common';
import { DemoDirectiveDirective } from './demo-directive.directive';

@Component({
  selector: 'app-demo-cmp',
  standalone: true,
  imports: [CommonModule,DemoDirectiveDirective],
  templateUrl: './demo-cmp.component.html',
})
export class DemoCmpComponent implements OnInit {
  constructor() {}

  ngOnInit(): void {}
}

and DemoDirectiveDirective has this content:

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

@Directive({
  selector: '[appDemoDirective]',
  standalone: true,
})
export class DemoDirectiveDirective {
  constructor(private hostElement: ElementRef) {
    this.hostElement.nativeElement.innerHTML += 'Update from directive! ';
  }
}

I want to test DemoCmpComponent like this:

import { ComponentFixture, TestBed } from '@angular/core/testing';

import { DemoCmpComponent } from './demo-cmp.component';

describe('DemoCmpComponent', () => {

  beforeEach(async () => {
    await TestBed.configureTestingModule({
      imports: [DemoCmpComponent],
    }).compileComponents();
  });

  it('should create', () => {
    const fixture = TestBed.createComponent(DemoCmpComponent);
    fixture.detectChanges();

    expect(fixture.nativeElement.querySelector('p').innerHTML).toBe(
      'demo-cmp works!'
    );
  });
});

This test will fail as the the content of the p tag will be Update from directive! demo-cmp works!.

What I'm missing here is the step in which I mock DemoDirectiveDirective which I don't know how to and I don't find any resources for this on Angular page.

Note this is just a demo test, a proof of concept. Please ignore the component names and the superfluous test.

Here is the GitHub repo with the code.

Sarad answered 12/12, 2022 at 10:13 Comment(0)
W
8
// define a mock directive
@Directive({standalone: true, selector: "[appDemoDirective]"})
class MockDemoDirectiveDirective {
  // define whatever mock inputs/methods etc you need here
}

// override imports in TestBed to use the mock directive
TestBed.overrideComponent(DemoCmpComponent, {
  remove: {imports: [DemoDirectiveDirective] },
  add: {imports: [MockDemoDirectiveDirective]}
})
Windsor answered 26/1, 2023 at 7:9 Comment(0)
L
1

One of the easiest ways to mock an angular declaration is to use a mocking library, for example ng-mocks.

ng-mocks supports standalone component, and its MockBuilder does know how to mock imports of standalone declarations.

In your case, the test can be like:

import { ComponentFixture, TestBed } from '@angular/core/testing';
import { MockBuilder } from "ng-mocks";

import { DemoCmpComponent } from './demo-cmp.component';

describe('DemoCmpComponent', () => {

  beforeEach(() => {
    return MockBuilder(DemoCmpComponent);
    // it will mock DemoDirectiveDirective automatically,
    // because it's a dependency of DemoCmpComponent.
  });

  it('should create', () => {
    const fixture = TestBed.createComponent(DemoCmpComponent);
    fixture.detectChanges();

    expect(fixture.nativeElement.querySelector('p').innerHTML).toBe(
      'demo-cmp works!'
    );
  });
});

Here is a live example: https://codesandbox.io/s/bold-kate-1gkymd?file=/src/test.spec.ts:770-812

Lycian answered 18/12, 2022 at 13:45 Comment(1)
Hi! I used this library for testing and it really works, however I was looking for some kind of native solution.Sarad

© 2022 - 2024 — McMap. All rights reserved.