Skip to content

Commit

Permalink
feat(MockProvider): simple generators for different types #599
Browse files Browse the repository at this point in the history
  • Loading branch information
satanTime committed May 1, 2022
1 parent 0bc5241 commit 9d90121
Show file tree
Hide file tree
Showing 9 changed files with 496 additions and 15 deletions.
2 changes: 1 addition & 1 deletion .codeclimate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ checks:
enabled: false
similar-code:
config:
threshold: 100
threshold: 150
file-lines:
config:
threshold: 500
Expand Down
132 changes: 132 additions & 0 deletions docs/articles/api/MockProvider.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,138 @@ TestBed.configureTestingModule({
});
```

## useValue

```ts
TestBed.configureTestingModule({
providers: [
MockProvider(Service, {name: 'mock'}, 'useValue'),
// generates
// {
// provide: Service,
// useValue: {name: 'mock},
// }

MockProvider(TOKEN, 'token', 'useValue', true),
// generates
// {
// provide: TOKEN,
// useValue: 'token',
// multi: true,
// }
],
});
```

## useExisting

```ts
TestBed.configureTestingModule({
providers: [
MockProvider(Service, MockService, 'useExisting'),
// generates
// {
// provide: Service,
// useExisting: MockService,
// }

MockProvider(TOKEN, FAKE, 'useExisting', true),
// generates
// {
// provide: TOKEN,
// useExisting: FAKE,
// multi: true,
// }
],
});
```

## useClass

```ts
TestBed.configureTestingModule({
providers: [
MockProvider(Service, MockService, 'useClass'),
// generates
// {
// provide: Service,
// useClass: MockService,
// }

MockProvider(Service, MockService, 'useClass', true),
// generates
// {
// provide: Service,
// useClass: MockService,
// multi: true,
// }

MockProvider(Service, MockService, 'useClass', [DbService]),
// generates
// {
// provide: Service,
// useClass: MockService,
// deps: [DbService],
// }

MockProvider(Service, MockService, 'useClass', {
multi: true,
deps: [DbService],
}),
// generates
// {
// provide: Service,
// useClass: MockService,
// deps: [DbService],
// multi: true,
// }
],
});
```

## useFactory

```ts
TestBed.configureTestingModule({
providers: [
MockProvider(Service, () => new MockService(), 'useFactory'),
// generates
// {
// provide: Service,
// useFactory: () => new MockService(),
// }

MockProvider(Service, () => new MockService(), 'useFactory', true),
// generates
// {
// provide: Service,
// useFactory: () => new MockService(),
// multi: true,
// }

MockProvider(Service, () => new MockService(), 'useFactory', [DbService]),
// generates
// {
// provide: Service,
// useFactory: (db) => new MockService(db),
// deps: [DbService],
// }

MockProvider(Service, MockService, 'useFactory', {
multi: true,
deps: [DbService],
}),
// generates
// {
// provide: Service,
// useFactory: (db) => new MockService(db),
// deps: [DbService],
// multi: true,
// }
],
});
```

## Simple example

Let's pretend that in an Angular application `TargetComponent` depends on `DependencyService` service,
Expand Down
9 changes: 9 additions & 0 deletions libs/ng-mocks/src/lib/mock-builder/mock-builder.promise.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { TestBed, TestModuleMetadata } from '@angular/core/testing';

import { flatten, mapValues } from '../common/core.helpers';
import { Type } from '../common/core.types';
import funcGetName from '../common/func.get-name';
import { isNgDef } from '../common/func.is-ng-def';
import { isNgModuleDefWithProviders } from '../common/func.is-ng-module-def-with-providers';
import ngMocksUniverse from '../common/ng-mocks-universe';
Expand Down Expand Up @@ -142,6 +143,14 @@ export class MockBuilderPromise implements IMockBuilder {
const { def, providers } = normaliseModule(input);

const { config, mock } = parseMockArguments(def, a1, a2, defaultMock);
if (isNgDef(mock) && isNgDef(input) && !isNgDef(input, 't')) {
throw new Error(
[
`MockBuilder.mock(${funcGetName(input)}) received a class when its shape is expected.`,
'Please try ngMocks.defaultMock instead.',
].join(' '),
);
}

const existing = this.mockDef.has(def) ? this.defProviders.get(def) : [];
this.wipe(def);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@ export default (
mock: any;
} => {
let mock: any = def === a1 ? defaultMockValue : a1;
let config: any = a2 ? a2 : a1 !== defaultMockValue ? a1 : undefined;
if (isNgDef(def, 'p') && typeof a1 === 'function' && a1 !== def) {
mock = a1;
let config: any = a2 ? a2 : a1 !== defaultMockValue && typeof a1 === 'object' ? a1 : undefined;
if (isNgDef(def, 'p') && typeof a1 === 'function' && a1 !== def && !isNgDef(a1, 'p')) {
mock = {
transform: a1,
};
config = a2;
} else if (isNgDef(def, 'i') || !isNgDef(def)) {
config = a2;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,7 @@ export default (def: any, defValue: Map<any, any>): void => {
const instance = defValue.get(def);
ngMocksUniverse.builtDeclarations.set(
def,
typeof instance === 'function'
? MockPipe(def, instance)
: instance && typeof instance === 'object' && typeof instance.transform === 'function'
? MockPipe(def, instance.transform)
: MockPipe(def),
typeof instance?.transform === 'function' ? MockPipe(def, instance.transform) : MockPipe(def),
);
}
};
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ import helperUseFactory from '../../mock-service/helper.use-factory';
import mockProvider from '../../mock-service/mock-provider';
import { IMockBuilderConfigMock } from '../types';

const createInstance = (existing: any, instance: any, config: IMockBuilderConfigMock, isPipeFunc: boolean): any => {
const params = isPipeFunc ? { transform: instance } : instance;
const createInstance = (existing: any, params: any, config: IMockBuilderConfigMock): any => {
if (config.precise) {
return params;
}
Expand All @@ -18,10 +17,9 @@ export default (def: any, defValue: Map<any, any>): void => {
if (isNgDef(def, 'i') && defValue.has(def)) {
const config: IMockBuilderConfigMock = ngMocksUniverse.config.get(def) || {};
const instance = defValue.get(def);
const isPipeFunc = isNgDef(def, 'p') && typeof instance === 'function';
ngMocksUniverse.builtProviders.set(
def,
helperUseFactory(def, undefined, existing => createInstance(existing, instance, config, isPipeFunc)),
helperUseFactory(def, undefined, existing => createInstance(existing, instance, config)),
);
} else if (isNgDef(def, 'i')) {
ngMocksUniverse.builtProviders.set(def, mockProvider(def, true));
Expand Down
148 changes: 146 additions & 2 deletions libs/ng-mocks/src/lib/mock-provider/mock-provider.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
import { FactoryProvider, InjectionToken, Provider } from '@angular/core';
import {
ClassProvider,
ExistingProvider,
FactoryProvider,
InjectionToken,
Provider,
StaticClassProvider,
ValueProvider,
} from '@angular/core';

import { AnyType } from '../common/core.types';
import funcImportExists from '../common/func.import-exists';
Expand Down Expand Up @@ -82,9 +90,145 @@ export function MockProvider<I>(provider: InjectionToken<I>, useValue?: Partial<
*/
export function MockProvider<I = any>(provider: string, useValue?: Partial<I>): FactoryProvider;

export function MockProvider(provide: any, overrides: any = defaultValue): Provider {
/**
* MockProvider generates useValue based on passed parameters.
*
* @see https://ng-mocks.sudo.eu/api/MockProvider#useValue
*
* ```ts
* TestBed.configureTestingModule({
* providers: [
* MockProvider(AuthService, {isLoggedIn: true}, 'useValue'),
* MockProvider(APP_ROUTES, 5, 'useValue', true), // multi flag
* ],
* });
* ```
*/
export function MockProvider<I>(
provider: AnyType<I> | InjectionToken<I>,
value: ValueProvider['useValue'],
style: 'useValue',
multi?: ValueProvider['multi'],
): ValueProvider;

/**
* MockProvider generates useExisting based on passed parameters.
*
* @see https://ng-mocks.sudo.eu/api/MockProvider#useExisting
*
* ```ts
* TestBed.configureTestingModule({
* providers: [
* MockProvider(AuthService, MockAuthService, 'useExisting', true),
* MockProvider(APP_ROUTES, MOCK_ROUTES, 'useExisting', true), // multi flag
* ],
* });
* ```
*/
export function MockProvider<I>(
provider: AnyType<I> | InjectionToken<I>,
value: ExistingProvider['useExisting'],
style: 'useExisting',
multi?: ExistingProvider['multi'],
): ExistingProvider;

/**
* MockProvider generates useClass based on passed parameters.
*
* @see https://ng-mocks.sudo.eu/api/MockProvider#useClass
*
* ```ts
* TestBed.configureTestingModule({
* providers: [
* MockProvider(AuthService, MockAuthService, 'useClass', [ctorDep1, ctorDep2]),
* MockProvider(UserService, MockUserService, 'useClass', {
* multi: true, // multi flag
* deps: [ctorDep1, ctorDep2],
* }),
* ],
* });
* ```
*/
export function MockProvider<I>(
provider: AnyType<I> | InjectionToken<I>,
value: StaticClassProvider['useClass'],
style: 'useClass',
multiDeps?:
| StaticClassProvider['multi']
| StaticClassProvider['deps']
| {
multi?: StaticClassProvider['multi'];
deps?: StaticClassProvider['deps'];
},
): ClassProvider;

/**
* MockProvider generates useFactory based on passed parameters.
*
* @see https://ng-mocks.sudo.eu/api/MockProvider#useFactory
*
* ```ts
* TestBed.configureTestingModule({
* providers: [
* MockProvider(AuthService, (dep1, dep2) => {
* // ...
* }, 'useFactory', [ctorDep1, ctorDep2]),
* MockProvider(UserService, (dep1, dep2) => {
* // ...
* }, 'useFactory', {
* multi: true, // multi flag
* deps: [ctorDep1, ctorDep2],
* }),
* ],
* });
* ```
*/
export function MockProvider<I>(
provider: AnyType<I> | InjectionToken<I>,
value: FactoryProvider['useFactory'],
style: 'useFactory',
multiDeps?:
| FactoryProvider['multi']
| FactoryProvider['deps']
| {
multi?: FactoryProvider['multi'];
deps?: FactoryProvider['deps'];
},
): FactoryProvider;

export function MockProvider(
provide: any,
overrides: any = defaultValue,
style?: 'useValue' | 'useExisting' | 'useClass' | 'useFactory',
flags:
| boolean
| any[]
| {
deps?: any[];
multi?: boolean;
} = {},
): Provider {
funcImportExists(provide, 'MockProvider');

const { deps, multi } =
typeof flags === 'boolean'
? { deps: undefined, multi: flags }
: Array.isArray(flags)
? {
deps: flags,
multi: undefined,
}
: flags;

if (style) {
return {
provide,
[style]: overrides,
deps,
multi,
};
}

return helperUseFactory(
provide,
() => MockService(provide),
Expand Down
Loading

0 comments on commit 9d90121

Please sign in to comment.