Angular testing

How to mock natively standalone component dependencies

Redin Gaetan
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.

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.

--

--