*Interviewer: “How do you test your code?” I smiled… (then got rejected)
💡 Article Highlights: Master JavaScript testing and debugging from scratch, including unit testing, integration testing, E2E testing practical cases, and professional debugging techniques to help you stand out in interviews!
Main Content
“Code is finished? Great. So, how do you test it?”
When the interviewer casually throws out this question, does your brain instantly go blank? You feel like you’ve implemented all the functionality perfectly, the logic is flawless, but you just can’t prove it’s really “flawless.” This feeling is like being a martial arts master asked “Is your sword sharp?” but unable to prove it.
Friend, welcome to the real world. Here, code that runs is just “functional,” but code that’s been tested is called “reliable.” Today, I’ll give you a complete methodology to help you completely abandon the侥幸心理 of “code that runs is good enough” and become a professional developer that both interviewers and colleagues can trust.
Chapter 1: Mental Framework — Why Your Code Must Be Tested?
Many beginners think testing is unnecessary, a waste of time. Wrong! Testing isn’t an accessory to functionality; it’s the foundation of product quality itself.
Think of it like a top chef. He’ll taste the dish before serving it. This “tasting” is testing.
It ensures your code performs according to your script, not randomly. It prevents you from secretly creating ten new bugs while fixing one (this is called “regression testing”). It gives you confidence when you boldly refactor code in the future, with solid footing. It builds confidence, absolute confidence in the code you’ve created with your own hands.
When an interviewer asks why you need testing, they don’t want to hear you recite concepts; they want to see if there’s reverence for the four words “engineering quality” in your eyes.
Chapter 2: Arsenal of Tools — Master Your Testing Weaponry
Testing isn’t a random stew; it has layers and methodology. Like assembling a supercar, you first ensure every screw is qualified, then see if the engine can start, and finally drive the whole car on the track.
Layer 1: Unit Testing - Tighten Every Screw
This is the most basic, most core testing. It only cares about the smallest functional units, like an independent function. It’s like an obsessive-compulsive person who puts the function in a “black box,” isolating all external dependencies (like databases, APIs), just to see if it can produce the expected output given certain inputs.
For example, we have a simple sum function:
// sum.js
function sum(a, b) {
return a + b;
}
module.exports = sum;
Its unit test (using the popular Jest framework) would look like this, like a simple mathematical verification:
// sum.test.js
const sum = require('./sum');
test('test if 1 plus 2 equals 3', () => {
// This is an "assertion" - I assert that the result of sum(1, 2) "toBe" (is) 3
expect(sum(1, 2)).toBe(3);
});
Layer 2: Integration Testing - Start the Engine
When all parts are qualified, we need to assemble them and see if the engine, transmission, and electrical systems can work together. This is integration testing. It tests how multiple unit modules perform when combined, like your business logic calling database modules, or frontend requesting backend APIs.
Layer 3: End-to-End Testing (E2E Test) - Track Racing
This is the highest level of testing, completely simulating real user scenarios. Like a real race car driver sitting in the cockpit, starting the engine, shifting gears, pressing the accelerator, running a lap on a real track. E2E testing automatically opens browsers, clicks buttons, fills forms, then verifies if the page gives the correct feedback. Cypress is the king in this field.
When interviewers ask about the differences between these three, they want to probe your vision. Are you a craftsman only looking at parts, or an architect who can control the overall situation?
Chapter 3: Internal Methods — Decoding Testing Jargon
Assertion: This is the soul of testing. Simply put, it’s your “prediction” or “assertion” about code behavior. expect(A).toBe(B) is a classic assertion, meaning “I expect A to equal B.” If the prediction comes true, the test passes; if reality slaps you in the face, the test fails.
toBe vs toEqual: This is a classic “trap” in Jest. toBe is a strict guy, using ===, requiring both to be the same object, even memory addresses must be the same. toEqual is a friendly friend, only caring if object contents are the same, recursively checking each field.
// For arrays and objects, you almost always need toEqual
expect([1, 2]).toEqual([1, 2]); // ✅ Pass! Same content
expect([1, 2]).toBe([1, 2]); // ❌ Fail! These are two different array instances
Mocks & Stubs: In unit testing, to put the “function being tested” in a black box, we need to “pretend” its dependencies exist. This requires “mock objects” (Mocks). For example, if you want to test a function that gets user information, but you don’t want to actually request the database, you can create a fake database module that directly returns the user data you want. This makes your tests fast and stable.
Test-Driven Development (TDD): This is a “prophetic” development pattern. Its process is:
- Write a failing test: Before writing any functional code, write a test for it first. Since the function isn’t implemented yet, this test should fail.
- Write minimal code to make the test pass: Use the simplest, even most “ugly” code to turn the red light green.
- Refactor: Now the test passes, you have a safety net. Start optimizing and refactoring your code with confidence, always ensuring tests remain green.
TDD is a powerful discipline that forces you to think deeply and write high-quality code.
Chapter 4: Healing Hands — Debug Bugs Like a Detective
Even with testing, bugs are still the programmer’s destiny. But debugging ability determines whether you’re “ordinary” or “excellent.”
When a function returns undefined, don’t panic. You have at least three weapons in your arsenal:
console.log()Method: Simple and brutal, but effective. On key paths in your code, sprinkle variables like breadcrumbs, printing them out to see where the problem occurs.- Breakpoints: This is the most professional weapon. In browser developer tools, set a “pause point” for your code. When code executes here, the whole world stops for you. You can calmly check the state of all variables, step through execution, and understand everything.
debugger;Keyword: Writedebugger;directly in your code, with the same effect as setting a breakpoint. When the browser executes here, it automatically opens developer tools and pauses.
When interviewers ask how you debug, they don’t want to see if you can use console.log, but whether you have a systematic, coarse-to-fine logic for troubleshooting problems.
Chapter 5: Path to Ascension — Pursuing Code Excellence
Code Coverage: This is a metric measuring how much of your business code your tests “cover.” For example, Jest can tell you that your tests ran through 80% of your code. But remember, 100% coverage doesn’t mean 100% bug-free. It only proves your code was executed, but can’t prove all logic branches (especially those tricky edge cases) were correctly tested. Pursuing high coverage is good, but never be blinded by numbers.
Async Testing: Modern JavaScript can’t do without asynchrony. Testing async code is like defusing a time bomb; you need async/await and Jest’s .resolves / .rejects to ensure you can catch that future result.
test('test async data fetching', async () => {
// I assert that the Promise fetchData() will succeed
// and its resolved value should equal { id: 1 }
await expect(fetchData()).resolves.toEqual({ id: 1 });
});
Summary: The Leap from “Functional” to “Reliable”
Testing and debugging are invisible watersheds that divide programmer levels. It’s not a bunch of tools or commands, but a way of thinking, an extreme pursuit of excellent engineering quality.
When you next face the soul-searching question “how to test,” I hope you won’t hesitate. Your answer will no longer be scattered knowledge points, but a complete, confident professional system.
🎯 Action Guide: Start Your Testing Journey Now
Step 1: Set Up Testing Environment
# Install Jest testing framework
npm install --save-dev jest
# Install Cypress for E2E testing
npm install --save-dev cypress
Step 2: Write Your First Test
// calculator.js
function add(a, b) {
return a + b;
}
module.exports = { add };
// calculator.test.js
const { add } = require('./calculator');
describe('Calculator Tests', () => {
test('1 + 2 should equal 3', () => {
expect(add(1, 2)).toBe(3);
});
test('adding negative numbers', () => {
expect(add(-1, -2)).toBe(-3);
});
});
Step 3: Master Debugging Techniques
- Use Chrome DevTools: Press F12, go to Sources tab to set breakpoints
- VS Code Debugging: Configure launch.json for one-click debugging
- Advanced console.log: Use console.table, console.group, etc.
Step 4: Establish Testing Habits
- ✅ Write tests immediately after writing each function
- ✅ Write tests to reproduce bugs before fixing them
- ✅ Ensure all tests pass when refactoring code
- ✅ Regularly check code coverage
🚀 Advanced Resource Recommendations
- Jest Official Documentation: https://jestjs.io/
- Cypress Testing Guide: https://docs.cypress.io/
- Chrome DevTools Debugging: https://developers.google.com/web/tools/chrome-devtools
- Test-Driven Development Practice: https://martinfowler.com/bliki/TestDrivenDevelopment.html
💬 Interactive Discussion What testing challenges have you encountered? Welcome to share your experiences and confusions in the comments!
Want to have your own unique skills in the technology world? Follow Dream Beast Programming WeChat Official Account to unlock more black tech.
🔥 Hot Recommendations:
