Posts

Showing posts from November, 2025

๐Ÿ“ฆ v0.0.0-dev.1 — Mockable Promises with MockChain

๐Ÿ“ฆ v0.0.0-dev.1 — Mockable Promises with MockChain We’re excited to announce the   first prerelease   of   @fizzwiz/mockchain :   v0.0.0-dev.1   — a library dedicated to   controlling and testing asynchronous operations   in distributed systems. This release introduces the core abstraction:   MockChain . Each   MockChain   instance is a   mockable Promise   that lets you manually resolve or reject asynchronous flows, inspect chain nodes, and simulate network or event-driven operations deterministically. Key highlights include: Full support for   controlling any point in a promise chain , not just the root. .mock()   and   .fail()   methods for manual resolution and rejection. .root   and   .parent   properties for navigating the chain and testing midstream transformations. Factory methods for   fetch requests ,   WebSocket messages , and generic   events . ๐Ÿงช This is a   pr...

๐Ÿ”‘ Why MockChain Is a Chain

๐Ÿ”‘ Why   MockChain   Is a Chain When testing asynchronous methods, you often want to   control behavior without rewriting the method logic .   MockChain   allows you to inject and observe behavior at   any point in a promise chain , not just at the start. ๐Ÿงฉ Two Ends of the Chain Consider a typical async method: function fetchData ( url, opts, autoStart = true ) { return MockChain . fromResponse (url, opts, autoStart) . then ( res => res. json ()) . then ( obj => obj. ok ); } There are   three promises   involved: Root node ( root )   — the initial wrapped promise (e.g.,   fetch() ) resolving to a Response object. Mid node   — the in-between promise resolving to the JSON object. Leaf node ( end )   — the last node resolving to a Boolean flag. In tests, you usually mock the   root   to trigger the   end   manually: const chain = fetchData ( undefined , undefined , false ); // don’t awai...

๐Ÿ The autostart Flag

๐Ÿ The   autostart   Flag — Designing Testable Async Methods Async functions are convenient, but they hide execution inside native Promises — which can’t be intercepted or mocked. To preserve control, every asynchronous method in a distributed system should expose a   autostart   flag . This simple addition enables you to decide   when   asynchronous execution actually happens, giving your tests full deterministic control while keeping production behavior untouched. ๐ŸŽฏ The Idea Instead of rewriting async logic for testing, let each function decide whether to execute its asynchronous flow immediately or defer it. A   mockable method : Runs normally in production ( autostart = true ). Defers execution in tests ( autostart = false ), allowing   MockChain   to intercept and control outcomes. ⚙️ Implementation Pattern import { MockChain } from '@fizzwiz/mockchain' ; /** * Fetch data from a URL. * @param { string } url * @param { boolean ...

⚠️ Async Functions Are Not Mockable

  ⚠️ Async Functions Are Not Mockable Async functions are convenient in JavaScript, but they are   incompatible with mockable Promises   like   MockChain . This post explains   why   async functions break mockability and how to preserve control over promise chains. ❌ Why Async Functions Break Mockability An   async   function always returns a   native Promise , even if you return another Promise (as a   MockChain ) inside it. async function example ( ) { const chain = MockChain . resolve ( 42 ); return chain; // You think you're returning a MockChain... } const result = example (); console . log (result instanceof MockChain ); // ❌ false Why this happens:   ECMAScript’s   Promise assimilation   wraps any thenable in a new native Promise, so the original instance is lost. Consequences: Custom subclass methods ( .mock() ,   .fail() ,   .root ,   .parent ) disappear. Any attached metadata ( promise...