Lesson 25: Mastering JavaScript Automated Testing with Mocha + BDD

🔹 1. Summarize the Concept Clearly
🧪 What is Mocha?
Mocha is a JavaScript test framework that runs on Node.js and in the browser. It's primarily used for unit and integration testing. It provides:
describe()
– a way to group tests (suites)it()
– individual test casesbefore
,after
,beforeEach
,afterEach
– hooks for setup/teardown
🧪 What is Chai?
Chai is an assertion library used with Mocha. It supports:
assert
style (like Node.js's built-inassert
)expect
andshould
styles (BDD, expressive syntax)
💡 Core Idea of BDD (Behavior-Driven Development)
In BDD, you write tests first, describing expected behavior in human-readable specs. These tests act as:
🧪 Tests (fail early, pass when ready)
📘 Documentation (describe use cases)
💡 Examples (live demos of how to use a function)
💻 Code Sample
describe("pow", function () {
it("2 raised to 3 should be 8", function () {
assert.equal(pow(2, 3), 8);
});
});
Mocha Test Flow Diagram
describe("feature", function() {
└── it("should do something", function() {
└── assertion
});
});
Hooks in play:
before() → once before all
beforeEach() → before each it
afterEach() → after each it
after() → once after all
🔹 2. Fill Any Gaps (Advanced Insights, Edge Cases, Traps):
🔍 Hidden Concepts & Edge Cases:
🧨 Test File Order Pitfall
Mocha executes test files in lexical order. If you depend on test execution order, you’ll hit issues — structure your specs so they are isolated and independent.
🧪 Async Test Support
it("waits for async", function (done) {
setTimeout(() => {
assert.equal(2 + 2, 4);
done();
}, 100);
});
Or using Promises:
it("returns promise", function () {
return Promise.resolve().then(() => assert.isTrue(true));
});
Or with async/await:
it("uses async/await", async function () {
const result = await someAsyncFunc();
assert.equal(result, 42);
});
🚫 Arrow Functions in describe
/it
?
Avoid them when using this.timeout()
or this.skip()
— because arrow functions don’t bind this
.
📊 Non-Deterministic Tests
Avoid randomness unless you fix the seed. Random behavior leads to flaky tests.
🔍 Gotchas & Dev Mistakes:
❌ Testing multiple concerns in a single
it
block❌ Overusing
beforeEach
to create deeply coupled setup❌ Forgetting to reset stubs/mocks after each test
❌ Using
assert.equal()
instead ofstrictEqual()
where needed
⚙️ Internal Mechanics
Mocha runs tests asynchronously, so you can return a
Promise
or usedone()
callback to handle async code.Test order is not guaranteed, don’t rely on shared state unless scoped correctly with hooks.
BDD vs TDD: Mocha supports both styles. BDD is
describe/it
, TDD issuite/test
.
❗ Common Pitfalls
Mistake | Why it's bad |
Only testing happy paths | Leads to false confidence in reliability |
Overusing beforeEach for unrelated setup | Slows tests and introduces side effects |
Testing implementation, not behavior | Breaks tests when refactoring |
Not isolating tests | Can cause flaky tests if state leaks |
Using assert.equal(a, b) for objects | Fails silently; use deepEqual instead |
🔍 Assertion Traps
assert.equal(NaN, NaN)
→ Fails! Useassert.isNaN()
assert.equal([], [])
→ Fails! Useassert.deepEqual()
assert.equal(null, undefined)
→ Passes! Useassert.strictEqual()
for accuracy
⚠️ Browser-Specific Quirks
DOM-related tests may behave differently across browsers unless normalized.
Timing-sensitive code (like animations or timers) needs
fake timers
via Sinon.
🔹 3. Challenge Me Deeply
🟢 Basic
Write a test suite for a
reverseString(str)
function.Create tests for a
capitalize(word)
function that trims and capitalizes.Write tests to validate a
sum(a, b)
function handles negative numbers.
🟡 Intermediate
Test a
fetchData(url)
function using fake XMLHttpRequest (Sinon).Write a spec for
filterEven(arr)
that verifies output is always a new array.Group tests with
describe()
and show whenbeforeEach
runs.
🔴 Advanced
Write tests for a function that takes a callback and executes it after 2 seconds.
Test a caching function using spies to verify function calls are memoized.
Validate
debounce(fn, ms)
with a fake timer that callsfn
only once.
🧠 Bonus Brain-Twister
- Write a test for a function that returns
true
if given function throws an error — and false otherwise. The tricky part: make sure it catchesasync
errors too!
🔹 4. Interview-Ready Questions
📘 Conceptual
What's the difference between unit and integration testing?
Why is BDD preferred in large teams?
How does Mocha execute async tests?
🧠 Debugging
A test that passes locally fails on CI. What would you check?
A test suite sometimes passes, sometimes fails. What could cause flaky behavior?
🔥 Real-World Pitfalls
Overuse of mocks/spies
Not testing edge cases (e.g., empty inputs, invalid types)
Coupling tests to DOM structure
✅ Best Practices
One
it()
per logical behaviorAlways include edge/corner case tests
Use
beforeEach
/afterEach
only when necessary
❌ Red Flags
200+ line test files with no grouping
Tests that pass without actually asserting anything
Tests that mutate shared state
🔹 5. Real-World Usage
💼 Front-End
Testing React/Vue components via [Mocha + jsdom or Karma]
Ensuring animations/timers (debounce, throttle) work correctly
🖥️ Back-End
Unit testing business logic functions
Integration tests with mock databases (e.g., Sinon spies/fakes)
🛠️ Frameworks
Vue Test Utils + Mocha
React + Mocha + Enzyme
Electron apps often use Mocha for browser+Node tests
🧩 Open Source
Mocha itself is tested with Mocha
Chai: high usage in many testing stacks
Sinon: widely used for mocking/stubbing timers and APIs
🔹 6. Remember Like a Pro
🧠 Mnemonics
“DITCH” your bugs
Describe
It
Tests
Chai
Hooks
⚡ Cheatsheet
Method | Use Case |
assert.equal(a, b) | Primitive equality (not strict) |
assert.strictEqual(a, b) | Strict equality === |
assert.deepEqual(obj1, obj2) | Object structure comparison |
assert.isNaN(val) | Value is NaN |
assert.throws(fn) | Function throws |
assert.isTrue(val) | val === true |
🔹 7. Apply It in a Fun Way
📦 Mini Project: “Power Playground”
A UI + Test integration sandbox for exploring pow(x, n)
with Mocha.
Steps to build:
Create a simple HTML page with two inputs (
x
,n
) and a button.Add a
pow(x, n)
function with input validation.Display result on the page.
Add
test.js
to write tests for various power cases.Integrate Mocha & Chai via CDN.
Show test results in a
<div id="mocha">
.
Bonus Extensions:
Add sliders for
x
andn
Show real-time test results as user changes input
Export test results as JSON or downloadable logs
🎯 Mini Project: "Calculator Test Harness"
Goal: Build a basic calculator with BDD-style tests.
✅ Steps:
Create a
calculator.js
with functions:add
,subtract
,multiply
,divide
.Create a
test.js
file using Mocha + Chai.Write BDD-style specs like:
it("adds two numbers")
it("throws error on divide by 0")
Add
beforeEach()
to reset any global state (if any).Run tests in browser or via Node CLI.
Watch test suite grow as you add features!
➕ Extend It:
Add memory storage (store last result)
Add square root, exponentiation
Test edge cases:
divide(1, 0)
,multiply(Number.MAX_SAFE_INTEGER)
➕ Bonus Section: Extra Value
⚙️ Performance Tips
Avoid redundant tests — use parameterized tests (
makeTest
) smartlyRun tests in parallel (via CI or test runners like Karma)
🧰 Polyfills / Modern Alternatives
Jest (modern alternative) includes Mocha-like syntax, mocking, and snapshots
Vitest (Vite + Jest-like) is trending for modern frontend setups
❌ Dev Mistakes
Writing tests after the fact
Skipping test writing for “small” changes (often the riskiest)
Confusing test coverage with test quality
Subscribe to my newsletter
Read articles from manoj ymk directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
