Skip to content

Commit 36e7e2c

Browse files
committed
add advanced mocking example
1 parent 9193034 commit 36e7e2c

File tree

14 files changed

+3513
-10
lines changed

14 files changed

+3513
-10
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ jobs:
1616
fail-fast: false
1717
matrix:
1818
example:
19+
- advanced-mock-config
1920
- inversify-jest
2021
- inversify-sinon
2122
- inversify-vitest

README.md

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,41 +4,47 @@ Real-world examples demonstrating [Suites](https://suites.dev) integration with
44

55
## Examples
66

7-
| Example | DI Framework | Test Runner | Use When |
8-
|----------------------------------------|--------------|-------------|---------------------------------------------|
9-
| [nestjs-jest](./nestjs-jest) | NestJS | Jest | NestJS with Jest |
10-
| [nestjs-vitest](./nestjs-vitest) | NestJS | Vitest | NestJS with Vitest |
11-
| [nestjs-sinon](./nestjs-sinon) | NestJS | Sinon | NestJS with Sinon/Mocha |
12-
| [inversify-jest](./inversify-jest) | InversifyJS | Jest | InversifyJS with Jest |
13-
| [inversify-vitest](./inversify-vitest) | InversifyJS | Vitest | InversifyJS with Vitest |
14-
| [inversify-sinon](./inversify-sinon) | InversifyJS | Sinon | InversifyJS with Sinon/Mocha |
7+
| Example | DI Framework | Test Runner | Use When |
8+
| ---------------------------------------------- | ------------ | ----------- | ------------------------------------------------- |
9+
| [nestjs-jest](./nestjs-jest) | NestJS | Jest | NestJS with Jest |
10+
| [nestjs-vitest](./nestjs-vitest) | NestJS | Vitest | NestJS with Vitest |
11+
| [nestjs-sinon](./nestjs-sinon) | NestJS | Sinon | NestJS with Sinon/Mocha |
12+
| [inversify-jest](./inversify-jest) | InversifyJS | Jest | InversifyJS with Jest |
13+
| [inversify-vitest](./inversify-vitest) | InversifyJS | Vitest | InversifyJS with Vitest |
14+
| [inversify-sinon](./inversify-sinon) | InversifyJS | Sinon | InversifyJS with Sinon/Mocha |
15+
| [advanced-mock-config](./advanced-mock-config) | NestJS | Jest | Advanced `.mock().final()` and `.impl()` patterns |
1516

1617
## Choosing an Example
1718

1819
### By DI Framework
1920

2021
**NestJS**
22+
2123
- Full-featured framework with built-in modules
2224
- Includes HTTP, validation, configuration
2325
- Use for: Applications requiring framework features
2426

2527
**InversifyJS**
28+
2629
- Lightweight IoC container
2730
- Minimal abstractions
2831
- Use for: Applications requiring less framework overhead
2932

3033
### By Test Runner
3134

3235
**Jest**
36+
3337
- Includes built-in assertions and mocking
3438
- Use for: Standard Jest-based projects
3539

3640
**Vitest**
41+
3742
- Faster test execution
3843
- Native ESM support
3944
- Use for: Projects requiring faster feedback
4045

4146
**Sinon**
47+
4248
- Works with any assertion library (Chai, Node assert, etc.)
4349
- Used with Mocha test runner
4450
- Use for: Projects with specific assertion library requirements
@@ -67,6 +73,7 @@ const { unit, unitRef } = await TestBed.solitary(UserService).compile();
6773
Test one class in complete isolation. All dependencies are replaced with test doubles.
6874

6975
**When to use:**
76+
7077
- Testing component logic in isolation
7178
- Controlling all inputs for predictable results
7279

@@ -76,14 +83,15 @@ Test one class in complete isolation. All dependencies are replaced with test do
7683

7784
```typescript
7885
const { unit, unitRef } = await TestBed.sociable(UserService)
79-
.expose(UserValidator) // Use real validator
86+
.expose(UserValidator) // Use real validator
8087
.expose(UserRepository) // Use real repository
8188
.compile();
8289
```
8390

8491
Test multiple classes together with their real collaborators. External I/O (databases, APIs, file systems) is replaced with test doubles to keep tests fast.
8592

8693
**When to use:**
94+
8795
- Verifying components work together correctly
8896
- Testing interactions between business logic components
8997

@@ -124,10 +132,12 @@ examples/
124132
├── nestjs-sinon/ # NestJS with Sinon
125133
├── inversify-jest/ # InversifyJS with Jest
126134
├── inversify-vitest/ # InversifyJS with Vitest
127-
└── inversify-sinon/ # InversifyJS with Sinon
135+
├── inversify-sinon/ # InversifyJS with Sinon
136+
└── advanced-mock-config/ # Advanced .mock().final() and .impl() patterns
128137
```
129138

130139
Each example contains:
140+
131141
- `src/types.ts` - Domain types
132142
- `src/user.service.ts` - Business logic
133143
- `src/user.validator.ts` - Validation logic
@@ -153,6 +163,7 @@ Each example contains:
153163
### "reflect-metadata" errors (InversifyJS examples)
154164

155165
InversifyJS requires decorator metadata. Configuration is already set in `tsconfig.json` and imports. If errors occur, verify:
166+
156167
- `experimentalDecorators: true` in tsconfig.json
157168
- `emitDecoratorMetadata: true` in tsconfig.json
158169
- `import 'reflect-metadata'` at top of test files

advanced-mock-config/README.md

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
# Suites + NestJS + Jest (Advanced Mock Configuration)
2+
3+
Simple user management example demonstrating [Suites](https://suites.dev) with NestJS and Jest, showcasing advanced mock configuration patterns using `.mock().final()` and `.mock().impl()`.
4+
5+
## Prerequisites
6+
7+
- Node.js 18 or higher
8+
- pnpm installed globally
9+
10+
## What This Demonstrates
11+
12+
-**Solitary unit tests** - Test UserService in complete isolation
13+
-**Sociable unit tests** - Test components together with real validation, mocked I/O
14+
-**`.mock().final()`** - Immutable mock configuration with plain functions
15+
-**`.mock().impl()`** - Flexible mock configuration with stub functions
16+
-**Token injection** - DATABASE_TOKEN as external boundary
17+
-**Class injection** - UserValidator and UserRepository
18+
19+
## Running the Example
20+
21+
```bash
22+
pnpm install
23+
pnpm test
24+
```
25+
26+
All tests should pass, demonstrating both testing strategies with advanced mock configuration.
27+
28+
## Project Structure
29+
30+
```
31+
src/
32+
├── types.ts # User types and interfaces
33+
├── user.validator.ts # Validation logic (no dependencies)
34+
├── user.repository.ts # Data access (token injection)
35+
├── user.service.ts # Business logic (class injections)
36+
├── user.solitary.spec.ts # Solitary unit tests with mock config
37+
└── user.sociable.spec.ts # Sociable unit tests with mock config
38+
```
39+
40+
## Mock Configuration Patterns
41+
42+
### `.mock().final()` - Immutable Configuration
43+
44+
Use when you want to **lock down** mock behavior that should never change:
45+
46+
```typescript
47+
const { unit, unitRef } = await TestBed.solitary(UserService)
48+
.mock(UserValidator)
49+
.final({
50+
// Plain functions - cannot be reconfigured in tests
51+
validate: () => ({ isValid: true, errors: [] })
52+
})
53+
.compile();
54+
```
55+
56+
**Key characteristics:**
57+
58+
- Functions provided to `.final()` are plain functions, not Jest mocks
59+
- Behavior is locked - tests cannot use `mockReturnValue()` or similar
60+
- Call inspection (`toHaveBeenCalled()`) is not available
61+
- Best for: external services, logging, fixed configuration values
62+
63+
### `.mock().impl()` - Flexible Configuration
64+
65+
Use when you want **sensible defaults** that tests can override:
66+
67+
```typescript
68+
const { unit, unitRef } = await TestBed.solitary(UserService)
69+
.mock(UserValidator)
70+
.impl((stubFn) => ({
71+
// Stubs - can be reconfigured and inspected in tests
72+
validate: stubFn().mockReturnValue({ isValid: true, errors: [] })
73+
}))
74+
.compile();
75+
76+
// Later in tests, you can override:
77+
validator.validate.mockReturnValue({ isValid: false, errors: ['Error'] });
78+
```
79+
80+
**Key characteristics:**
81+
82+
- Uses `stubFn()` factory to create Jest mock functions
83+
- Behavior is flexible - tests can reconfigure using `mockReturnValue()`, etc.
84+
- Call inspection (`toHaveBeenCalled()`, `toHaveBeenCalledWith()`) is available
85+
- Best for: most mocks where different tests need different behaviors
86+
87+
## Comparing Testing Strategies
88+
89+
**When to use `.final()`:**
90+
91+
- External APIs (email, SMS, payments) - prevent accidental real calls
92+
- Configuration/settings - fixed test environment values
93+
- Logging/telemetry - consistent, predictable output
94+
95+
**When to use `.impl()`:**
96+
97+
- Database operations - need to simulate different query results
98+
- Collaborator services - need flexibility for different test scenarios
99+
- Any mock where behavior needs to vary per-test
100+
101+
## Comparison Table
102+
103+
| Feature | `.final()` | `.impl()` |
104+
| ---------------------- | ---------------- | ----------------- |
105+
| Reconfigurable | ❌ No | ✅ Yes |
106+
| Call inspection | ❌ No | ✅ Yes |
107+
| Function type | Plain functions | Jest stubs |
108+
| `mockReturnValue()` | ❌ Cannot use | ✅ Can use |
109+
| `toHaveBeenCalled()` | ❌ Cannot use | ✅ Can use |
110+
111+
## Related Examples
112+
113+
- [nestjs-jest](../nestjs-jest) - Basic NestJS + Jest example
114+
- [nestjs-vitest](../nestjs-vitest) - NestJS with Vitest
115+
- [inversify-jest](../inversify-jest) - InversifyJS with Jest
116+
117+
## Learn More
118+
119+
- [Suites Documentation](https://suites.dev)
120+
- [Mock Configuration API](https://suites.dev/docs/api-reference/mock-configuration)
121+
- [Test Doubles Guide](https://suites.dev/docs/guides/test-doubles)

advanced-mock-config/global.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
/// <reference types="@suites/doubles.jest/unit" />
2+
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
module.exports = {
2+
testEnvironment: 'node',
3+
testRegex: '.spec.ts$',
4+
transform: {
5+
'^.+\\.ts$': ['ts-jest', { isolatedModules: true }]
6+
},
7+
collectCoverageFrom: [
8+
'src/**/*.ts',
9+
'!src/**/*.spec.ts',
10+
'!src/types.ts'
11+
]
12+
};
13+

advanced-mock-config/package.json

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
{
2+
"name": "advanced-mock-config-example",
3+
"version": "0.0.0",
4+
"private": true,
5+
"description": "Suites Advanced Mock Configuration Example - .mock().final() and .mock().impl()",
6+
"scripts": {
7+
"test": "tsc --noEmit && jest"
8+
},
9+
"dependencies": {
10+
"@nestjs/common": "^10.4.15",
11+
"@nestjs/core": "^10.4.15",
12+
"reflect-metadata": "^0.2.2"
13+
},
14+
"devDependencies": {
15+
"@suites/di.nestjs": "^3.0.1",
16+
"@suites/doubles.jest": "^3.0.1",
17+
"@suites/unit": "^3.0.1",
18+
"@types/jest": "^29.5.13",
19+
"jest": "^29.7.0",
20+
"ts-jest": "^29.2.5",
21+
"typescript": "^5.7.2"
22+
},
23+
"packageManager": "pnpm@9.15.4+sha512.b2dc20e2fc72b3e18848459b37359a32064663e5627a51e4c74b2c29dd8e8e0491483c3abb40789cfd578bf362fb6ba8261b05f0387d76792ed6e23ea3b1b6a0"
24+
}
25+

0 commit comments

Comments
 (0)