Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 14 additions & 11 deletions docs/api-reference/fail-fast.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,14 +120,15 @@ const logger = unitRef.get(Logger);
logger.log.mockReturnValue(undefined);
```

### Option 3: Use .boundaries() (Best for Sociable)
### Option 3: Use .collaborate() + .exclude() (Best for Sociable)

Switch to boundaries pattern for cleaner configuration:
Switch to collaborate pattern for cleaner configuration:

```typescript
// Instead of configuring many real dependencies
const { unit } = await TestBed.sociable(OrderService)
.boundaries([ComplexTaxEngine]) // Avoid complex logic
.collaborate()
.exclude([ComplexTaxEngine]) // Exclude from collaboration
.compile();

// Note: Token-injected deps (DATABASE, HTTP) are auto-mocked
Expand Down Expand Up @@ -158,20 +159,21 @@ await TestBed.sociable(PaymentService)
- Unconfigured mocks throw immediately
- Bug caught at test time ✅

### Less Critical in Boundaries Mode
### Less Critical in Collaborate Mode

With `.boundaries()`, the default is **everything real**:
With `.collaborate()`, the default is **everything real**:

```typescript
await TestBed.sociable(OrderService)
.boundaries([ComplexMLService]) // Avoid complex logic
.collaborate()
.exclude([ComplexMLService]) // Exclude from collaboration
.compile();

// All other deps try to instantiate as real
// If deps missing → natural constructor failure (already caught)
```

Fail-fast still helps, but boundaries mode naturally fails if dependencies are misconfigured.
Fail-fast still helps, but collaborate mode naturally fails if dependencies are misconfigured.

## Differences Between Test Types

Expand All @@ -194,9 +196,10 @@ const { unit } = await TestBed.sociable(Service)
.expose(RealService)
.compile();

// Boundaries mode: Default is real → natural failures
// Collaborate mode: Default is real → natural failures
const { unit } = await TestBed.sociable(Service)
.boundaries([ComplexService])
.collaborate()
.exclude([ComplexService])
.compile();
```

Expand All @@ -205,13 +208,13 @@ const { unit } = await TestBed.sociable(Service)
## Best Practices

1. **Configure used methods**: Only mock methods your test actually calls
2. **Use .boundaries() for test scope**: List classes to avoid (complex logic tested elsewhere, legacy code, third-party SDKs)
2. **Use .collaborate() + .exclude() for test scope**: Exclude classes you want to avoid (complex logic tested elsewhere, legacy code, third-party SDKs)
3. **Tokens are auto-mocked**: Token-injected dependencies (`@Inject('DB')`) are automatically mocked
4. **Migrate gradually**: Use `.failFast({ enabled: false })` temporarily
5. **Remove `.failFast({ enabled: false })`**: Complete migration before v5.0.0

## See Also

- [TestBed.sociable()](/docs/api-reference/testbed-sociable) - Sociable test configuration (includes `.expose()` and `.boundaries()`)
- [TestBed.sociable()](/docs/api-reference/testbed-sociable) - Sociable test configuration (includes `.expose()` and `.collaborate() + .exclude()`)
- [Mock Configuration](/docs/api-reference/mock-configuration) - Configuring mock behavior
- [Migration Guide](/docs/migration-guides/from-automock) - Migrating to Suites
7 changes: 4 additions & 3 deletions docs/api-reference/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ API reference for setting up and managing unit tests with Suites.
## Core APIs

- [**TestBed.solitary()**](/docs/api-reference/testbed-solitary) - Create isolated unit tests where all dependencies are automatically mocked
- [**TestBed.sociable()**](/docs/api-reference/testbed-sociable) - Test business logic interactions with `.boundaries()` <span class="version-badge version-badge--new">v4.0.0+</span> or `.expose()`
- [**TestBed.sociable()**](/docs/api-reference/testbed-sociable) - Test business logic interactions with `.collaborate()` + `.exclude()` <span class="version-badge version-badge--new">v4.0.0+</span> or `.expose()`
- [**Mock Configuration**](/docs/api-reference/mock-configuration) - Configure mock behavior with `.mock().final()` and `.mock().impl()`
- [**mock() and stub()**](/docs/api-reference/mock) - Create standalone mocks outside TestBed
- [**UnitReference**](/docs/api-reference/unit-reference) - Access mocked dependencies in tests
Expand All @@ -31,9 +31,10 @@ const { unit, unitRef } = await TestBed.solitary(UserService).compile();

### Creating a Sociable Test
```typescript
// Recommended: boundaries (v4.0.0+)
// Recommended: collaborate + exclude (v4.0.0+)
const { unit, unitRef } = await TestBed.sociable(UserService)
.boundaries([ComplexService]) // List what to avoid
.collaborate()
.exclude([ComplexService]) // Exclude from collaboration
.compile();

// Alternative: expose
Expand Down
38 changes: 19 additions & 19 deletions docs/api-reference/testbed-sociable.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,17 @@ TestBed.sociable<T>(ClassUnderTest: Type<T>): SociableTestBuilder<T>

`SociableTestBuilder<T>` with two configuration modes:

### .boundaries() <span class="version-badge version-badge--new">v4.0.0+</span>
### .collaborate() + .exclude() <span class="version-badge version-badge--new">v4.0.0+</span>

Recommended approach. List classes to avoid - everything else runs real.
Recommended approach. Enable natural collaboration, then exclude specific classes.

```typescript
boundaries(): SociableTestBuilder<T>
boundaries(dependencies: Type[]): SociableTestBuilder<T>
collaborate(): SociableTestBuilderInCollaborateMode<T>
exclude(dependencies: [Type, ...Type[]]): SociableTestBuilderInCollaborateMode<T>
```

:::tip Token Auto-Mocking
Token-injected dependencies are automatically mocked. Use .boundaries() for class dependencies you want to avoid.
Token-injected dependencies are automatically mocked. Use `.exclude()` for class dependencies you want to opt-out of collaboration.
:::

### .expose() - Alternative
Expand All @@ -57,17 +57,18 @@ Both methods only accept class constructors, not tokens.

## Examples

### Using .boundaries()
### Using .collaborate() + .exclude()

```typescript
const { unit, unitRef } = await TestBed.sociable(OrderService)
.boundaries([ComplexTaxEngine]) // Avoid complex logic
.collaborate()
.exclude([ComplexTaxEngine]) // Exclude from collaboration
.compile();

// Can retrieve boundaries (mocked)
// Can retrieve excluded dependencies (mocked)
const taxEngine = unitRef.get(ComplexTaxEngine);

// Cannot retrieve real dependencies
// Cannot retrieve collaborating dependencies
// const calculator = unitRef.get(PriceCalculator); // ERROR - it's real
```

Expand All @@ -87,11 +88,11 @@ const database = unitRef.get(Database);

## What's Retrievable

**Boundaries mode:**
- ✅ Classes in .boundaries() array (mocked)
**Collaborate mode:**
- ✅ Classes in .exclude() array (mocked)
- ✅ Tokens (auto-mocked)
- ✅ Explicitly mocked dependencies
- ❌ Real dependencies (auto-exposed, leaf classes)
- ❌ Collaborating dependencies (real, auto-exposed)

**Expose mode:**
- ✅ Non-exposed dependencies (mocked)
Expand All @@ -101,15 +102,14 @@ const database = unitRef.get(Database);

## Mode Comparison

| Aspect | .boundaries() | .expose() |
|--------|---------------|-----------|
| Default | Everything real | Everything mocked |
| You list | What to avoid | What to keep |
| Aspect | .collaborate() + .exclude() | .expose() |
|--------|----------------------------|-----------|
| Default | Everything collaborates | Everything mocked |
| You list | What to exclude | What to keep |
| Use when | Many deps should be real | Few deps should be real |
| Future-proof | ✅ New deps auto-tested | ⚠️ New deps ignored |
| Refactoring-stable | ✅ New deps auto-collaborate | ⚠️ New deps ignored |

**Example:** eight `.expose()` calls vs one `.boundaries()` call achieves the same outcome. See [Sociable Guide]
(/docs/guides/sociable) for the comparison.
**Example:** eight `.expose()` calls vs one `.collaborate()` call achieves the same outcome. See [Sociable Guide](/docs/guides/sociable) for the comparison.

## See Also

Expand Down
32 changes: 20 additions & 12 deletions docs/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ description: Release notes and version history for Suites
## v4.0.0-beta.0 (Current - Testing)

:::warning Beta Release
v4.0.0 is in beta for testing. Applies alpha infrastructure improvements **plus** boundaries mode and fail-fast behavior. All functionality will be backported to v3.1.0 soon.
v4.0.0 is in beta for testing. Applies alpha infrastructure improvements **plus** collaborate/exclude API and fail-fast behavior. All functionality will be backported to v3.1.0 soon.
:::

**From Alpha:**
Expand All @@ -21,22 +21,30 @@ v4.0.0 is in beta for testing. Applies alpha infrastructure improvements **plus*

**New in Beta:**

**`.boundaries()` API**
Blacklist strategy for sociable tests. List classes to avoid - everything else runs real.
**`.collaborate()` + `.exclude()` API**
Natural collaboration strategy for sociable tests. Enable collaboration, then exclude specific classes.

```typescript
TestBed.sociable(OrderService)
.boundaries([ComplexTaxEngine]) // Avoid complex logic
.collaborate()
.exclude([ComplexTaxEngine]) // Exclude from collaboration
.compile();
```

**Why boundaries:**
- Simpler when most deps should be real
- Future-proof: new dependencies auto-tested
- Leaf classes (no dependencies) automatically real
**Why collaborate + exclude:**
- **Refactoring stable**: Adding dependencies doesn't break tests
- **Natural mental model**: Think collaboration, not avoidance
- **Future-proof**: New dependencies auto-collaborate
- **Clear intent**: "Exclude" is more intuitive than "boundaries"
- Tokens always auto-mocked (databases, HTTP)

See [TestBed.sociable()](/docs/api-reference/testbed-sociable) for complete API details.
**The refactoring stability advantage:**

When you add new dependencies to your production code (e.g., adding a `Logger` or `ValidationService`), tests using `.collaborate()` continue to work because new dependencies automatically join the collaboration. You only need to `.exclude()` specific expensive or external services.

This is more stable than "leaf-based" strategies where adding intermediate dependencies can break tests because the graph structure changed.

See [TestBed.sociable()](/docs/api-reference/testbed-sociable) for complete API details and [Sociable Tests Guide](/docs/guides/sociable) for examples.

**Fail-Fast Behavior**
Enabled by default. Throws `DependencyNotConfiguredError` on unconfigured dependencies. Prevents false positives.
Expand All @@ -54,7 +62,7 @@ See [Fail-Fast Behavior](/docs/api-reference/fail-fast) for details and migratio
# Core package (beta)
npm install @suites/unit@beta

# DI adapters (alpha - no boundaries/fail-fast changes)
# DI adapters (alpha - no collaborate/exclude or fail-fast changes)
npm install @suites/di.nestjs@alpha
# or
npm install @suites/di.inversify@alpha
Expand All @@ -68,7 +76,7 @@ npm install @suites/doubles.sinon@beta
```

:::tip Why Different Versions?
`@suites/unit` and doubles adapters are on **beta** (boundaries + fail-fast features). DI adapters remain on **alpha** - they're installed separately and don't contain the new testing logic.
`@suites/unit` and doubles adapters are on **beta** (collaborate/exclude + fail-fast features). DI adapters remain on **alpha** - they're installed separately and don't contain the new testing logic.
:::

**Breaking Changes:**
Expand All @@ -89,7 +97,7 @@ All v4.0.0 features will be backported to v3.1.0 with no breaking changes. This
:::

**What's coming:**
- `.boundaries()` API (opt-in)
- `.collaborate()` + `.exclude()` API (opt-in)
- Fail-fast behavior (disabled by default, opt-in with <code>.failFast(\{ enabled: true \})</code>)
- Enhanced type safety
- Performance improvements
Expand Down
2 changes: 1 addition & 1 deletion docs/get-started/installation.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ Suites v4.0.0+ requires **Node.js 20 or higher**. Check your version with `node
## Installation

:::tip Testing Beta?
For v4.0.0 beta with boundaries and fail-fast features, see [Changelog](/docs/changelog) for beta installation instructions.
For v4.0.0 beta with collaborate/exclude and fail-fast features, see [Changelog](/docs/changelog) for beta installation instructions.
:::

Install Suites' core package:
Expand Down
2 changes: 1 addition & 1 deletion docs/guides/fundamentals.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ Test one class in complete isolation. All dependencies are mocked.
See [Solitary Unit Tests](/docs/guides/solitary) for examples and usage.

**Sociable Tests**
Test multiple business logic classes together. Use `.boundaries()` to list classes you want to avoid.
Test multiple business logic classes together. Use `.collaborate()` + `.exclude()` to enable natural collaboration and exclude specific classes.

See [Sociable Unit Tests](/docs/guides/sociable) for examples and usage.

Expand Down
Loading