Skip to content

Commit

Permalink
feat: createMock, for easier SpyObj creations
Browse files Browse the repository at this point in the history
  • Loading branch information
jnizet committed Feb 21, 2022
1 parent 55de6e2 commit bf9309e
Show file tree
Hide file tree
Showing 3 changed files with 108 additions and 0 deletions.
80 changes: 80 additions & 0 deletions projects/ngx-speculoos/src/lib/mock.spec.ts
@@ -0,0 +1,80 @@
import { createMock } from './mock';

class Simple {
foo(): string {
return '';
}
bar(): string {
return '';
}
baz(): string {
return '';
}
}

class GrandParent {
foo(): string {
return '';
}
}
class Parent extends GrandParent {
bar(): string {
return '';
}
}
class Child extends Parent {
baz(): string {
return '';
}
}

abstract class AbstractBase {
abstract foo(): string;
bar(): string {
return '';
}
}
class AbstractChild extends AbstractBase {
foo(): string {
return '';
}

baz(): string {
return '';
}
}

describe('mock', () => {
it('should create a mock for a simple class', () => {
const mock = createMock(Simple);
mock.foo.and.returnValue('foo');
mock.bar.and.returnValue('bar');
mock.baz.and.returnValue('baz');

expect(mock.foo()).toBe('foo');
expect(mock.bar()).toBe('bar');
expect(mock.baz()).toBe('baz');
});

it('should create a mock for a child class class', () => {
const mock = createMock(Child);
mock.foo.and.returnValue('foo');
mock.bar.and.returnValue('bar');
mock.baz.and.returnValue('baz');

expect(mock.foo()).toBe('foo');
expect(mock.bar()).toBe('bar');
expect(mock.baz()).toBe('baz');
});

it('should create a mock for a child class of an abstract class', () => {
const mock = createMock(AbstractChild);
mock.foo.and.returnValue('foo');
mock.bar.and.returnValue('bar');
mock.baz.and.returnValue('baz');

expect(mock.foo()).toBe('foo');
expect(mock.bar()).toBe('bar');
expect(mock.baz()).toBe('baz');
});
});
27 changes: 27 additions & 0 deletions projects/ngx-speculoos/src/lib/mock.ts
@@ -0,0 +1,27 @@
import { Type } from '@angular/core';

// eslint-disable-next-line @typescript-eslint/no-explicit-any
function collectMethodNames(proto: unknown): Array<string> {
if (!proto || proto === Object.prototype) {
return [];
}
const methodNames: Array<string> = [];
for (const key of Object.getOwnPropertyNames(proto)) {
const descriptor = Object.getOwnPropertyDescriptor(proto, key);
if (descriptor && typeof descriptor.value === 'function' && key !== 'constructor') {
methodNames.push(key);
}
}
return [...methodNames, ...collectMethodNames(Object.getPrototypeOf(proto))];
}

/**
* Creates a spy object for a class where all the methods of the class (and of its superclasses) are spies.
* I.e., for a class `UserService` with methods `get()`, `create()`, `update()` and `delete()`, calling
* `createMock(UserService)` is equivalent to calling
* `jasmine.createSpyObj<UserService>('UserService', ['get', 'create', 'update', 'delete']).
* @param type the type to mock (usually a service class)
*/
export function createMock<T>(type: Type<T>): jasmine.SpyObj<T> {
return jasmine.createSpyObj<T>(type.name, collectMethodNames(type.prototype) as unknown as jasmine.SpyObjMethodNames<T>);
}
1 change: 1 addition & 0 deletions projects/ngx-speculoos/src/public_api.ts
Expand Up @@ -13,3 +13,4 @@ export * from './lib/test-select';
export * from './lib/test-textarea';
export * from './lib/route';
export * from './lib/matchers';
export * from './lib/mock';

0 comments on commit bf9309e

Please sign in to comment.