Last active
September 12, 2022 15:41
-
-
Save kylefender/7e0a730ef1a1c4fef2f70bec4cb2d2bd to your computer and use it in GitHub Desktop.
Jasmine extensions that make creating and interacting with mocked methods and properties easier. These are based on an article by Andrei Mihalciuc (https://itnext.io/better-typescript-support-for-jasmine-20dc7454ba94)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // Based on https://itnext.io/better-typescript-support-for-jasmine-20dc7454ba94 | |
| import { Pipe, Type } from '@angular/core'; | |
| import { TestBed } from '@angular/core/testing'; | |
| export type Spied<T> = T & { [Method in keyof T]: jasmine.Spy }; | |
| export function spyOnClass<T>(spiedClass: Type<T>): Spied<T> { | |
| const prototype = spiedClass.prototype; | |
| let methodNames = Object.getOwnPropertyNames(prototype) | |
| .map(name => [name, Object.getOwnPropertyDescriptor(prototype, name)]) | |
| .filter(([name, descriptor]) => { | |
| return (descriptor as PropertyDescriptor).value instanceof Function; | |
| }) | |
| .map(([name]) => name); | |
| let propertyNames = Object.getOwnPropertyNames(prototype) | |
| .map(name => [name, Object.getOwnPropertyDescriptor(prototype, name)]) | |
| .filter(([name, descriptor]) => { | |
| return ( | |
| (descriptor as PropertyDescriptor).get instanceof Function | |
| || (descriptor as PropertyDescriptor).set instanceof Function | |
| ); | |
| }) | |
| .map(([name]) => name); | |
| // Walk-through all of the object's prototypes | |
| let proto = prototype.__proto__; | |
| while (proto !== Object.prototype) { | |
| const protoMethods = Object.getOwnPropertyNames(proto) | |
| .map(name => [name, Object.getOwnPropertyDescriptor(proto, name)]) | |
| .filter(([name, descriptor]) => { | |
| return (descriptor as PropertyDescriptor).value instanceof Function; | |
| }) | |
| .map(([name]) => name.toString()); | |
| const protoProperties = Object.getOwnPropertyNames(proto) | |
| .map(name => [name, Object.getOwnPropertyDescriptor(proto, name)]) | |
| .filter(([name, descriptor]) => { | |
| return ( | |
| (descriptor as PropertyDescriptor).get instanceof Function | |
| || (descriptor as PropertyDescriptor).set instanceof Function | |
| ); | |
| }) | |
| .map(([name]) => name.toString()); | |
| methodNames = methodNames.concat(protoMethods); | |
| propertyNames = propertyNames.concat(protoProperties); | |
| proto = proto.__proto__; | |
| } | |
| // Grab just the unique values for methods and properties | |
| return jasmine.createSpyObj(spiedClass.toString(), [...new Set(methodNames)], [...new Set(propertyNames)]); | |
| } | |
| export function provideMock<T>(spiedClass: Type<T>): Object { | |
| return { | |
| provide: spiedClass, | |
| useValue: spyOnClass(spiedClass) | |
| }; | |
| } | |
| export function getMock<T>(spiedClass: Type<T>): Spied<T> { | |
| return TestBed.inject<any>(spiedClass) as Spied<T>; | |
| } | |
| export function getMockedProperty<T>(spiedClass: Spied<T>, propertyName: string, accessType?: 'get' | 'set'): jasmine.Spy { | |
| if (accessType === 'set') { | |
| return Object.getOwnPropertyDescriptor(spiedClass, propertyName).set as jasmine.Spy; | |
| } | |
| return Object.getOwnPropertyDescriptor(spiedClass, propertyName).get as jasmine.Spy; | |
| } | |
| export function addMockedProperty<T>(spiedClass: Spied<T>, name: string, value: any, linkSetterToGetter?: boolean): void { | |
| Object.defineProperty(spiedClass, name, { | |
| get: function () { return value; }, | |
| set: linkSetterToGetter === true ? function (setterValue: any) { value = setterValue; } : function (setterValue: any) { } | |
| }); | |
| } | |
| export function addMockedProperties<T>(spiedClass: Spied<T>, properties: { [key: string]: any }, linkSetterToGetter?: boolean): void { | |
| Object.getOwnPropertyNames(properties).map(name => addMockedProperty(spiedClass, name, properties[name], linkSetterToGetter)); | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment