When you test components with multiple layers of children, this is cost. Cost because of rendering. Cost because of the dependency maintenance for all children.
Shallow testing skips compiling and rendering child components during test execution, makes a true unit test.
Performance. Where unit test can be run within 5ms, a component test is 50ms. Usually, Page components are the biggest in the codebase. I found such tests can run for 900ms to even 5 seconds. For single test. This is a no-go when having over 10k tests. Shallow tests might be good quick win practice optimizing tests.
Isolation. It’s quite common to add a new dependency to a resuable directive or component. It tends to break other tests because something is missing in dependency injection tree.
This one is simple. You add schema NO_ERRORS_SCHEMA
and drop all imports, providers and declarations outside what is directly needed by the component we test.
import { NO_ERRORS_SCHEMA } from '@angular/core';
TestBed.configureTestingModule({
schemas: [NO_ERRORS_SCHEMA],
declarations: [ComponentWeTest],
});
Typically, this removes lots of setup code and unrelated mocks from the test suite.
The initialization of the test can take much more time and resources than the test itself. The most costly part of it is resolving Dependency Injection tree from all listed modules and compilation of all child components.
Knowing that, check:
See example of module that will be great to optimize:
TestBed.configureTestingModule({
declarations: [
ComponentWeTest,
MockOfChildComponent1,
MockOfChildComponent2,
MockOfChildComponent3, // and so on for up to
MockOfChildComponent4, // 10, 20 mocked child components
MockOfChildComponent5,
MockOfChildComponent6,
],
imports: [
YourModuleWrappingAllDependenciesForAllModules,
MatButtonComponent, // + other component library stuff
DependentModule1,
DependentModule2,
DependentModule3,
DependentModule4, // and so on for up to
DependentModule1, // 10, 20 imported modules
],
})
Apply NO_ERRORS_SCHEMA
and drop all imports, providers and declarations. Keep only direct dependencies and mocks related to ComponentWeTest
. It’s cleaner, faster and easier to maintain.s
NO_ERRORS_SCHEMA
?⚠️ It’s not silver-bullet! It skips some part of the compilation, so it makes a test truly unit test.
Page
component tests tend to be less complex in my experience.@ViewChild
annotations in a given component. Since the component is not rendered, @ViewChild
won’t find a proper reference.@Input
name changes, because components are not included in compilationI found it interesting to learn how Shallow Testing practices differ between frameworks. React have a simple API of mount()
and shallow()
:
mount()
, everything will be compiled no matter how huge component tree is, shallow()
, all children component will be ignored. No exceptions. To my knowledge, you can’t mix them and do integration test of some components. This is one of the common complaints about the practice. I found Angular approach a little better here.
Since Angular have module system, you can define which components should be included in test and skipping the rest with NO_ERRORS_SCHEMA
option.
TestBed.configureTestingModule({
schemas: [NO_ERRORS_SCHEMA],
declarations: [
ComponentWeTest,
MaterialTableComponent, // <-- this child will be recognised and rendered
],
});
This is great for precise integration testing.