✅ JavaScript Unit Testing: Start Simple But Grow Very Powerful
If you’ve ever written code and wondered “How do I know this works every time?”, you’re already thinking about unit testing.
Unit testing is the practice of writing small, automated tests to check that individual pieces of code (like a function) behave exactly the way you expect. In JavaScript, this can be as simple as testing a basic function, or as advanced as mocking dependencies and testing Promises.
In this lesson, we’ll explore both worlds:
- Basic Assertions → The foundation of testing.
- Unit Testing Promises → Using modern tools like Mocha, Chai, and Sinon.
🧪 Basic Assertions
At its simplest, a unit test is just an assertion:
“If I run this code, do I get the result I expect?”
Let’s build a tiny custom assertion function:
function assert(outcome, description) {
var passFail = outcome ? "pass" : "fail";
console.log(passFail, ":", description);
return outcome;
};
This function prints pass
if the test condition is true, or fail
if it’s false.
Now let’s test a simple add
function:
function add(num1, num2) {
return num1 + num2;
}
A failing test (wrong expectation):
var result = add(5, 20);
assert(result == 24, "add(5, 20) should return 25...");
// Output: fail : add(5, 20) should return 25...
A corrected test:
assert(result == 25, "add(5, 20) should return 25...");
// Output: pass : add(5, 20) should return 25...
👉 A real-world test suite would include multiple assertions — testing positive numbers, negative numbers, and even edge cases like add(0, 0)
— to make sure your function works in all scenarios.
⚡ Testing Promises with Modern Tools
JavaScript isn’t just about simple functions — modern apps rely heavily on Promises for asynchronous work. Testing them requires a bit more setup.
Popular testing tools:
- Mocha → A test runner (organizes and runs tests).
- Chai → An assertion library (human-readable checks).
- chai-as-promised → Extends Chai to handle Promises easily.
- Sinon → For spies, stubs, and mocks.
- Proxyquire → To override dependencies (inject stubs).
🔹 Testing a Function that Returns a Promise
Imagine a ping
function that returns a Promise:
const ping = () => {
return new Promise((resolve, _reject) => {
const response = processResponse(data);
resolve(response);
});
};
We don’t want the real processResponse
— we just want to test ping
in isolation.
So we use:
proxyquire
→ To replaceprocessResponse
with a fake one (formattingStub
).sinon.stub()
→ To define what the stub should return.
In the test, we assert:
expect(pingResult).to.be.fulfilled;
expect(pingResult).to.eventually.equal(response);
Thanks to chai-as-promised, this reads almost like English: “I expect this Promise to be fulfilled and eventually equal the response.”
🔹 Testing a Function that Consumes a Promise
Now let’s test a wrapper that calls ping
:
const pingWrapper = () => {
ping.then((response) => {
// do something with the response
});
};
Steps for testing:
- Stub the
ping
function itself withproxyquire
. -
Use
sinon-stub-promise
to control how the stubbed Promise behaves:pingStub.returnsPromise().resolves(response);
-
After running
pingWrapper
, assert thatping
was called properly:expect(pingSpy).to.have.been.calledWith(response);
👉 This approach ensures that your tests don’t rely on real external dependencies, making them fast, reliable, and isolated.
✅ Summary
Unit testing in JavaScript can start simple but grow very powerful:
- Basic Assertions → Use functions like
assert()
or libraries like Chai to check expected results. - Testing Promises → Requires stubbing dependencies and using tools like Mocha, Chai-as-Promised, Sinon, and Proxyquire.
- Good tests → Small, focused, reliable, and independent of real-world services.
The more you test, the more confidence you’ll have that your code won’t break when users interact with it.
📝 Review – Fill in the Gaps
- A unit test checks a small, discrete piece of __.
- The
assert
function returns “pass” if the test outcome is __. add(5, 20)
should correctly return __.- Mocha is a JavaScript test __.
- Chai is used as an __ library.
- To test Promises, Chai can be extended with
chai-______
. - Sinon is useful for creating spies, stubs, and __.
- Proxyquire allows you to override a module’s __.
expect(pingResult).to.eventually.equal(response);
checks the resolved value of a __.- A good test suite should cover not just one case but multiple __.