Dropping TypeScript Test Times from 12 Minutes to 40 Seconds

So there I was, staring at my terminal at 11:30 PM on a Thursday. My test suite was crawling past the 9-minute mark. Every time I needed to debug our transaction validation logic, I had to spin up a local database, wait for the RPC nodes to sync, and pray the source maps didn’t randomly detach from my editor.

It failed. Again. Spitting out a useless undefined error somewhere deep in the protocol layer.

Debugging TypeScript applications that rely on heavy external state—like databases, blockchain nodes, or complex microservices—is a nightmare. Your iteration loop dies. When tests take 10 minutes, you stop running them. You start guessing. You add console.log('here 1') and console.log('here 2') because attaching a real debugger takes too much effort.

I got fed up. I ripped out the external dependencies for our test environment and built a completely in-memory validation layer.

The result? I cut our build and test time from 11 minutes and 40 seconds to exactly 38 seconds. Here is exactly how I set up the architecture to make debugging fast, local, and actually bearable.

Ditching the Server for In-Memory State

TypeScript code - Debugging TypeScript with Visual Studio Code
TypeScript code – Debugging TypeScript with Visual Studio Code

The biggest bottleneck in debugging is waiting for network I/O. If you want to step through your code quickly, you need the state in memory. No server required.

I built a simple in-memory tracker to replace our heavy backend during testing. It handles basic validation and keeps everything in a local Map. This means I can drop a breakpoint anywhere and instantly inspect the entire system state.

// Core validation and in-memory storage
type UTXO = { id: string; amount: number; parent?: string };

class InMemoryValidator {
  private utxoSet = new Map<string, UTXO>();

  // Basic function to add state
  addRecord(record: UTXO): void {
    if (this.utxoSet.has(record.id)) {
      throw new Error(Record ${record.id} already exists);
    }
    this.utxoSet.set(record.id, record);
  }

  // Debugging helper to dump state
  dumpState(): UTXO[] {
    return Array.from(this.utxoSet.values());
  }

  validateTransaction(inputs: string[], outputAmount: number): boolean {
    let totalInput = 0;
    
    for (const id of inputs) {
      const record = this.utxoSet.get(id);
      if (!record) {
        // Drop a breakpoint right here. 
        // You'll catch missing parents instantly.
        return false; 
      }
      totalInput += record.amount;
    }

    return totalInput >= outputAmount;
  }
}

Because everything lives in memory, I can write a test that sets up 10,000 records, runs the validation, and finishes in milliseconds. If something breaks, the VS Code debugger snaps right to the line. No network timeouts. No stale database rows messing up my test runs.

Mocking the Protocol API Layer

Of course, your frontend still expects to talk to an API. You can’t just delete the network layer and expect things to work.

Instead, I intercept the API calls and route them directly to the in-memory class. We use a JSON-RPC format, so I wrote a quick async dispatcher. This intercepts the requests before they ever hit the network.

// Async API dispatcher for local debugging
class LocalRPCNode {
  constructor(private validator: InMemoryValidator) {}

  async handleRequest(method: string, params: any[]): Promise<any> {
    // Simulate network delay for realistic frontend testing
    await new Promise(resolve => setTimeout(resolve, 50));

    switch (method) {
      case 'get_balance': {
        const [id] = params;
        const state = this.        

More From Author

Fixing Angular Circular Dependencies Without Losing Your Mind

Leave a Reply

Your email address will not be published. Required fields are marked *