How to mock natively component dependencies
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 () 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.
Top comments (0)