Sitemap
Redin Gaetan

Redin Gaetan

Lead developer | Tech writer | ReCowd

Angular testing

How to mock natively standalone component dependencies

3 min readMar 1, 2023

--

Hey, I know there are many documentation about this but it seems to not be effective. Why? People does not read documentation… Every time I start a new mission, the developers don’t test their components or do it wrong. In the last case, they never mock their component dependencies and that’s a problem because it’s not a unit test anymore.

I think a little reminder is necessary.

Context

I’m developing a CancelButtonComponent. It’s a material button with a specific text and specific mat-button properties.

I’m working on a standalone component.

I’m using jest and snapshot testing to be sure that the genereted HTML is as expected.

How to

@Component({
selector: 'app-cancel-button',
standalone: true,
template: `<button mat-button color="primary">Cancel</button>`,
imports: [MatButtonModule]
})
export class CancelButton {}

It’s a really simple component and here we just have to control the generated HTML.

Here’s the spec file:

describe('CancelButtonComponent', () => {
let component: CancelButtonComponent;
let fixture: ComponentFixture<CancelButtonComponent>;

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

fixture = TestBed.createComponent(CancelButtonComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

it('should create', () => {
expect(fixture.nativeElement).toMatchSnapshot();
});
});

Here I don’t mock anything.

That’s the generated snapshot:

// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`CancelButtonComponent should create 1`] = `
<div
id="root0"
>
<button
class="mdc-button mat-mdc-button mat-primary mat-mdc-button-base"
color="primary"
mat-button=""
>
<span
class="mat-mdc-button-persistent-ripple mdc-button__ripple"
/>
<span
class="mdc-button__label"
>
Cancel
</span>
<span
class="mat-mdc-focus-indicator"
/>
<span
class="mat-ripple mat-mdc-button-ripple"
matripple=""
/>
<span
class="mat-mdc-button-touch-target"
/>
</button>
</div>
`;

I don’t think this snapshot is really relevant. I don’t care about MatButton specific css classes, I don’t care about all generated spans and that make a big snapshot just for one line of HTML to test. Snapshot is a part of your code it MUST be reviewed.

Now, let’s do better with a mock.

Here’s a simple mock for the MatButton:

@Component({
selector: '[mat-button]',
standalone: true,
template: ` <ng-content></ng-content>`,
})
class MatButtonMock {}

I use the projection (<ng-content/>) to let the button’s text appear in the snapshot.

And that’s how to use it in the test:

beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [CancelButtonComponent],
})
.overrideComponent(CancelButtonComponent, {
remove: {
imports: [MatButtonModule],
},
add: {
imports: [MatButtonMock],
},
})
.compileComponents();

...
});

We just tell to TestBed to remove the import of MatButtonModule for CancelButtonComponent and to replace it with our MatButtonMock.

Get Redin Gaetan’s stories in your inbox

Join Medium for free to get updates from this writer.

Let’s see the snapshot now:

// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`CancelButtonComponent should create 1`] = `
<div
id="root0"
>
<button
color="primary"
mat-button=""
>
Cancel
</button>
</div>
`;

It’s so better. I see only the specifities of my CancelButtonComponent:

  • usage of a mat-button
  • set the color to primary
  • set the text to “Cancel”

It’s more readable, it’s more reviewable and it’s more relevant.

Angular offers other methods to override a component in a test:

  • overrideComponent
  • overridePipe
  • overrideDirective
  • overrideModule
  • overrideProviders

I put here the full spec file if you need it:

describe('CancelButtonComponent', () => {
let component: CancelButtonComponent;
let fixture: ComponentFixture<CancelButtonComponent>;

beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [CancelButtonComponent],
})
.overrideComponent(CancelButtonComponent, {
remove: {
imports: [MatButtonModule],
},
add: {
imports: [MatButtonMock],
},
})
.compileComponents();

fixture = TestBed.createComponent(CancelButtonComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

it('should create', () => {
expect(fixture.nativeElement).toMatchSnapshot();
});
});

Conclusion

Never mind, you are using jest, Karma/jasmine or another testing tool. You always must mock your dependencies (components, services, pipes…) to avoid to be impacted by a third party in your test. And also because we are speaking about unit test.

If you need more examples or other testing use case, let me know in comment. I will try to help you.

Thanks for reading.

--

--

Written by Redin Gaetan

Lead developer | Tech writer | ReCowd