Skip to content

Commit

Permalink
feat: Record unexpected calls
Browse files Browse the repository at this point in the history
  • Loading branch information
NiGhTTraX committed May 5, 2020
1 parent bcae757 commit a87f612
Show file tree
Hide file tree
Showing 5 changed files with 81 additions and 34 deletions.
74 changes: 63 additions & 11 deletions src/base-repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ export abstract class BaseRepository implements ExpectationRepository2 {
CountableExpectation[]
>();

private readonly callStats: CallMap = new Map();
private readonly expectedCallStats: CallMap = new Map();

private readonly unexpectedCallStats: CallMap = new Map();

add(expectation: Expectation2): void {
const { property } = expectation;
Expand All @@ -30,15 +32,17 @@ export abstract class BaseRepository implements ExpectationRepository2 {

clear(): void {
this.expectations.clear();
this.callStats.clear();
this.expectedCallStats.clear();
}

get(property: PropertyKey): any {
this.record(property, undefined);

const expectations = this.expectations.get(property);

if (expectations && expectations.length) {
// We record that an expected property access has happened, but an
// unexpected call could still happen later.
this.recordExpected(property, undefined);

const propertyExpectation = expectations.find((e) =>
e.expectation.matches(undefined)
);
Expand All @@ -50,27 +54,31 @@ export abstract class BaseRepository implements ExpectationRepository2 {
}

return (...args: any[]) => {
this.record(property, args);

const callExpectation = expectations.find((e) =>
e.expectation.matches(args)
);

if (callExpectation) {
this.recordExpected(property, args);
this.countAndConsume(callExpectation);

return callExpectation.expectation.returnValue;
}

this.recordUnexpected(property, args);
return this.getValueForUnexpectedCall(property, args);
};
}

this.recordUnexpected(property, undefined);
return this.getValueForUnexpectedAccess(property);
}

getCallStats() {
return { expected: this.callStats, unexpected: new Map() };
return {
expected: this.expectedCallStats,
unexpected: this.unexpectedCallStats,
};
}

getUnmet(): Expectation2[] {
Expand Down Expand Up @@ -103,12 +111,21 @@ export abstract class BaseRepository implements ExpectationRepository2 {
): void;

/**
* Record the property access/method call.
* Record an expected property access/method call.
*/
protected recordExpected(property: PropertyKey, args: any[] | undefined) {
const calls = this.expectedCallStats.get(property) || [];

this.expectedCallStats.set(property, [...calls, { arguments: args }]);
}

/**
* Record an unexpected property access/method call.
*/
protected record(property: PropertyKey, args: any[] | undefined) {
const calls = this.callStats.get(property) || [];
protected recordUnexpected(property: PropertyKey, args: any[] | undefined) {
const calls = this.unexpectedCallStats.get(property) || [];

this.callStats.set(property, [...calls, { arguments: args }]);
this.unexpectedCallStats.set(property, [...calls, { arguments: args }]);
}

private countAndConsume(expectation: CountableExpectation) {
Expand All @@ -118,3 +135,38 @@ export abstract class BaseRepository implements ExpectationRepository2 {
this.consumeExpectation(expectation);
}
}

/**
* const strictMock = mock<() => void>();
* when(strictMock()).thenReturn(undefined);
*
* instance(strictMock)();
*
* verify(strictMock); // all good
*
* instance(strictMock)(); // throws
* verify(strictMock); // throws
*
* callStats = {
* expected: [mock()],
* unexpected: [mock()]
* }
*
* ------------------------------------------
*
* const weakMock = mock<() => void>({ weak: true });
* when(weakMock()).thenReturn(undefined);
*
* instance(weakMock)();
* instance(weakMock)();
*
* verify(weakMock); // all good
*
* instance(weakMock)(); // all good
* verify(weakMock); // all good
*
* callStats = {
* expected: [mock(), mock(), mock()],
* unexpected: []
* }
*/
14 changes: 13 additions & 1 deletion src/expectation-repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,18 @@ export type Call = {
arguments: any[] | undefined;
};

/**
* Method calls should be recorded both as a property access and a method call.
*
* @example
* // foo.bar(1, 2, 3) should generate
* {
* foo: [
* { arguments: undefined },
* { arguments: [1, 2, 3] }
* ]
* }
*/
export type CallMap = Map<PropertyKey, Call[]>;

export type CallStats = {
Expand Down Expand Up @@ -93,7 +105,7 @@ export interface ExpectationRepository2 {
getUnmet(): Expectation2[];

/**
* Return all the calls that successfully returned a value so far.
* Return all the calls that have been made so far.
*/
getCallStats(): CallStats;
}
2 changes: 1 addition & 1 deletion src/weak-repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export class WeakRepository extends BaseRepository
return WeakRepository.TO_STRING_VALUE;
default:
return (...args: any[]) => {
this.record(property, args);
this.recordExpected(property, args);

return null;
};
Expand Down
10 changes: 4 additions & 6 deletions tests/repo-contract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -302,8 +302,8 @@ export const repoContractTests2: ExpectationRepositoryContract = {
} catch (e) {}

const callStats: CallStats = {
expected: new Map([['foo', [{ arguments: undefined }]]]),
unexpected: new Map(),
expected: new Map(),
unexpected: new Map([['foo', [{ arguments: undefined }]]]),
};
expect(repo.getCallStats()).toEqual(callStats);
},
Expand All @@ -318,10 +318,8 @@ export const repoContractTests2: ExpectationRepositoryContract = {
} catch (e) {}

const callStats: CallStats = {
expected: new Map([
['foo', [{ arguments: undefined }, { arguments: [1, 2, 3] }]],
]),
unexpected: new Map(),
expected: new Map([['foo', [{ arguments: undefined }]]]),
unexpected: new Map([['foo', [{ arguments: [1, 2, 3] }]]]),
};
expect(repo.getCallStats()).toEqual(callStats);
},
Expand Down
15 changes: 0 additions & 15 deletions tests/weak-repository.spec.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { expect } from 'tdd-buffet/expect/jest';
import { describe, it } from 'tdd-buffet/suite/node';
import { CallStats } from '../src/expectation-repository';
import { WeakRepository } from '../src/weak-repository';
import {
MatchingCallExpectation,
Expand All @@ -23,20 +22,6 @@ describe('WeakRepository', () => {
expect(repo.get('whatever')()).toEqual(null);
});

it('should record unexpected function calls', () => {
const repo = new WeakRepository();

repo.get('foo')(1, 2, 3);

const callStats: CallStats = {
expected: new Map([
['foo', [{ arguments: undefined }, { arguments: [1, 2, 3] }]],
]),
unexpected: new Map(),
};
expect(repo.getCallStats()).toEqual(callStats);
});

it('should keep repeating the last met property expectation', () => {
const repo = new WeakRepository();

Expand Down

0 comments on commit a87f612

Please sign in to comment.