diff --git a/.circleci/config.yml b/.circleci/config.yml index 46db5620bf..b9d246c329 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -225,6 +225,25 @@ jobs: key: a-min-<< pipeline.parameters.lockindex >>-{{ arch }}-{{ checksum "e2e/a-min/package-lock.json" }} paths: - ./e2e/a-min/node_modules + 'Performance': + docker: + - image: satantime/puppeteer-node:14.17.0-buster + steps: + - checkout + - restore_cache: + key: root-<< pipeline.parameters.lockindex >>-{{ arch }}-{{ checksum "package-lock.json" }} + - run: + name: Default + command: KARMA_SUITE=tests-performance/test.spec.ts npm run test + - run: + name: TestBed + command: KARMA_SUITE=tests-performance/test-bed.spec.ts npm run test + - run: + name: MockBuilder + command: KARMA_SUITE=tests-performance/mock-builder.spec.ts npm run test + - run: + name: MockRender + command: KARMA_SUITE=tests-performance/mock-render.spec.ts npm run test 'Angular 5 ES5': docker: - image: satantime/puppeteer-node:14.17.0-buster @@ -755,6 +774,9 @@ workflows: - 'Install': requires: - Core + - 'Performance': + requires: + - Core - 'Angular 5 ES5': requires: - Install diff --git a/.codeclimate.yml b/.codeclimate.yml index f4ecf40475..8cdfaae7a5 100644 --- a/.codeclimate.yml +++ b/.codeclimate.yml @@ -13,6 +13,7 @@ exclude_patterns: - 'test-reports/' - 'tests-angular/' - 'tests-failures/' + - 'tests-performance/' - 'tests/' - 'tmp/' - '.codeclimate.yml' diff --git a/karma.conf.ts b/karma.conf.ts index 89d77a0651..4faba29c19 100644 --- a/karma.conf.ts +++ b/karma.conf.ts @@ -8,6 +8,32 @@ process.on('infrastructure_error', error => { process.env.CHROME_BIN = require('puppeteer').executablePath(); +const suite: any[] = []; +if (!process.env.KARMA_SUITE) { + suite.push({ + pattern: './libs/ng-mocks/src/lib/**/*.ts', + watched: true, + }); + suite.push({ + pattern: './examples/**/*.ts', + watched: true, + }); + suite.push({ + pattern: './tests/**/*.ts', + watched: true, + }); +} else if (process.env.KARMA_SUITE === 'perf') { + suite.push({ + pattern: './tests-performance/**/*.ts', + watched: true, + }); +} else { + suite.push({ + pattern: process.env.KARMA_SUITE, + watched: true, + }); +} + export default (config: KarmaTypescriptConfig) => { config.set({ autoWatch: false, @@ -43,12 +69,17 @@ export default (config: KarmaTypescriptConfig) => { }, }, files: [ - 'empty.ts', - 'karma-test-shim.ts', - 'libs/ng-mocks/src/index.ts', - { pattern: 'libs/ng-mocks/src/lib/**/*.ts' }, - { pattern: 'examples/**/*.ts' }, - { pattern: 'tests/**/*.ts' }, + './empty.ts', + './karma-test-shim.ts', + { + pattern: './libs/ng-mocks/src/index.ts', + watched: true, + }, + { + pattern: './libs/ng-mocks/src/lib/**/!(*.spec|*.fixtures).ts', + watched: true, + }, + ...suite, ], frameworks: ['jasmine', 'karma-typescript'], junitReporter: { diff --git a/libs/ng-mocks/src/lib/common/core.config.ts b/libs/ng-mocks/src/lib/common/core.config.ts index 3d278c4220..b3f6063688 100644 --- a/libs/ng-mocks/src/lib/common/core.config.ts +++ b/libs/ng-mocks/src/lib/common/core.config.ts @@ -3,6 +3,7 @@ import { ApplicationModule } from '@angular/core'; export default { flags: ['cacheModule', 'cacheComponent', 'cacheDirective', 'cacheProvider', 'correctModuleExports'], + mockRenderCacheSize: 25, neverMockModule: [ApplicationModule, CommonModule], neverMockProvidedFunction: [ 'DomRendererFactory2', @@ -19,4 +20,5 @@ export default { 'InjectionToken EventManagerPlugins', // EVENT_MANAGER_PLUGINS 'InjectionToken HammerGestureConfig', // HAMMER_GESTURE_CONFIG ], + onTestBedFlushNeed: 'warn', }; diff --git a/libs/ng-mocks/src/lib/common/ng-mocks-universe.ts b/libs/ng-mocks/src/lib/common/ng-mocks-universe.ts index a130076128..9e0ebbb397 100644 --- a/libs/ng-mocks/src/lib/common/ng-mocks-universe.ts +++ b/libs/ng-mocks/src/lib/common/ng-mocks-universe.ts @@ -49,7 +49,7 @@ ngMocksUniverse.touches = new Set(); ngMocksUniverse.global.set('flags', { // @deprecated and will be changed in A13 to 'throw' - onTestBedFlushNeed: 'warn', + onTestBedFlushNeed: coreConfig.onTestBedFlushNeed, }); ngMocksUniverse.getLocalMocks = () => { diff --git a/libs/ng-mocks/src/lib/mock-helper/mock-helper.object.ts b/libs/ng-mocks/src/lib/mock-helper/mock-helper.object.ts index ea62be38a0..6a63393295 100644 --- a/libs/ng-mocks/src/lib/mock-helper/mock-helper.object.ts +++ b/libs/ng-mocks/src/lib/mock-helper/mock-helper.object.ts @@ -1,3 +1,4 @@ +import coreConfig from '../common/core.config'; import ngMocksUniverse from '../common/ng-mocks-universe'; import mockHelperCrawl from './crawl/mock-helper.crawl'; @@ -40,11 +41,21 @@ export default { autoSpy: mockHelperAutoSpy, change: mockHelperChange, click: mockHelperClick, - config: (config: { onTestBedFlushNeed?: 'throw' | 'warn' | 'i-know-but-disable' }) => { + config: (config: { + mockRenderCacheSize?: number | null; + onTestBedFlushNeed?: 'throw' | 'warn' | 'i-know-but-disable' | null; + }) => { const flags = ngMocksUniverse.global.get('flags'); - if (config.onTestBedFlushNeed !== undefined) { + if (config.onTestBedFlushNeed === null) { + flags.onTestBedFlushNeed = coreConfig.onTestBedFlushNeed; + } else if (config.onTestBedFlushNeed !== undefined) { flags.onTestBedFlushNeed = config.onTestBedFlushNeed; } + if (config.mockRenderCacheSize === null) { + ngMocksUniverse.global.delete('mockRenderCacheSize'); + } else if (config.mockRenderCacheSize !== undefined) { + ngMocksUniverse.global.set('mockRenderCacheSize', config.mockRenderCacheSize); + } }, crawl: mockHelperCrawl, defaultMock: mockHelperDefaultMock, diff --git a/libs/ng-mocks/src/lib/mock-helper/mock-helper.reset.ts b/libs/ng-mocks/src/lib/mock-helper/mock-helper.reset.ts index 161cfeb07d..764de763ed 100644 --- a/libs/ng-mocks/src/lib/mock-helper/mock-helper.reset.ts +++ b/libs/ng-mocks/src/lib/mock-helper/mock-helper.reset.ts @@ -3,6 +3,7 @@ import ngMocksUniverse from '../common/ng-mocks-universe'; export default (): void => { ngMocksUniverse.builtDeclarations = new Map(); + ngMocksUniverse.builtProviders = new Map(); ngMocksUniverse.cacheDeclarations = new Map(); ngMocksUniverse.cacheProviders = new Map(); ngMocksUniverse.config = new Map(); diff --git a/libs/ng-mocks/src/lib/mock-helper/mock-helper.ts b/libs/ng-mocks/src/lib/mock-helper/mock-helper.ts index d805697967..d9f59a0108 100644 --- a/libs/ng-mocks/src/lib/mock-helper/mock-helper.ts +++ b/libs/ng-mocks/src/lib/mock-helper/mock-helper.ts @@ -35,7 +35,10 @@ export const ngMocks: { */ click(elSelector: HTMLElement | DebugNodeSelector, payload?: Partial): void; - config(config: { onTestBedFlushNeed?: 'throw' | 'warn' | 'i-know-but-disable' }): void; + config(config: { + mockRenderCacheSize?: number | null; + onTestBedFlushNeed?: 'throw' | 'warn' | 'i-know-but-disable' | null; + }): void; /** * @see https://ng-mocks.sudo.eu/api/ngMocks/crawl diff --git a/libs/ng-mocks/src/lib/mock-render/func.create-wrapper.ts b/libs/ng-mocks/src/lib/mock-render/func.create-wrapper.ts index b931004995..4409f57c8a 100644 --- a/libs/ng-mocks/src/lib/mock-render/func.create-wrapper.ts +++ b/libs/ng-mocks/src/lib/mock-render/func.create-wrapper.ts @@ -1,7 +1,9 @@ import { Component, Directive } from '@angular/core'; +import coreConfig from '../common/core.config'; import coreDefineProperty from '../common/core.define-property'; import { Type } from '../common/core.types'; +import ngMocksUniverse from '../common/ng-mocks-universe'; import helperDefinePropertyDescriptor from '../mock-service/helper.define-property-descriptor'; import funcGenerateTemplate from './func.generate-template'; @@ -43,12 +45,50 @@ const generateWrapper = ({ bindings, options, inputs }: any) => { return MockRenderComponent; }; +const getCache = () => { + const caches: Array & Record<'cacheKey', any[]>> = ngMocksUniverse.config.get('MockRenderCaches') ?? []; + if (caches.length === 0) { + ngMocksUniverse.config.set('MockRenderCaches', caches); + } + + return caches; +}; + +const checkCache = (caches: Array & Record<'cacheKey', any[]>>, cacheKey: any[]): undefined | Type => { + for (const cache of caches) { + if (cache.cacheKey.length !== cacheKey.length) { + continue; + } + let isValid = true; + for (let i = 0; i < cacheKey.length; i += 1) { + if (cache.cacheKey[i] !== cacheKey[i]) { + isValid = false; + break; + } + } + if (isValid) { + return cache; + } + } + + return undefined; +}; + export default ( template: any, meta: Directive, bindings: undefined | null | any[], flags: Record, ): Type => { + const caches = getCache(); + + // nulls help to detect defaults + const cacheKey = [template, ...(bindings ?? [null]), ...(flags.providers ?? [null])]; + let ctor = checkCache(caches, cacheKey); + if (ctor) { + return ctor; + } + const mockTemplate = funcGenerateTemplate(template, { ...meta, bindings }); const options: Component = { providers: flags.providers, @@ -56,5 +96,10 @@ export default ( template: mockTemplate, }; - return generateWrapper({ ...meta, bindings, options }); + ctor = generateWrapper({ ...meta, bindings, options }); + coreDefineProperty(ctor, 'cacheKey', cacheKey, false); + caches.unshift(ctor as any); + caches.splice(ngMocksUniverse.global.get('mockRenderCacheSize') ?? coreConfig.mockRenderCacheSize); + + return ctor; }; diff --git a/tests-performance/mock-builder.spec.ts b/tests-performance/mock-builder.spec.ts new file mode 100644 index 0000000000..92ccf06106 --- /dev/null +++ b/tests-performance/mock-builder.spec.ts @@ -0,0 +1,167 @@ +import { + Component, + Directive, + Injectable, + NgModule, +} from '@angular/core'; +import { TestBed } from '@angular/core/testing'; +import { MockBuilder, ngMocks } from 'ng-mocks'; + +@Injectable() +class TargetService { + public readonly name = 'target'; +} + +@Directive({ + selector: 'target', +}) +class TargetDirective {} + +@Component({ + selector: 'target', + template: '{{ service.name }}', +}) +class TargetComponent { + public constructor(public readonly service: TargetService) {} +} + +@NgModule({ + declarations: [TargetComponent, TargetDirective], + exports: [TargetComponent], + providers: [TargetService], +}) +class TargetModule {} + +describe('performance:MockBuilder', () => { + let timeStandard = 0; + let timeMockBuilder = 0; + let timeFasterBeforeEach = 0; + let timeFasterBeforeAll = 0; + + jasmine.getEnv().addReporter({ + jasmineDone: () => { + // tslint:disable-next-line no-console + console.log(`performance:MockBuilder`); + // tslint:disable-next-line no-console + console.log(`Time standard: ${timeStandard}`); + // tslint:disable-next-line no-console + console.log(`Time MockBuilder: ${timeMockBuilder}`); + // tslint:disable-next-line no-console + console.log( + `Ration standard / MockBuilder: ${ + timeStandard / timeMockBuilder + }`, + ); + // tslint:disable-next-line no-console + console.log(`Time beforeEach: ${timeFasterBeforeEach}`); + // tslint:disable-next-line no-console + console.log( + `Ratio standard / beforeEach: ${ + timeStandard / timeFasterBeforeEach + }`, + ); + // tslint:disable-next-line no-console + console.log(`Time beforeAll: ${timeFasterBeforeAll}`); + // tslint:disable-next-line no-console + console.log( + `Ratio beforeEach / beforeAll: ${ + timeFasterBeforeEach / timeFasterBeforeAll + }`, + ); + }, + }); + + describe('standard', () => { + beforeAll(() => (timeStandard = Date.now())); + afterAll(() => (timeStandard = Date.now() - timeStandard)); + + beforeEach(() => + TestBed.configureTestingModule({ + imports: [TargetModule], + }).compileComponents(), + ); + + for (let i = 0; i < 100; i += 1) { + it(`#${i}`, () => { + const fixture = TestBed.createComponent(TargetComponent); + fixture.detectChanges(); + expect(ngMocks.formatText(fixture)).toEqual('target'); + }); + } + }); + + describe('faster:MockBuilder', () => { + beforeAll(() => (timeMockBuilder = Date.now())); + afterAll(() => (timeMockBuilder = Date.now() - timeMockBuilder)); + + beforeEach(() => + MockBuilder([TargetComponent, TargetService], TargetModule), + ); + + for (let i = 0; i < 100; i += 1) { + it(`#${i}`, () => { + const fixture = TestBed.createComponent(TargetComponent); + fixture.detectChanges(); + expect(ngMocks.formatText(fixture)).toEqual('target'); + }); + } + }); + + describe('faster:beforeEach', () => { + ngMocks.faster(); + + beforeAll(() => (timeFasterBeforeEach = Date.now())); + afterAll( + () => + (timeFasterBeforeEach = Date.now() - timeFasterBeforeEach), + ); + + beforeEach(() => + MockBuilder([TargetComponent, TargetService], TargetModule), + ); + + for (let i = 0; i < 100; i += 1) { + it(`#${i}`, () => { + const fixture = TestBed.createComponent(TargetComponent); + fixture.detectChanges(); + expect(ngMocks.formatText(fixture)).toEqual('target'); + }); + } + }); + + describe('faster:beforeAll', () => { + ngMocks.faster(); + + beforeAll(() => (timeFasterBeforeAll = Date.now())); + afterAll( + () => (timeFasterBeforeAll = Date.now() - timeFasterBeforeAll), + ); + + beforeAll(() => + MockBuilder([TargetComponent, TargetService], TargetModule), + ); + + for (let i = 0; i < 100; i += 1) { + it(`#${i}`, () => { + const fixture = TestBed.createComponent(TargetComponent); + fixture.detectChanges(); + expect(ngMocks.formatText(fixture)).toEqual('target'); + }); + } + }); + + it('ensures that faster is faster', () => { + // Usually, it is faster, but it is fine if we are down for 25% + expect(timeStandard / timeFasterBeforeEach).toBeGreaterThan(0.75); + + // beforeEach should be definitely slower than beforeAll + expect( + timeFasterBeforeEach / timeFasterBeforeAll, + ).toBeGreaterThan(0.75); + + // without faster should be always slower + expect(timeMockBuilder / timeFasterBeforeEach).toBeGreaterThan( + 0.75, + ); + }); +}); diff --git a/tests-performance/mock-render.spec.ts b/tests-performance/mock-render.spec.ts new file mode 100644 index 0000000000..f3c296e27c --- /dev/null +++ b/tests-performance/mock-render.spec.ts @@ -0,0 +1,136 @@ +import { + Component, + Directive, + Injectable, + NgModule, +} from '@angular/core'; +import { TestBed } from '@angular/core/testing'; +import { MockBuilder, MockRender, ngMocks } from 'ng-mocks'; + +@Injectable() +class TargetService { + public readonly name = 'target'; +} + +@Directive({ + selector: 'target', +}) +class TargetDirective {} + +@Component({ + selector: 'target', + template: '{{ service.name }}', +}) +class TargetComponent { + public constructor(public readonly service: TargetService) {} +} + +@NgModule({ + declarations: [TargetComponent, TargetDirective], + exports: [TargetComponent], + providers: [TargetService], +}) +class TargetModule {} + +describe('performance:MockRender', () => { + let timeStandard = 0; + let timeFasterBeforeEach = 0; + let timeFasterBeforeAll = 0; + + jasmine.getEnv().addReporter({ + jasmineDone: () => { + // tslint:disable-next-line no-console + console.log(`performance:MockRender`); + // tslint:disable-next-line no-console + console.log(`Time standard: ${timeStandard}`); + // tslint:disable-next-line no-console + console.log(`Time beforeEach: ${timeFasterBeforeEach}`); + // tslint:disable-next-line no-console + console.log( + `Ratio standard / beforeEach: ${ + timeStandard / timeFasterBeforeEach + }`, + ); + // tslint:disable-next-line no-console + console.log(`Time beforeAll: ${timeFasterBeforeAll}`); + // tslint:disable-next-line no-console + console.log( + `Ratio beforeEach / beforeAll: ${ + timeFasterBeforeEach / timeFasterBeforeAll + }`, + ); + }, + }); + + describe('standard', () => { + beforeAll(() => (timeStandard = Date.now())); + afterAll(() => (timeStandard = Date.now() - timeStandard)); + + beforeEach(() => + TestBed.configureTestingModule({ + imports: [TargetModule], + }).compileComponents(), + ); + + for (let i = 0; i < 100; i += 1) { + it(`#${i}`, () => { + expect( + ngMocks.formatText(MockRender(TargetComponent)), + ).toEqual('target'); + }); + } + }); + + describe('faster:beforeEach', () => { + ngMocks.faster(); + + beforeAll(() => (timeFasterBeforeEach = Date.now())); + afterAll( + () => + (timeFasterBeforeEach = Date.now() - timeFasterBeforeEach), + ); + + beforeEach(() => + MockBuilder([TargetComponent, TargetService], TargetModule), + ); + + for (let i = 0; i < 100; i += 1) { + it(`#${i}`, () => { + expect( + ngMocks.formatText(MockRender(TargetComponent)), + ).toEqual('target'); + }); + } + }); + + describe('faster:beforeAll', () => { + ngMocks.faster(); + + beforeAll(() => (timeFasterBeforeAll = Date.now())); + afterAll( + () => (timeFasterBeforeAll = Date.now() - timeFasterBeforeAll), + ); + + beforeAll(() => + MockBuilder([TargetComponent, TargetService], TargetModule), + ); + + for (let i = 0; i < 100; i += 1) { + it(`#${i}`, () => { + expect( + ngMocks.formatText(MockRender(TargetComponent)), + ).toEqual('target'); + }); + } + }); + + it('ensures that faster is faster', () => { + // Usually, it is faster, but it is fine if we are down for 25% + expect(timeStandard / timeFasterBeforeEach).toBeGreaterThan(0.75); + + // beforeEach should be definitely slower than beforeAll + expect( + timeFasterBeforeEach / timeFasterBeforeAll, + ).toBeGreaterThan(0.75); + }); +}); diff --git a/tests-performance/test-bed.spec.ts b/tests-performance/test-bed.spec.ts new file mode 100644 index 0000000000..e6fa3ce858 --- /dev/null +++ b/tests-performance/test-bed.spec.ts @@ -0,0 +1,140 @@ +import { + Component, + Directive, + Injectable, + NgModule, +} from '@angular/core'; +import { TestBed } from '@angular/core/testing'; +import { ngMocks } from 'ng-mocks'; + +@Injectable() +class TargetService { + public readonly name = 'target'; +} + +@Directive({ + selector: 'target', +}) +class TargetDirective {} + +@Component({ + selector: 'target', + template: '{{ service.name }}', +}) +class TargetComponent { + public constructor(public readonly service: TargetService) {} +} + +@NgModule({ + declarations: [TargetComponent, TargetDirective], + exports: [TargetComponent], + providers: [TargetService], +}) +class TargetModule {} + +describe('performance:TestBed', () => { + let timeStandard = 0; + let timeFasterBeforeEach = 0; + let timeFasterBeforeAll = 0; + + jasmine.getEnv().addReporter({ + jasmineDone: () => { + // tslint:disable-next-line no-console + console.log(`performance:TestBed`); + // tslint:disable-next-line no-console + console.log(`Time standard: ${timeStandard}`); + // tslint:disable-next-line no-console + console.log(`Time beforeEach: ${timeFasterBeforeEach}`); + // tslint:disable-next-line no-console + console.log( + `Ratio standard / beforeEach: ${ + timeStandard / timeFasterBeforeEach + }`, + ); + // tslint:disable-next-line no-console + console.log(`Time beforeAll: ${timeFasterBeforeAll}`); + // tslint:disable-next-line no-console + console.log( + `Ratio beforeEach / beforeAll: ${ + timeFasterBeforeEach / timeFasterBeforeAll + }`, + ); + }, + }); + + describe('standard', () => { + beforeAll(() => (timeStandard = Date.now())); + afterAll(() => (timeStandard = Date.now() - timeStandard)); + + beforeEach(() => + TestBed.configureTestingModule({ + imports: [TargetModule], + }).compileComponents(), + ); + + for (let i = 0; i < 100; i += 1) { + it(`#${i}`, () => { + const fixture = TestBed.createComponent(TargetComponent); + fixture.detectChanges(); + expect(ngMocks.formatText(fixture)).toEqual('target'); + }); + } + }); + + describe('faster:beforeEach', () => { + ngMocks.faster(); + + beforeAll(() => (timeFasterBeforeEach = Date.now())); + afterAll( + () => + (timeFasterBeforeEach = Date.now() - timeFasterBeforeEach), + ); + + beforeEach(() => + TestBed.configureTestingModule({ + imports: [TargetModule], + }).compileComponents(), + ); + + for (let i = 0; i < 100; i += 1) { + it(`#${i}`, () => { + const fixture = TestBed.createComponent(TargetComponent); + fixture.detectChanges(); + expect(ngMocks.formatText(fixture)).toEqual('target'); + }); + } + }); + + describe('faster:beforeAll', () => { + ngMocks.faster(); + + beforeAll(() => + TestBed.configureTestingModule({ + imports: [TargetModule], + }).compileComponents(), + ); + + beforeAll(() => (timeFasterBeforeAll = Date.now())); + afterAll( + () => (timeFasterBeforeAll = Date.now() - timeFasterBeforeAll), + ); + + for (let i = 0; i < 100; i += 1) { + it(`#${i}`, () => { + const fixture = TestBed.createComponent(TargetComponent); + fixture.detectChanges(); + expect(ngMocks.formatText(fixture)).toEqual('target'); + }); + } + }); + + it('ensures that faster is faster', () => { + // Usually, it is faster, but it is fine if we are down for 25% + expect(timeStandard / timeFasterBeforeEach).toBeGreaterThan(0.75); + + // beforeEach should be definitely slower than beforeAll + expect( + timeFasterBeforeEach / timeFasterBeforeAll, + ).toBeGreaterThan(0.75); + }); +}); diff --git a/tests-performance/test.spec.ts b/tests-performance/test.spec.ts new file mode 100644 index 0000000000..85646b804f --- /dev/null +++ b/tests-performance/test.spec.ts @@ -0,0 +1,98 @@ +import { + Component, + Directive, + Injectable, + NgModule, +} from '@angular/core'; +import { TestBed } from '@angular/core/testing'; +import { MockBuilder, MockRenderFactory, ngMocks } from 'ng-mocks'; + +@Injectable() +class TargetService { + public readonly name = 'target'; +} + +@Directive({ + selector: 'target', +}) +class TargetDirective {} + +@Component({ + selector: 'target', + template: '{{ service.name }}', +}) +class TargetComponent { + public constructor(public readonly service: TargetService) {} +} + +@NgModule({ + declarations: [TargetComponent, TargetDirective], + exports: [TargetComponent], + providers: [TargetService], +}) +class TargetModule {} + +describe('performance', () => { + let timeStandard = 0; + let timeFaster = 0; + + jasmine.getEnv().addReporter({ + jasmineDone: () => { + // tslint:disable-next-line no-console + console.log(`performance`); + // tslint:disable-next-line no-console + console.log(`Time standard: ${timeStandard}`); + // tslint:disable-next-line no-console + console.log(`Time faster: ${timeFaster}`); + // tslint:disable-next-line no-console + console.log( + `Ratio standard / faster: ${timeStandard / timeFaster}`, + ); + }, + }); + + describe('standard', () => { + beforeAll(() => (timeStandard = Date.now())); + afterAll(() => (timeStandard = Date.now() - timeStandard)); + + beforeEach(() => + TestBed.configureTestingModule({ + declarations: [TargetComponent, TargetDirective], + providers: [TargetService], + }).compileComponents(), + ); + + for (let i = 0; i < 100; i += 1) { + it(`#${i}`, () => { + const fixture = TestBed.createComponent(TargetComponent); + fixture.detectChanges(); + expect(ngMocks.formatText(fixture)).toEqual('target'); + }); + } + }); + + describe('faster', () => { + ngMocks.faster(); + + beforeAll(() => (timeFaster = Date.now())); + afterAll(() => (timeFaster = Date.now() - timeFaster)); + + const factory = MockRenderFactory(TargetComponent); + beforeAll(() => + MockBuilder([TargetComponent, TargetService], TargetModule), + ); + beforeAll(factory.configureTestBed); + + for (let i = 0; i < 100; i += 1) { + it(`#${i}`, () => { + const fixture = factory(); + expect(ngMocks.formatText(fixture)).toEqual('target'); + }); + } + }); + + it('ensures that faster is faster', () => { + // Usually, it is faster, but it is fine if we are down for 25% + expect(timeStandard / timeFaster).toBeGreaterThan(0.75); + }); +}); diff --git a/tests/issue-572/test.spec.ts b/tests/issue-572/test.spec.ts index 1b73b831fc..076d674272 100644 --- a/tests/issue-572/test.spec.ts +++ b/tests/issue-572/test.spec.ts @@ -14,6 +14,7 @@ describe('issue-572', () => { ngMocks.faster(); let consoleWarn: typeof console.warn; + beforeAll(() => ngMocks.config({ mockRenderCacheSize: 0 })); beforeAll(() => MockBuilder(TargetComponent)); beforeAll(() => (consoleWarn = console.warn)); @@ -23,7 +24,10 @@ describe('issue-572', () => { afterAll(() => { console.warn = consoleWarn; - ngMocks.config({ onTestBedFlushNeed: 'warn' }); + ngMocks.config({ + mockRenderCacheSize: null, + onTestBedFlushNeed: null, + }); }); it('warns via console on TestBed change', () => { diff --git a/tsconfig.spec.json b/tsconfig.spec.json index 265ae130b6..7307626ad6 100644 --- a/tsconfig.spec.json +++ b/tsconfig.spec.json @@ -7,5 +7,11 @@ }, "files": ["libs/ng-mocks/src/index.ts", "empty.ts", "karma-test-shim.ts", "karma.conf.ts"], "exclude": ["e2e", "node_modules"], - "include": ["libs/ng-mocks/src/lib/**/*", "examples/**/*.spec.ts", "tests/**/*.spec.ts", "tests-failures/**/*"] + "include": [ + "libs/ng-mocks/src/lib/**/*.ts", + "examples/**/*.ts", + "tests/**/*.ts", + "tests-failures/**/*.ts", + "tests-performance/**/*.ts" + ] }