Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added .not() matcher #81

Closed
wants to merge 15 commits into from
27 changes: 15 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ Mocking library for TypeScript inspired by http://mockito.org/
* `thenThrow` - throw an error
* `thenCall` - call custom method
* Checking if methods were called with given arguments (`verify`)
* `anything`, `notNull`, `anyString`, `anyOfClass` etc. - for more flexible comparision
* `anything`, `notNull`, `anyString`, `anyOfClass`, `not` etc. - for more flexible comparision
* `once`, `twice`, `times`, `atLeast` etc. - allows call count verification
* `calledBefore`, `calledAfter` - allows call order verification
* Resetting mock (`reset`, `resetCalls`)
Expand Down Expand Up @@ -105,13 +105,16 @@ foo.getBar(2);
foo.getBar(3);

// Call count verification
verify(mockedFoo.getBar(1)).once(); // was called with arg === 1 only once
verify(mockedFoo.getBar(2)).twice(); // was called with arg === 2 exactly two times
verify(mockedFoo.getBar(between(2, 3))).thrice(); // was called with arg beween 2-3 exactly three times
verify(mockedFoo.getBar(anyNumber()).times(4); // was called with any number arg exactly four times
verify(mockedFoo.getBar(2)).atLeast(2); // was called with arg === 2 min two times
verify(mockedFoo.getBar(1)).atMost(1); // was called with arg === 1 max one time
verify(mockedFoo.getBar(4)).never(); // was never called with arg === 4
verify(mockedFoo.getBar(1)).once(); // was called with arg === 1 only once
verify(mockedFoo.getBar(2)).twice(); // was called with arg === 2 exactly two times
verify(mockedFoo.getBar(not().strictEqual(2))).once(); // was called with anything except 2 once
verify(mockedFoo.getBar(between(2, 3))).thrice(); // was called with arg beween 2-3 exactly three times
verify(mockedFoo.getBar(anyNumber()).times(4); // was called with any number arg exactly four times
verify(mockedFoo.getBar(not().anyNumber).times(4); // was called with anything but not a number exactly four times
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing () on anyNumber -> should be: not().anyNumber()

verify(mockedFoo.matchString(/param\d+/)).once(); // was once called with arg matching regexp
verify(mockedFoo.getBar(2)).atLeast(2); // was called with arg === 2 min two times
verify(mockedFoo.getBar(1)).atMost(1); // was called with arg === 1 max one time
verify(mockedFoo.getBar(4)).never(); // was never called with arg === 4
```

### Call order verification
Expand Down Expand Up @@ -159,7 +162,7 @@ let mockedFoo:Foo = mock(Foo);
let foo:Foo = instance(mockedFoo);

when(mockedFoo.sumTwoNumbers(anyNumber(), anyNumber())).thenCall((arg1:number, arg2:number) => {
return arg1 * arg2;
return arg1 * arg2;
});

// prints '50' because we've changed sum method implementation to multiply!
Expand Down Expand Up @@ -335,13 +338,13 @@ const spiedFoo = spy(foo);

foo.bar();

console.log(capture(spiedFoo.bar).last()); // [42]
console.log(capture(spiedFoo.bar).last()); // [42]
```

### Thanks

* Szczepan Faber (https://www.linkedin.com/in/szczepiq)
* Sebastian Konkol (https://www.linkedin.com/in/sebastiankonkol)
* Szczepan Faber (https://www.linkedin.com/in/szczepiq)
* Sebastian Konkol (https://www.linkedin.com/in/sebastiankonkol)
* Clickmeeting (http://clickmeeting.com)
* Michał Stocki (https://github.com/michalstocki)
* Łukasz Bendykowski (https://github.com/viman)
Expand Down
4 changes: 2 additions & 2 deletions src/matcher/type/AnyFunctionMatcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ export class AnyFunctionMatcher extends Matcher {
}

public match(value: any): boolean {
return _.isFunction(value);
return this.reverseResult(_.isFunction(value));
}

public toString(): string {
return "anyFunction()";
return `${this.prefix}anyFunction()`;
}
}
4 changes: 2 additions & 2 deletions src/matcher/type/AnyNumberMatcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ export class AnyNumberMatcher extends Matcher {
}

public match(value: any): boolean {
return _.isNumber(value);
return this.reverseResult(_.isNumber(value));
}

public toString(): string {
return "anyNumber()";
return `${this.prefix}anyNumber()`;
}
}
4 changes: 2 additions & 2 deletions src/matcher/type/AnyOfClassMatcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ export class AnyOfClassMatcher<T> extends Matcher {
}

public match(value: any): boolean {
return value instanceof this.expectedClass;
return this.reverseResult(value instanceof this.expectedClass);
}

public toString() {
return `anyOfClass(${this.expectedClass["name"]})`;
return `${this.prefix}anyOfClass(${this.expectedClass["name"]})`;
}
}
4 changes: 2 additions & 2 deletions src/matcher/type/AnyStringMatcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ export class AnyStringMatcher extends Matcher {
}

public match(value: any): boolean {
return _.isString(value);
return this.reverseResult(_.isString(value));
}

public toString(): string {
return "anyString()";
return `${this.prefix}anyString()`;
}
}
4 changes: 2 additions & 2 deletions src/matcher/type/AnythingMatcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ export class AnythingMatcher extends Matcher {
}

public match(value: any): boolean {
return true;
return this.reverseResult(true);
}

public toString(): string {
return "anything()";
return `${this.prefix}anything()`;
}
}
4 changes: 2 additions & 2 deletions src/matcher/type/BetweenMatcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ export class BetweenMatcher extends Matcher {
}

public match(value: any): boolean {
return value >= this.min && value <= this.max;
return this.reverseResult(value >= this.min && value <= this.max);
}

public toString(): string {
return `between(${this.min}, ${this.max})`;
return `${this.prefix}between(${this.min}, ${this.max})`;
}
}
7 changes: 4 additions & 3 deletions src/matcher/type/DeepEqualMatcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,22 @@ export class DeepEqualMatcher extends Matcher {
}

public match(value: any): boolean {
return _.isEqualWith(this.expectedValue, value,
const result = _.isEqualWith(this.expectedValue, value,
(expected: any, actual: any): boolean => {
if (expected instanceof Matcher) {
return expected.match(actual);
}

return undefined;
});
return this.reverseResult(result);
}

public toString(): string {
if (this.expectedValue instanceof Array) {
return `deepEqual([${this.expectedValue}])`;
return `${this.prefix}deepEqual([${this.expectedValue}])`;
} else {
return `deepEqual(${this.expectedValue})`;
return `${this.prefix}deepEqual(${this.expectedValue})`;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import {Matcher} from "./Matcher";

export class MatchingStringMatcher extends Matcher {
export class MatchStringMatcher extends Matcher {
constructor(private expectedValue: any) {
super();
}

public match(value: any): boolean {
return value.match(this.expectedValue);
return this.reverseResult(value.match(this.expectedValue));
}

public toString(): string {
return `match(${this.expectedValue})`;
return `${this.prefix}match(${this.expectedValue})`;
}
}
21 changes: 18 additions & 3 deletions src/matcher/type/Matcher.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,24 @@
export class Matcher {
public match(value: any): boolean {
return false;
private isNot: boolean = false;

public match(...values: any[]): boolean {
return this.reverseResult(false);
}

public toString(): string {
return "";
return `${this.prefix}`;
}

public reverse() {
this.isNot = true;
return this;
}

protected get prefix(): string {
return this.isNot ? "not()." : "";
}

protected reverseResult(result: boolean): boolean {
return this.isNot ? !result : result;
}
}
60 changes: 60 additions & 0 deletions src/matcher/type/Not.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import {AnyFunctionMatcher} from "./AnyFunctionMatcher";
import {AnyNumberMatcher} from "./AnyNumberMatcher";
import {AnyOfClassMatcher} from "./AnyOfClassMatcher";
import {AnyStringMatcher} from "./AnyStringMatcher";
import {AnythingMatcher} from "./AnythingMatcher";
import {BetweenMatcher} from "./BetweenMatcher";
import {DeepEqualMatcher} from "./DeepEqualMatcher";
import {Matcher} from "./Matcher";
import {MatchStringMatcher} from "./MatchStringMatcher";
import {NotNullMatcher} from "./NotNullMatcher";
import {ObjectContainingMatcher} from "./ObjectContainingMatcher";
import {StrictEqualMatcher} from "./StrictEqualMatcher";

export class Not {

public anyOfClass<T>(expectedClass: {new (...args: any[]): T}): Matcher {
return (new AnyOfClassMatcher<T>(expectedClass)).reverse();
}

public anyFunction(): Matcher {
return (new AnyFunctionMatcher()).reverse();
}

public anyNumber(): Matcher {
return (new AnyNumberMatcher()).reverse();
}

public anyString(): Matcher {
return (new AnyStringMatcher()).reverse();
}

public anything(): Matcher {
return (new AnythingMatcher()).reverse();
}

public between(min: number, max: number): Matcher {
return (new BetweenMatcher(min, max)).reverse();
}

public deepEqual(expectedValue: any): Matcher {
return (new DeepEqualMatcher(expectedValue)).reverse();
}

public notNull(): Matcher {
return (new NotNullMatcher()).reverse();
}

public strictEqual(expectedValue: any): Matcher {
return (new StrictEqualMatcher(expectedValue)).reverse();
}

public matchString(expectedValue: string | RegExp): Matcher {
return (new MatchStringMatcher(expectedValue)).reverse();
}

public objectContaining(expectedValue: Object): Matcher {
return (new ObjectContainingMatcher(expectedValue)).reverse();
}

}
4 changes: 2 additions & 2 deletions src/matcher/type/NotNullMatcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ import {Matcher} from "./Matcher";

export class NotNullMatcher extends Matcher {
public match(value: any): boolean {
return !_.isNull(value);
return this.reverseResult(!_.isNull(value));
}

public toString(): string {
return "notNull()";
return `${this.prefix}notNull()`;
}
}
4 changes: 2 additions & 2 deletions src/matcher/type/ObjectContainingMatcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ export class ObjectContainingMatcher extends Matcher {
}

public match(value: Object): boolean {
return _.isMatch(value, this.expectedValue);
return this.reverseResult(_.isMatch(value, this.expectedValue));
}

public toString(): string {
return `objectContaining(${this.expectedValue})`;
return `${this.prefix}objectContaining(${this.expectedValue})`;
}
}
6 changes: 3 additions & 3 deletions src/matcher/type/StrictEqualMatcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@ export class StrictEqualMatcher extends Matcher {
}

public match(value: any): boolean {
return this.expectedValue === value;
return this.reverseResult(this.expectedValue === value);
}

public toString(): string {
if (this.expectedValue instanceof Array) {
return `strictEqual([${this.expectedValue}])`;
return `${this.prefix}strictEqual([${this.expectedValue}])`;
} else {
return `strictEqual(${this.expectedValue})`;
return `${this.prefix}strictEqual(${this.expectedValue})`;
}
}
}
15 changes: 10 additions & 5 deletions src/ts-mockito.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ import {AnythingMatcher} from "./matcher/type/AnythingMatcher";
import {BetweenMatcher} from "./matcher/type/BetweenMatcher";
import {DeepEqualMatcher} from "./matcher/type/DeepEqualMatcher";
import {Matcher} from "./matcher/type/Matcher";
import {MatchingStringMatcher} from "./matcher/type/MatchingStringMatcher";
import {MatchStringMatcher} from "./matcher/type/MatchStringMatcher";
import {Not} from "./matcher/type/Not";
import {NotNullMatcher} from "./matcher/type/NotNullMatcher";
import {ObjectContainingMatcher} from "./matcher/type/ObjectContainingMatcher";
import {StrictEqualMatcher} from "./matcher/type/StrictEqualMatcher";
Expand All @@ -33,7 +34,7 @@ export function spy<T>(instanceToSpy: T): T {
return new Spy(instanceToSpy).getMock();
}

export function mock<T>(clazz: { new(...args: any[]): T; } | (Function & { prototype: T }) ): T {
export function mock<T>(clazz: {new(...args: any[]): T; } | (Function & {prototype: T})): T {
return new Mocker(clazz).getMock();
}

Expand Down Expand Up @@ -78,7 +79,7 @@ export function resetCalls<T>(mockedValue: T): void {
(mockedValue as any).__tsmockitoMocker.resetCalls();
}

export function anyOfClass<T>(expectedClass: { new (...args: any[]): T }): any {
export function anyOfClass<T>(expectedClass: {new (...args: any[]): T}): any {
return new AnyOfClassMatcher(expectedClass) as any;
}

Expand Down Expand Up @@ -114,14 +115,18 @@ export function strictEqual(expectedValue: any): Matcher {
return new StrictEqualMatcher(expectedValue);
}

export function match(expectedValue: RegExp | string): Matcher {
return new MatchingStringMatcher(expectedValue);
export function matchString(expectedValue: RegExp | string): Matcher {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Version with match name has been already released so we should not change it.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or we can add matchString as duplicated function and add deprecation infromation to match.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lets leave it as match

return new MatchStringMatcher(expectedValue);
}

export function objectContaining(expectedValue: Object): Matcher {
return new ObjectContainingMatcher(expectedValue);
}

export function not(): Not {
return new Not();
}

import * as mockito from "./ts-mockito";

export default mockito;
2 changes: 1 addition & 1 deletion src/utils/MockableFunctionsFinder.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/**
* Looking for all function calls and declarations and provides an array of their names. The mechanism is greedy
* and tries to match as many function names as it can find and not only those of inspecting class.
* and tries to matchString as many function names as it can find and not only those of inspecting class.
*
* Matching occurrences are:
* - [.]functionName(
Expand Down
Loading