From a70756cddcf221d5a8b8dd885ffaec87e6e8f2f9 Mon Sep 17 00:00:00 2001 From: timdeschryver <28659384+timdeschryver@users.noreply.github.com> Date: Tue, 9 Jul 2019 21:40:49 +0200 Subject: [PATCH] feat(jest): add createMock The createMock function creates a mock object for an Angular type Use provideMock to create an Angular provider for the type --- .../src/jest-utils/create-mock.ts | 37 +++++++++++++ .../testing-library/src/jest-utils/index.ts | 1 + .../tests/jest-utils/create-mock.spec.ts | 52 +++++++++++++++++++ .../tests/providers/module-provider.spec.ts | 1 - 4 files changed, 90 insertions(+), 1 deletion(-) create mode 100644 projects/testing-library/src/jest-utils/create-mock.ts create mode 100644 projects/testing-library/tests/jest-utils/create-mock.spec.ts diff --git a/projects/testing-library/src/jest-utils/create-mock.ts b/projects/testing-library/src/jest-utils/create-mock.ts new file mode 100644 index 00000000..3613abf1 --- /dev/null +++ b/projects/testing-library/src/jest-utils/create-mock.ts @@ -0,0 +1,37 @@ +import { Type, Provider } from '@angular/core'; + +export type Mock = T & { [K in keyof T]: T[K] & jest.Mock }; + +export function createMock(type: Type): Mock { + const mock: any = {}; + + function mockFunctions(proto: any) { + if (!proto) { + return; + } + + for (const prop of Object.getOwnPropertyNames(proto)) { + if (prop === 'constructor') { + continue; + } + + const descriptor = Object.getOwnPropertyDescriptor(proto, prop); + if (typeof descriptor.value === 'function') { + mock[prop] = jest.fn(); + } + } + + mockFunctions(Object.getPrototypeOf(proto)); + } + + mockFunctions(type.prototype); + + return mock; +} + +export function provideMock(type: Type): Provider { + return { + provide: type, + useFactory: () => createMock(type), + }; +} diff --git a/projects/testing-library/src/jest-utils/index.ts b/projects/testing-library/src/jest-utils/index.ts index ea63e26e..5cd8585b 100644 --- a/projects/testing-library/src/jest-utils/index.ts +++ b/projects/testing-library/src/jest-utils/index.ts @@ -1 +1,2 @@ export * from './configure-test-suite'; +export * from './create-mock'; diff --git a/projects/testing-library/tests/jest-utils/create-mock.spec.ts b/projects/testing-library/tests/jest-utils/create-mock.spec.ts new file mode 100644 index 00000000..1dd04b51 --- /dev/null +++ b/projects/testing-library/tests/jest-utils/create-mock.spec.ts @@ -0,0 +1,52 @@ +import { createMock, provideMock, Mock } from '../../src/jest-utils'; +import { render } from '../../src/public_api'; +import { Component } from '@angular/core'; +import { TestBed } from '@angular/core/testing'; + +class FixtureService { + constructor(private foo: string, public bar: string) {} + + print() { + console.log(this.foo, this.bar); + } +} + +@Component({ + selector: 'fixture', + template: ` + + `, +}) +export class FixtureComponent { + constructor(private service: FixtureService) {} + + print() { + this.service.print(); + } +} + +it('mocks all functions', () => { + const mock = createMock(FixtureService); + expect(mock.print.mock).toBeDefined(); +}); + +it('provides a mock service', async () => { + const { click, getByText } = await render(FixtureComponent, { + providers: [provideMock(FixtureService)], + }); + const service = TestBed.get(FixtureService); + + click(getByText('Print')); + expect(service.print).toHaveBeenCalledTimes(1); +}); + +it('is possible to write a mock implementation', async done => { + const { click, getByText } = await render(FixtureComponent, { + providers: [provideMock(FixtureService)], + }); + + const service = TestBed.get(FixtureService) as Mock; + service.print.mockImplementation(() => done()); + + click(getByText('Print')); +}); diff --git a/projects/testing-library/tests/providers/module-provider.spec.ts b/projects/testing-library/tests/providers/module-provider.spec.ts index 2b34109c..48dcabc7 100644 --- a/projects/testing-library/tests/providers/module-provider.spec.ts +++ b/projects/testing-library/tests/providers/module-provider.spec.ts @@ -1,7 +1,6 @@ import { Injectable } from '@angular/core'; import { Component } from '@angular/core'; import { render } from '../../src/public_api'; -import { TestBed } from '@angular/core/testing'; // tslint:disable: no-use-before-declare test('shows the service value', async () => {