Mock components, services and more out of annoying dependencies for simplification of Angular testing
ng-mocks
facilitates Angular testing and helps to:
- mock Components, Directives, Pipes, Modules, Services and Tokens
- reduce boilerplate in tests
- access declarations via simple interface
The current version of the library has been tested and can be used with:
Angular | ng-mocks | Jasmine | Jest | Ivy |
---|---|---|---|---|
14 | latest | yes | yes | yes |
13 | latest | yes | yes | yes |
12 | latest | yes | yes | yes |
11 | latest | yes | yes | yes |
10 | latest | yes | yes | yes |
9 | latest | yes | yes | yes |
8 | latest | yes | yes | |
7 | latest | yes | yes | |
6 | latest | yes | yes | |
5 | latest | yes | yes |
- Live example on StackBlitz
- Live example on CodeSandbox
Global configuration for mocks in src/test.ts
.
In case of jest, src/setupJest.ts
should be used.
// All methods in mock declarations and providers
// will be automatically spied on their creation.
// https://ng-mocks.sudo.eu/extra/auto-spy
ngMocks.autoSpy('jasmine'); // or jest
// ngMocks.defaultMock helps to customize mocks
// globally. Therefore, we can avoid copy-pasting
// among tests.
// https://ng-mocks.sudo.eu/api/ngMocks/defaultMock
ngMocks.defaultMock(AuthService, () => ({
isLoggedIn$: EMPTY,
currentUser$: EMPTY,
}));
An example of a spec for a profile edit component.
// Let's imagine that there is a ProfileComponent
// and it has 3 text fields: email, firstName,
// lastName, and a user can edit them.
// In the following test suite, we would like to
// cover behavior of the component.
describe('profile:classic', () => {
// First of all, we would like to reuse the same
// TestBed in every test.
// ngMocks.faster suppresses reset of TestBed
// after each test and allows to use TestBed,
// MockBuilder and MockRender in beforeAll.
// https://ng-mocks.sudo.eu/api/ngMocks/faster
ngMocks.faster();
// Helps to reset customizations after each test.
MockInstance.scope();
// Let's declare TestBed in beforeAll
// instead of beforeEach.
// The code mocks everything in SharedModule
// and provides a mock AuthService.
beforeAll(async () => {
return TestBed.configureTestingModule({
imports: [
MockModule(SharedModule), // mock
ReactiveFormsModule, // real
],
declarations: [
MockComponent(AvatarComponent), // mock
ProfileComponent, // real
],
providers: [
MockProvider(AuthService), // mock
],
}).compileComponents();
});
// A test to ensure that ProfileComponent
// can be created.
it('should be created', () => {
// MockRender is an advanced version of
// TestBed.createComponent.
// It respects all lifecycle hooks,
// onPush change detection, and creates a
// wrapper component with a template like
// <app-root ...allInputs></profile>
// https://ng-mocks.sudo.eu/api/MockRender
const fixture = MockRender(ProfileComponent);
expect(
fixture.point.componentInstance,
).toEqual(jasmine.any(ProfileComponent));
});
// A test to ensure that the component listens
// on ctrl+s hotkey.
it('saves on ctrl+s hot key', () => {
// A fake profile.
const profile = {
email: 'test2@email.com',
firstName: 'testFirst2',
lastName: 'testLast2',
};
// A spy to track save calls.
// MockInstance helps to configure mock
// providers, declarations and modules
// before their initialization and usage.
// https://ng-mocks.sudo.eu/api/MockInstance
const spySave = MockInstance(
StorageService,
'save',
jasmine.createSpy(), // or jest.fn()
);
// Renders <profile [profile]="params.profile">
// </profile>.
// https://ng-mocks.sudo.eu/api/MockRender
const { point } = MockRender(
ProfileComponent,
{ profile }, // bindings
);
// Let's change the value of the form control
// for email addresses with a random value.
// ngMocks.change finds a related control
// value accessor and updates it properly.
// https://ng-mocks.sudo.eu/api/ngMocks/change
ngMocks.change(
'[name=email]', // css selector
'test3@em.ail', // an email address
);
// Let's ensure that nothing has been called.
expect(spySave).not.toHaveBeenCalled();
// Let's assume that there is a host listener
// for a keyboard combination of ctrl+s,
// and we want to trigger it.
// ngMocks.trigger helps to emit events via
// simple interface.
// https://ng-mocks.sudo.eu/api/ngMocks/trigger
ngMocks.trigger(point, 'keyup.control.s');
// The spy should be called with the user
// and the random email address.
expect(spySave).toHaveBeenCalledWith({
email: 'test3@em.ail',
firstName: profile.firstName,
lastName: profile.lastName,
});
});
});
Profit.
If you like ng-mocks
, please support it:
Thank you!
P.S. Feel free to contact us if you need help.