# Herding Cats: Turning Chaos Agents into Senior Engineers (Part 2)
In [Part 1](/turning-chaos-agents-into-senior-engineers-part-1), we stopped our AI Agents from writing chaotic, state-mutating loops. We forced them to be functional. We forced them to be deterministic.
But there is a deeper problem.
AI is, by definition, **non-deterministic**. It is a probabilistic engine. It operates on “vibes.” It predicts the next token based on what *feels* right in the context of its training data.
Software production, however, is **binary**. It either works, or it throws a `500 Internal Server Error` and wakes you up on a Saturday.
This is where **Testing** comes in. Tests are the hard, cold reality that collapses the AI’s probability wave into a single, working function. And ironically, because LLMs are trained on billions of lines of open-source code (which include tests), they are surprisingly good at respecting test constraints—if you speak their language.
In Part 2, we are implementing the **Trust-But-Verify Suite**.
## Rule #1: The “No Magic” Rule (Dependency Injection)
When an AI writes a test, its instinct is to cheat. It tries to use “magic” mocking libraries that hijack the module loader (like `td.replace` or `jest.mock`) to swap out dependencies deep inside a file.
This is flaky. It causes caching issues. It causes “works on my machine” syndrome.
We need to force the Agent to write code that is honest about its needs. We enforce **Dependency Injection (DI)** via constructor options.
### The Guideline
Add this to your `.cursor/rules/testing.mdc`:
“`md
## Dependency Injection Pattern
– **Update constructors to accept an options object** for all params.
– **Example**: `constructor(options = {})` instead of args.
– **Why**: Allows easy injection of mock instances for testing.
“`
### The Result
**The “Magic” Way (Forbidden):**
“`js
// Hardcoded import. Impossible to test without hacking require cache.
import db from ‘./database’;
class UserService {
save(user) {
return db.insert(user);
}
}
“`
**The “Honest” Way (Required):**
“`js
import defaultDb from ‘./database’;
class UserService {
// Dependency is injected. Defaults to real DB, but replaceable.
constructor({ db = defaultDb } = {}) {
this.db = db;
}
save(user) {
return this.db.insert(user);
}
}
“`
## Rule #2: The Clean Slate Protocol
AI Agents have zero object permanence. They will mock a function in Test A, forget to reset it, and crash Test B. They create “state leakage,” which is the nemesis of reliable CI/CD.
We must enforce a strict regime of creation and destruction using `beforeEach` and `afterEach`.
### The Guideline
“`md
## Test Lifecycle
– **Fresh Mocks Per Test**: Create new mocks in `beforeEach`.
– **Proper Cleanup**: Always call `td.reset()` in `afterEach`.
– **Dependency Injection**: Pass mocks via constructor options.
“`
### The Result
The Agent now generates robust test templates automatically:
“`js
describe(‘UserService’, () => {
let service;
let dbMock;
beforeEach(() => {
// 1. Fresh Mock
dbMock = { insert: td.func() };
// 2. Injection
service = new UserService({ db: dbMock });
});
afterEach(() => {
// 3. The Clean Slate
td.reset();
});
});
“`
## Rule #3: Don’t Just Check, Verify
A lazy AI will write a test that checks `assert.ok(result)`. This is useless. It tells you “something happened,” but not “the *right* thing happened.”
Since the AI’s output is non-deterministic, our checks must be hyper-specific. We force the use of **Nested Property Assertions**.
### The Guideline
“`md
## Assertions
– **Use nested property assertions** for object validation.
– Example: `assert.nestedPropertyVal(obj, ‘user.id’, 1)`
– Verify all mock calls using `td.verify()`.
“`
This forces the Agent to understand the *shape* of the data it is testing, ensuring that deep inside that JSON object, the value is exactly what we expect.
## Summary: The Binary Safety Net
By defining these Testing Rules, we have turned the AI’s greatest weakness (hallucination) into a strength. Because we force it to explicitly define dependencies and expectations, the AI becomes its own auditor.
It can still “vibe” out the code implementation, but if those vibes don’t pass the binary gate of the test suite, the code never leaves the editor.
**Coming up in Part 3:** We move from code to **Architecture**. How do we stop the Agent from putting 50 files in one folder? We’ll discuss File Structure and Naming Conventions.
Author:
[Justin Morant](https://www.linkedin.com/in/justinmorant/)



