Skip to content

Commit 2f1a84f

Browse files
Add jsonContaining() matcher
1 parent 8f2cbf7 commit 2f1a84f

7 files changed

+197
-3
lines changed

README.md

+55
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,11 @@ Fork of [ts-mockito](https://github.com/NagRock/ts-mockito), which will be kept
4141

4242
- [Improved argument capturing](#capture-by-matching-arguments)
4343

44+
### 1.0.44
45+
46+
- [Match by JSON string](#match-by-json)
47+
- [Custom matchers](#custom-matchers)
48+
4449
## Installation
4550

4651
`npm install @johanblumenberg/ts-mockito --save-dev`
@@ -179,6 +184,56 @@ verify(mockedFoo.getBar(anything())).called();
179184
verify(mockedFoo.getBar(_)).called();
180185
```
181186

187+
### Match by JSON
188+
189+
``` typescript
190+
// Creating mock
191+
let mockedFoo:Foo = mock(Foo);
192+
193+
// Getting instance from mock
194+
let foo:Foo = instance(mockedFoo);
195+
196+
// Using instance in source code
197+
foo.getBar('{"name": "John Doe", "age": 42}');
198+
foo.getBar('{"name": "John Smith", "age": 30}');
199+
200+
// Match by part of JSON string
201+
verify(mockedFoo.getBar(jsonContaining({name: startsWith("John")}))).twice();
202+
verify(mockedFoo.getBar(jsonContaining({age: 42}))).once();
203+
```
204+
205+
### Custom matchers
206+
207+
Sometimes it is useful to define custom matchers, to be able to easily match on anything. This can be done by extending the `Matcher` class.
208+
209+
```typescript
210+
class IsPalindromeMatcher extends Matcher {
211+
public match(value: string): boolean {
212+
return value === value.split("").reverse().join("");
213+
}
214+
215+
public toString(): string {
216+
return 'isPalindrome()';
217+
}
218+
}
219+
220+
function isPalindrome(): string {
221+
return new IsPalindromeMatcher() as any;
222+
}
223+
224+
// Creating mock
225+
let mockedFoo:Foo = mock(Foo);
226+
227+
// Getting instance from mock
228+
let foo:Foo = instance(mockedFoo);
229+
230+
// Using instance in source code
231+
foo.bar("racecar);
232+
233+
// Match using the custom matcher
234+
verify(mockedFoo.bar(isPalindrome())).once();
235+
```
236+
182237
## Usage
183238
184239
### Basics

package-lock.json

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@johanblumenberg/ts-mockito",
3-
"version": "1.0.43",
3+
"version": "1.0.44",
44
"description": "Mocking library for TypeScript",
55
"main": "lib/ts-mockito.js",
66
"typings": "lib/ts-mockito",
+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import * as _ from "lodash";
2+
import {Matcher} from "./Matcher";
3+
4+
export class JsonContainingMatcher extends Matcher {
5+
constructor(private expectedValue: any) {
6+
super();
7+
}
8+
9+
public match(value: string): boolean {
10+
return _.isMatchWith(JSON.parse(value), this.expectedValue, (objValue, srcValue) => {
11+
if (srcValue instanceof Matcher) {
12+
return srcValue.match(objValue);
13+
} else {
14+
return undefined;
15+
}
16+
});
17+
}
18+
19+
public toString(): string {
20+
return `jsonContaining(${JSON.stringify(this.expectedValue)})`;
21+
}
22+
}

src/ts-mockito.ts

+10
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {IsBeforeMatcher} from "./matcher/type/date/IsBeforeMatcher";
1212
import {IsBeforeOrEqualMatcher} from "./matcher/type/date/IsBeforeOrEqualMatcher";
1313
import {DeepEqualMatcher} from "./matcher/type/DeepEqualMatcher";
1414
import {EndsWithMatcher} from "./matcher/type/EndsWithMatcher";
15+
import { JsonContainingMatcher } from "./matcher/type/JsonContainingMatcher";
1516
import {MatchingStringMatcher} from "./matcher/type/MatchingStringMatcher";
1617
import {NotNullMatcher} from "./matcher/type/NotNullMatcher";
1718
import {GreaterThanMatcher} from "./matcher/type/number/GreaterThanMatcher";
@@ -26,6 +27,7 @@ import {MethodStubVerificator} from "./MethodStubVerificator";
2627
import {MethodToStub} from "./MethodToStub";
2728
import {Mocker, MockPropertyPolicy, MockOptions} from "./Mock";
2829
import {Spy} from "./Spy";
30+
import {Matcher} from './matcher/type/Matcher';
2931

3032
// Keep a reference to the original, in case it is replaced with fake timers
3133
// by some library like jest or lolex
@@ -158,6 +160,8 @@ export function resetCalls<T>(mockedValue: T): void {
158160
(mockedValue as any).__tsmockitoMocker.resetCalls();
159161
}
160162

163+
export {Matcher} from './matcher/type/Matcher';
164+
161165
export function anyOfClass<T>(expectedClass: new (...args: any[]) => T): any {
162166
return new AnyOfClassMatcher(expectedClass) as any;
163167
}
@@ -219,6 +223,10 @@ export function objectContaining<T>(expectedValue: RecursivePartial<T extends tr
219223
return new ObjectContainingMatcher(expectedValue) as any;
220224
}
221225

226+
export function jsonContaining<T>(expectedValue: T): string {
227+
return new JsonContainingMatcher(expectedValue) as any;
228+
}
229+
222230
export type Deferred<T> = Promise<T> & {
223231
resolve: (value: T) => Promise<void>;
224232
reject: (err: any) => Promise<void>;
@@ -285,6 +293,7 @@ export default {
285293
reset,
286294
resetStubs,
287295
resetCalls,
296+
Matcher,
288297
anyOfClass,
289298
anyFunction,
290299
anyNumber,
@@ -299,6 +308,7 @@ export default {
299308
startsWith,
300309
endsWith,
301310
objectContaining,
311+
jsonContaining,
302312
MockPropertyPolicy,
303313
defer,
304314
nextTick,
+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import {Matcher} from "../../../src/ts-mockito";
2+
3+
class IsPalindromeMatcher extends Matcher {
4+
constructor() {
5+
super();
6+
}
7+
8+
public match(value: string): boolean {
9+
return value === value.split("").reverse().join("");
10+
}
11+
12+
public toString(): string {
13+
return 'isPalindrome()';
14+
}
15+
}
16+
17+
function isPalindrome(): string {
18+
return new IsPalindromeMatcher() as any;
19+
}
20+
21+
describe("Custom matcher", () => {
22+
it("match returns true", () => {
23+
// given
24+
const testObj: Matcher = isPalindrome() as any;
25+
26+
// when
27+
const result = testObj.match("racecar");
28+
29+
// then
30+
expect(result).toBeTruthy();
31+
});
32+
33+
it("no match returns false", () => {
34+
// given
35+
const testObj: Matcher = isPalindrome() as any;
36+
37+
// when
38+
const result = testObj.match("taxi");
39+
40+
// then
41+
expect(result).toBeFalsy();
42+
});
43+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import {Matcher} from "../../../src/matcher/type/Matcher";
2+
import {jsonContaining, objectContaining, startsWith} from "../../../src/ts-mockito";
3+
4+
describe("JsonContainingMatcher", () => {
5+
describe("checking if source object contains given object", () => {
6+
const testObj: Matcher = jsonContaining({b: {c: "c", d: {}}}) as unknown as Matcher;
7+
8+
describe("when given value contains given object", () => {
9+
it("returns true", () => {
10+
// when
11+
const result = testObj.match('{"a": "a", "b": {"c": "c", "d": {}}}');
12+
13+
// then
14+
expect(result).toBeTruthy();
15+
});
16+
17+
it("returns true", () => {
18+
// when
19+
const result = testObj.match('{"b": {"c": "c", "d": {}}}');
20+
21+
// then
22+
expect(result).toBeTruthy();
23+
});
24+
});
25+
26+
describe("when given value doesn't contain given object", () => {
27+
it("returns false", () => {
28+
// when
29+
const result = testObj.match('{"b": {"c": "c"}}');
30+
31+
// then
32+
expect(result).toBeFalsy();
33+
});
34+
});
35+
});
36+
37+
describe("accept matchers as values", () => {
38+
it("should match using matcher as value", () => {
39+
const testObj: Matcher = jsonContaining({ key: startsWith("abc") }) as unknown as Matcher;
40+
const result = testObj.match('{"key": "abcdef"}');
41+
expect(result).toBeTruthy();
42+
});
43+
44+
it("should not match using matcher as value with mismatching value", () => {
45+
const testObj: Matcher = jsonContaining({ key: startsWith("abc") }) as unknown as Matcher;
46+
const result = testObj.match('{"key": "def"}');
47+
expect(result).toBeFalsy();
48+
});
49+
});
50+
51+
describe("accept matchers as values in arrays", () => {
52+
it("should match using matcher as value", () => {
53+
const testObj: Matcher = jsonContaining({ key: [startsWith("abc")] }) as unknown as Matcher;
54+
const result = testObj.match('{"key": ["abcdef"]}');
55+
expect(result).toBeTruthy();
56+
});
57+
58+
it("should not match using matcher as value with mismatching value", () => {
59+
const testObj: Matcher = jsonContaining({ key: [startsWith("abc")] }) as unknown as Matcher;
60+
const result = testObj.match('{"key": ["def"]}');
61+
expect(result).toBeFalsy();
62+
});
63+
});
64+
});

0 commit comments

Comments
 (0)