Skip to content

Commit

Permalink
fix(core): align TestBed interfaces and implementation (#46635)
Browse files Browse the repository at this point in the history
This commit performs various refactoring of the TestBed code to better align interfaces and implementation. The implementation class is also renamed from `TestBedRender3` -> `TestBedImpl`, but the public API name has not changed.

Note: as a part of this change, the TestBed interface became more consistent and typings for multiple methods were updated to account for the fact that the TestBed reference is returned. This was always a runtime behavior of TestBed, which was not reflected in few places in type.

PR Close #46635
  • Loading branch information
AndrewKushnir authored and thePunderWoman committed Jul 13, 2022
1 parent 22c40b4 commit 7968402
Show file tree
Hide file tree
Showing 7 changed files with 167 additions and 299 deletions.
75 changes: 16 additions & 59 deletions goldens/public-api/core/testing/index.md
Expand Up @@ -66,7 +66,7 @@ export function flush(maxTurns?: number): number;
export function flushMicrotasks(): void;

// @public
export const getTestBed: () => TestBed;
export function getTestBed(): TestBed;

// @public
export function inject(tokens: any[], fn: Function): () => any;
Expand Down Expand Up @@ -104,7 +104,7 @@ export interface TestBed {
useJit?: boolean;
}): void;
// (undocumented)
configureTestingModule(moduleDef: TestModuleMetadata): void;
configureTestingModule(moduleDef: TestModuleMetadata): TestBed;
// (undocumented)
createComponent<T>(component: Type<T>): ComponentFixture<T>;
// (undocumented)
Expand All @@ -119,90 +119,47 @@ export interface TestBed {
// (undocumented)
inject<T>(token: ProviderToken<T>, notFoundValue: null, flags?: InjectFlags): T | null;
// (undocumented)
ngModule: Type<any> | Type<any>[];
get ngModule(): Type<any> | Type<any>[];
// (undocumented)
overrideComponent(component: Type<any>, override: MetadataOverride<Component>): void;
overrideComponent(component: Type<any>, override: MetadataOverride<Component>): TestBed;
// (undocumented)
overrideDirective(directive: Type<any>, override: MetadataOverride<Directive>): void;
overrideDirective(directive: Type<any>, override: MetadataOverride<Directive>): TestBed;
// (undocumented)
overrideModule(ngModule: Type<any>, override: MetadataOverride<NgModule>): void;
overrideModule(ngModule: Type<any>, override: MetadataOverride<NgModule>): TestBed;
// (undocumented)
overridePipe(pipe: Type<any>, override: MetadataOverride<Pipe>): void;
overridePipe(pipe: Type<any>, override: MetadataOverride<Pipe>): TestBed;
overrideProvider(token: any, provider: {
useFactory: Function;
deps: any[];
}): void;
}): TestBed;
// (undocumented)
overrideProvider(token: any, provider: {
useValue: any;
}): void;
}): TestBed;
// (undocumented)
overrideProvider(token: any, provider: {
useFactory?: Function;
useValue?: any;
deps?: any[];
}): void;
}): TestBed;
// (undocumented)
overrideTemplateUsingTestingModule(component: Type<any>, template: string): void;
overrideTemplate(component: Type<any>, template: string): TestBed;
// (undocumented)
platform: PlatformRef;
overrideTemplateUsingTestingModule(component: Type<any>, template: string): TestBed;
// (undocumented)
get platform(): PlatformRef;
resetTestEnvironment(): void;
// (undocumented)
resetTestingModule(): void;
resetTestingModule(): TestBed;
}

// @public
export const TestBed: TestBedStatic;

// @public
export interface TestBedStatic {
export interface TestBedStatic extends TestBed {
// (undocumented)
new (...args: any[]): TestBed;
compileComponents(): Promise<any>;
configureCompiler(config: {
providers?: any[];
useJit?: boolean;
}): TestBedStatic;
configureTestingModule(moduleDef: TestModuleMetadata): TestBedStatic;
// (undocumented)
createComponent<T>(component: Type<T>): ComponentFixture<T>;
// @deprecated (undocumented)
get<T>(token: ProviderToken<T>, notFoundValue?: T, flags?: InjectFlags): any;
// @deprecated (undocumented)
get(token: any, notFoundValue?: any): any;
initTestEnvironment(ngModule: Type<any> | Type<any>[], platform: PlatformRef, options?: TestEnvironmentOptions): TestBed;
// (undocumented)
inject<T>(token: ProviderToken<T>, notFoundValue?: T, flags?: InjectFlags): T;
// (undocumented)
inject<T>(token: ProviderToken<T>, notFoundValue: null, flags?: InjectFlags): T | null;
// (undocumented)
overrideComponent(component: Type<any>, override: MetadataOverride<Component>): TestBedStatic;
// (undocumented)
overrideDirective(directive: Type<any>, override: MetadataOverride<Directive>): TestBedStatic;
// (undocumented)
overrideModule(ngModule: Type<any>, override: MetadataOverride<NgModule>): TestBedStatic;
// (undocumented)
overridePipe(pipe: Type<any>, override: MetadataOverride<Pipe>): TestBedStatic;
overrideProvider(token: any, provider: {
useFactory: Function;
deps: any[];
}): TestBedStatic;
// (undocumented)
overrideProvider(token: any, provider: {
useValue: any;
}): TestBedStatic;
// (undocumented)
overrideProvider(token: any, provider: {
useFactory?: Function;
useValue?: any;
deps?: any[];
}): TestBedStatic;
// (undocumented)
overrideTemplate(component: Type<any>, template: string): TestBedStatic;
overrideTemplateUsingTestingModule(component: Type<any>, template: string): TestBedStatic;
resetTestEnvironment(): void;
// (undocumented)
resetTestingModule(): TestBedStatic;
}

// @public
Expand Down
70 changes: 30 additions & 40 deletions packages/core/test/test_bed_spec.ts
Expand Up @@ -6,13 +6,12 @@
* found in the LICENSE file at https://angular.io/license
*/

import {CommonModule} from '@angular/common';
import {APP_INITIALIZER, ChangeDetectorRef, Compiler, Component, Directive, ElementRef, ErrorHandler, getNgModuleById, Inject, Injectable, InjectionToken, Injector, Input, LOCALE_ID, ModuleWithProviders, NgModule, Optional, Pipe, Type, ViewChild, ɵsetClassMetadata as setClassMetadata, ɵɵdefineComponent as defineComponent, ɵɵdefineInjector as defineInjector, ɵɵdefineNgModule as defineNgModule, ɵɵelementEnd as elementEnd, ɵɵelementStart as elementStart, ɵɵsetNgModuleScope as setNgModuleScope, ɵɵtext as text} from '@angular/core';
import {getTestBed, TestBed} from '@angular/core/testing/src/test_bed';
import {TestBedImpl} from '@angular/core/testing/src/r3_test_bed';
import {TestBed} from '@angular/core/testing/src/test_bed';
import {By} from '@angular/platform-browser';
import {expect} from '@angular/platform-browser/testing/src/matchers';

import {TestBedRender3} from '../testing/src/r3_test_bed';
import {TEARDOWN_TESTING_MODULE_ON_DESTROY_DEFAULT, THROW_ON_UNKNOWN_ELEMENTS_DEFAULT, THROW_ON_UNKNOWN_PROPERTIES_DEFAULT} from '../testing/src/test_bed_common';

const NAME = new InjectionToken<string>('name');
Expand Down Expand Up @@ -129,7 +128,7 @@ describe('TestBed', () => {
it('should apply scopes correctly for components in the lazy-loaded module', () => {
// Reset TestBed to the initial state, emulating an invocation of a first test.
// Check `TestBed.checkGlobalCompilationFinished` for additional info.
(getTestBed() as any)._globalCompilationChecked = false;
TestBedImpl.INSTANCE.globalCompilationChecked = false;

@Component({
selector: 'root',
Expand Down Expand Up @@ -170,7 +169,7 @@ describe('TestBed', () => {

describe('TestBed with Standalone types', () => {
beforeEach(() => {
getTestBed().resetTestingModule();
TestBed.resetTestingModule();
});

it('should override providers on standalone component itself', () => {
Expand Down Expand Up @@ -498,7 +497,7 @@ describe('TestBed with Standalone types', () => {

describe('TestBed', () => {
beforeEach(() => {
getTestBed().resetTestingModule();
TestBed.resetTestingModule();
TestBed.configureTestingModule({imports: [HelloWorldModule]});
});

Expand Down Expand Up @@ -681,15 +680,15 @@ describe('TestBed', () => {
expect(hello.nativeElement).toHaveText('Hello World!');

// override the template
getTestBed().resetTestingModule();
TestBed.resetTestingModule();
TestBed.configureTestingModule({imports: [HelloWorldModule]});
TestBed.overrideComponent(GreetingCmp, {set: {template: `Bonjour {{ name }}`}});
hello = TestBed.createComponent(HelloWorld);
hello.detectChanges();
expect(hello.nativeElement).toHaveText('Bonjour World!');

// restore the original template by calling `.resetTestingModule()`
getTestBed().resetTestingModule();
TestBed.resetTestingModule();
TestBed.configureTestingModule({imports: [HelloWorldModule]});
hello = TestBed.createComponent(HelloWorld);
hello.detectChanges();
Expand Down Expand Up @@ -1330,7 +1329,7 @@ describe('TestBed', () => {
class ProvidesErrorHandler {
}

getTestBed().resetTestingModule();
TestBed.resetTestingModule();
TestBed.configureTestingModule({imports: [ProvidesErrorHandler, HelloWorldModule]});

expect(TestBed.inject(ErrorHandler)).toEqual(jasmine.any(CustomErrorHandler));
Expand Down Expand Up @@ -1859,28 +1858,24 @@ describe('TestBed', () => {


describe('TestBed module teardown', () => {
// Cast the `TestBed` to the internal data type since we're testing private APIs.
let TestBed: TestBedRender3;

beforeEach(() => {
TestBed = getTestBed() as unknown as TestBedRender3;
TestBed.resetTestingModule();
});

it('should tear down the test module by default', () => {
expect(TestBed.shouldTearDownTestingModule()).toBe(true);
expect(TestBedImpl.INSTANCE.shouldTearDownTestingModule()).toBe(true);
});

it('should be able to configure the teardown behavior', () => {
TestBed.configureTestingModule({teardown: {destroyAfterEach: false}});
expect(TestBed.shouldTearDownTestingModule()).toBe(false);
expect(TestBedImpl.INSTANCE.shouldTearDownTestingModule()).toBe(false);
});

it('should reset the teardown behavior back to the default when TestBed is reset', () => {
TestBed.configureTestingModule({teardown: {destroyAfterEach: false}});
expect(TestBed.shouldTearDownTestingModule()).toBe(false);
expect(TestBedImpl.INSTANCE.shouldTearDownTestingModule()).toBe(false);
TestBed.resetTestingModule();
expect(TestBed.shouldTearDownTestingModule()).toBe(true);
expect(TestBedImpl.INSTANCE.shouldTearDownTestingModule()).toBe(true);
});

it('should destroy test module providers when test module teardown is enabled', () => {
Expand Down Expand Up @@ -2053,88 +2048,83 @@ describe('TestBed module teardown', () => {
});

it('should rethrow errors based on the default teardown behavior', () => {
expect(TestBed.shouldRethrowTeardownErrors()).toBe(TEARDOWN_TESTING_MODULE_ON_DESTROY_DEFAULT);
expect(TestBedImpl.INSTANCE.shouldRethrowTeardownErrors())
.toBe(TEARDOWN_TESTING_MODULE_ON_DESTROY_DEFAULT);
});

it('should rethrow errors if the option is omitted and test teardown is enabled', () => {
TestBed.configureTestingModule({teardown: {destroyAfterEach: true}});
expect(TestBed.shouldRethrowTeardownErrors()).toBe(true);
expect(TestBedImpl.INSTANCE.shouldRethrowTeardownErrors()).toBe(true);
});

it('should not rethrow errors if the option is omitted and test teardown is disabled', () => {
TestBed.configureTestingModule({teardown: {destroyAfterEach: false}});
expect(TestBed.shouldRethrowTeardownErrors()).toBe(false);
expect(TestBedImpl.INSTANCE.shouldRethrowTeardownErrors()).toBe(false);
});

it('should rethrow errors if the option is enabled, but teardown is disabled', () => {
TestBed.configureTestingModule({teardown: {destroyAfterEach: false, rethrowErrors: true}});
expect(TestBed.shouldRethrowTeardownErrors()).toBe(true);
expect(TestBedImpl.INSTANCE.shouldRethrowTeardownErrors()).toBe(true);
});

it('should not rethrow errors if the option is disabled, but teardown is enabled', () => {
TestBed.configureTestingModule({teardown: {destroyAfterEach: true, rethrowErrors: false}});
expect(TestBed.shouldRethrowTeardownErrors()).toBe(false);
expect(TestBedImpl.INSTANCE.shouldRethrowTeardownErrors()).toBe(false);
});
});

describe('TestBed module `errorOnUnknownElements`', () => {
// Cast the `TestBed` to the internal data type since we're testing private APIs.
let TestBed: TestBedRender3;

beforeEach(() => {
TestBed = getTestBed() as unknown as TestBedRender3;
TestBed.resetTestingModule();
});

it('should not throw based on the default behavior', () => {
expect(TestBed.shouldThrowErrorOnUnknownElements()).toBe(THROW_ON_UNKNOWN_ELEMENTS_DEFAULT);
expect(TestBedImpl.INSTANCE.shouldThrowErrorOnUnknownElements())
.toBe(THROW_ON_UNKNOWN_ELEMENTS_DEFAULT);
});

it('should not throw if the option is omitted', () => {
TestBed.configureTestingModule({});
expect(TestBed.shouldThrowErrorOnUnknownElements()).toBe(false);
expect(TestBedImpl.INSTANCE.shouldThrowErrorOnUnknownElements()).toBe(false);
});

it('should be able to configure the option', () => {
TestBed.configureTestingModule({errorOnUnknownElements: true});
expect(TestBed.shouldThrowErrorOnUnknownElements()).toBe(true);
expect(TestBedImpl.INSTANCE.shouldThrowErrorOnUnknownElements()).toBe(true);
});

it('should reset the option back to the default when TestBed is reset', () => {
TestBed.configureTestingModule({errorOnUnknownElements: true});
expect(TestBed.shouldThrowErrorOnUnknownElements()).toBe(true);
expect(TestBedImpl.INSTANCE.shouldThrowErrorOnUnknownElements()).toBe(true);
TestBed.resetTestingModule();
expect(TestBed.shouldThrowErrorOnUnknownElements()).toBe(false);
expect(TestBedImpl.INSTANCE.shouldThrowErrorOnUnknownElements()).toBe(false);
});
});

describe('TestBed module `errorOnUnknownProperties`', () => {
// Cast the `TestBed` to the internal data type since we're testing private APIs.
let TestBed: TestBedRender3;

beforeEach(() => {
TestBed = getTestBed() as unknown as TestBedRender3;
TestBed.resetTestingModule();
});

it('should not throw based on the default behavior', () => {
expect(TestBed.shouldThrowErrorOnUnknownProperties()).toBe(THROW_ON_UNKNOWN_PROPERTIES_DEFAULT);
expect(TestBedImpl.INSTANCE.shouldThrowErrorOnUnknownProperties())
.toBe(THROW_ON_UNKNOWN_PROPERTIES_DEFAULT);
});

it('should not throw if the option is omitted', () => {
TestBed.configureTestingModule({});
expect(TestBed.shouldThrowErrorOnUnknownProperties()).toBe(false);
expect(TestBedImpl.INSTANCE.shouldThrowErrorOnUnknownProperties()).toBe(false);
});

it('should be able to configure the option', () => {
TestBed.configureTestingModule({errorOnUnknownProperties: true});
expect(TestBed.shouldThrowErrorOnUnknownProperties()).toBe(true);
expect(TestBedImpl.INSTANCE.shouldThrowErrorOnUnknownProperties()).toBe(true);
});

it('should reset the option back to the default when TestBed is reset', () => {
TestBed.configureTestingModule({errorOnUnknownProperties: true});
expect(TestBed.shouldThrowErrorOnUnknownProperties()).toBe(true);
expect(TestBedImpl.INSTANCE.shouldThrowErrorOnUnknownProperties()).toBe(true);
TestBed.resetTestingModule();
expect(TestBed.shouldThrowErrorOnUnknownProperties()).toBe(false);
expect(TestBedImpl.INSTANCE.shouldThrowErrorOnUnknownProperties()).toBe(false);
});
});

0 comments on commit 7968402

Please sign in to comment.