Skip to main content

How to test a standalone directive in Angular application

Here you can find how to test a standalone directive.

A standalone directive has the same feature set as a regular directive. The only possible dependencies for a standalone directive are root services and tokens. In a unit test, developers prefer to mock such dependencies. MockBuilder helps to configure TestBed in such the way.

Let's image we have the next standalone directive:

@Directive({
selector: 'standalone',
standalone: true,
})
class StandaloneDirective implements OnInit {
@Input() public readonly name: string | null = null;

constructor(public readonly rootService: RootService) {}

ngOnInit(): void {
this.rootService.trigger(this.name);
}
}

As we can see, the standalone directive injects RootService, and, ideally, the service should be mocked.

To configure TestBed for that you need to use the next code:

beforeEach(() => {
return MockBuilder(StandaloneDirective);
});

Under the hood it marks StandaloneDirective as kept and sets shallow and export flags:

beforeEach(() => {
return MockBuilder().keep(StandaloneDirective, {
shallow: true,
export: true,
});
});

Now all dependencies of StandaloneDirective are mocks, and the properties, methods, injections of the directive are available for testing.

If you need to keep a dependency, simply call .keep with it. For example, if we wanted to keep RootService then the code would look like:

beforeEach(() => {
return MockBuilder(StandaloneDirective).keep(RootService);
});

Live example

https://github.com/help-me-mom/ng-mocks/tree/master/examples/TestStandaloneDirective/test.spec.ts
import {
Directive,
Injectable,
Input,
OnInit,
} from '@angular/core';

import { MockBuilder, MockRender, ngMocks } from 'ng-mocks';

// A root service we want to mock.
@Injectable({
providedIn: 'root',
})
class RootService {
trigger(name: string | null) {
// does something very cool

return name;
}
}

// A standalone directive we are going to test.
@Directive({
selector: 'standalone',
standalone: true,
})
class StandaloneDirective implements OnInit {
@Input() public readonly name: string | null = null;

constructor(public readonly rootService: RootService) {}

ngOnInit(): void {
this.rootService.trigger(this.name);
}
}

describe('TestStandaloneDirective', () => {
beforeEach(() => {
return MockBuilder(StandaloneDirective);
});

it('renders dependencies', () => {
// Rendering the directive.
MockRender(StandaloneDirective, {
name: 'test',
});

// Asserting that StandaloneDirective calls RootService.trigger.
const rootService = ngMocks.findInstance(RootService);
// it's possible because of autoSpy.
expect(rootService.trigger).toHaveBeenCalledWith('test');
});
});