Skip to content

Commit

Permalink
Merge remote-tracking branch 'remotes/origin/develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
CalionVarduk committed May 28, 2019
2 parents c3f6b43 + af98199 commit 2b804cc
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 10 deletions.
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
@@ -1,6 +1,6 @@
{
"name": "frlluc-mocking",
"version": "0.9.6",
"version": "1.0.0",
"description": "A simple mocking framework targetting TypeScript",
"main": "./lib/index.js",
"types": "./lib/index.d.ts",
Expand Down
22 changes: 17 additions & 5 deletions src/core/mock.ts
Expand Up @@ -3,6 +3,7 @@ import { IMockedMethodInfo } from './mocked-method-info.interface';
import { IMockedPropertyInfo } from './mocked-property-info.interface';
import { IInvocationData } from './invocation-data.interface';
import { IMock } from './mock.interface';
import { reinterpretCast } from './reinterpret-cast';

let GLOBAL_INVOCATION_NO = 0;

Expand Down Expand Up @@ -74,14 +75,23 @@ function createPropertyMock(
* @returns mocked object
* */
export function mock<T>(setup: Partial<T>): IMock<T> {
return partialMock<T>(reinterpretCast<T>({}), setup);
}

/**
* Allows to create a partially mocked object.
* @param subject an object to partially mock
* @param setup mock setup
* @returns mocked object
* */
export function partialMock<T>(subject: T, setup: Partial<T>): IMock<T> {
const members: string[] = [];
const invocationCache: { [id: string]: IMockedMethodInfo | IMockedPropertyInfo } = {};
const result: any = {
getMemberInfo(m: string): IMockedMethodInfo | IMockedPropertyInfo | null {
return invocationCache[m] || null;
}
};
const subject: any = {};
for (const key of Object.getOwnPropertyNames(setup)) {
members.push(key);
const descriptor = Object.getOwnPropertyDescriptor(setup, key)!;
Expand All @@ -92,12 +102,14 @@ export function mock<T>(setup: Partial<T>): IMock<T> {
set: propertyMock.setter
});
} else {
subject[key] = (typeof descriptor.value === 'function') ?
createMethodMock(invocationCache, subject, descriptor.value, key) :
descriptor.value;
Object.defineProperty(subject, key, {
value: (typeof descriptor.value === 'function') ?
createMethodMock(invocationCache, subject, descriptor.value, key) :
descriptor.value
});
}
}
result.subject = Object.freeze(subject);
result.subject = subject;
result.mockedMembers = Object.freeze(members);
return Object.freeze(result);
}
Expand Down
80 changes: 77 additions & 3 deletions src/tests/mock.spec.ts
Expand Up @@ -2,7 +2,7 @@ import { MockedInfoType } from '../core/mocked-info-type.enum';
import { IMockedMethodInfo } from '../core/mocked-method-info.interface';
import { IMockedPropertyInfo } from '../core/mocked-property-info.interface';
import { IMock } from '../core/mock.interface';
import { mock, resetGlobalMockInvocationNo, getGlobalMockInvocationNo } from '../core/mock';
import { mock, resetGlobalMockInvocationNo, getGlobalMockInvocationNo, partialMock } from '../core/mock';

abstract class Test {
public abstract field: string;
Expand All @@ -12,12 +12,21 @@ abstract class Test {
public abstract returningMethod(a1?: number, a2?: string): { x: number; y: number };
}

class TestImpl extends Test {
public get property(): number { return this._property; }
public set property(value: number) { this._property = value; }
public get readonlyProperty(): string { return 'test impl'; }
public field: string = 'field';
private _property: number = 0;
public voidMethod(a1?: number, a2?: string, a3?: boolean, a4?: { x: number; y: number }, a5?: number[]): void { return; }
public returningMethod(a1?: number, a2?: string): { x: number; y: number } { return { x: a1|| 0, y: (a1 || 0) * 2 }; }
}

function assert(sut: IMock<Test>, expectedMemberCount: number): void {
expect(sut).toBeDefined();
expect(sut).not.toBeNull();
expect(sut.subject).toBeDefined();
expect(sut.subject).not.toBeNull();
expect(Object.getOwnPropertyNames(sut.subject).length).toBe(expectedMemberCount);
expect(sut.mockedMembers).toBeDefined();
expect(sut.mockedMembers).not.toBeNull();
expect(sut.mockedMembers.length).toBe(expectedMemberCount);
Expand Down Expand Up @@ -504,7 +513,6 @@ test('created mock should be frozen',
() => {
const sut = mock<Test>({});
expect(Object.isFrozen(sut)).toBe(true);
expect(Object.isFrozen(sut.subject)).toBe(true);
expect(Object.isFrozen(sut.mockedMembers)).toBe(true);
}
);
Expand All @@ -531,3 +539,69 @@ test('all info should be frozen',
}
}
);

test('partial mock function should modify the original subject',
() => {
const subject = new TestImpl();
const expectedField = 'foo';
const expectedProperty = 10;
const expectedVoidMethodArgs: any[] = [7, 'faz', true, { x: 100, y: 200 }, [1, 2, 3]];
const expectedVoidMethodResult = void(0);
const expectedReturningMethodArgs: any[] = [15, 'baz'];
const expectedReturningMethodResult = { x: 15, y: 30 };
let property = -1;
let voidMethodArgs: any[] = [];
let returningMethodArgs: any[] = [];

const sut = partialMock<Test>(subject, {
field: expectedField,
get property(): number { return property; },
set property(value: number) { property = value; },
voidMethod(a1?: number, a2?: string, a3?: boolean, a4?: { x: number; y: number }, a5?: number[]): void {
voidMethodArgs = [a1, a2, a3, a4, a5];
},
returningMethod(a1?: number, a2?: string): { x: number; y: number } {
returningMethodArgs = [a1, a2];
return { x: a1!, y: a1! * 2 };
}
});
const propertyInfo = sut.getMemberInfo('property') as IMockedPropertyInfo;
const voidMethodInfo = sut.getMemberInfo('voidMethod') as IMockedMethodInfo;
const returningMethodInfo = sut.getMemberInfo('returningMethod') as IMockedMethodInfo;
assert(sut, 4);
expect(sut.subject).toBe(subject);
expect(sut.mockedMembers).toContain('field');
expect(sut.mockedMembers).toContain('property');
expect(sut.mockedMembers).toContain('voidMethod');
expect(sut.mockedMembers).toContain('returningMethod');
expect(sut.getMemberInfo('field')).toBeNull();
expect(propertyInfo).toBeDefined();
expect(propertyInfo).not.toBeNull();
expect(propertyInfo.type).toBe(MockedInfoType.Property);
expect(propertyInfo.get).toBeDefined();
expect(propertyInfo.get).not.toBeNull();
expect(propertyInfo.get!.type).toBe(MockedInfoType.PropertyGetter);
expect(propertyInfo.get!.count).toBe(0);
expect(propertyInfo.set).toBeDefined();
expect(propertyInfo.set).not.toBeNull();
expect(propertyInfo.set!.type).toBe(MockedInfoType.PropertySetter);
expect(propertyInfo.set!.count).toBe(0);
expect(voidMethodInfo).toBeDefined();
expect(voidMethodInfo).not.toBeNull();
expect(voidMethodInfo.type).toBe(MockedInfoType.Method);
expect(voidMethodInfo.count).toBe(0);
expect(returningMethodInfo).toBeDefined();
expect(returningMethodInfo).not.toBeNull();
expect(returningMethodInfo.type).toBe(MockedInfoType.Method);
expect(returningMethodInfo.count).toBe(0);
expect(sut.subject.field).toStrictEqual(expectedField);
sut.subject.property = expectedProperty;
expect(sut.subject.property).toBe(expectedProperty);
expect(property).toBe(expectedProperty);
expect(sut.subject.voidMethod(...expectedVoidMethodArgs)).toBe(expectedVoidMethodResult);
expect(voidMethodArgs).toStrictEqual(expectedVoidMethodArgs);
expect(sut.subject.returningMethod(...expectedReturningMethodArgs)).toStrictEqual(expectedReturningMethodResult);
expect(returningMethodArgs).toStrictEqual(expectedReturningMethodArgs);
expect(sut.subject.readonlyProperty).toBe('test impl');
}
);

0 comments on commit 2b804cc

Please sign in to comment.