Level 4 · Full-Stack Application Development · Course 4.3 Backend with Node.js

Module 46: Backend Testing

Goal: test a backend at every level: unit tests for rules, integration tests for parts working together, API tests for real HTTP requests, with mocks where needed and coverage to find gaps. You'll finish the Course 4.3 project.

You'll need: your enquiries-api from Module 45, and Module 26's testing skills.

Lessons

  1. 1What to test in a backend15–25 minutes✓
  2. 2Unit tests for services15–25 minutes✓
  3. 3API tests with Supertest15–25 minutes✓
  4. 4Mocks and test doubles15–25 minutes✓
  5. 5Coverage and test habits15–25 minutes✓

🛠️Module practice: Course 4.3 project, an enquiries API

Complete the enquiries API:

  1. Layered Express API with validated input and consistent errors.
  2. Staff sign-in with sessions; admin-only actions (delete, assign).
  3. Attachments with safe uploads; structured logs; error reporting.
  4. Unit, integration and API tests, including every permission rule; coverage report.
  5. README with setup, environment variables, endpoints and test instructions. Commit.

✅Module check

Explain in a few sentences, in your own words:

How do you test a backend so you can trust it?

Show a good answer

A good answer mentions: many fast unit tests for services and schemas using in-memory repositories and fresh data per test; API tests that send real HTTP requests (with Supertest) to check status codes, bodies, sign-in, validation errors and that errors leak no details; permission tests for every rule; test doubles only at the edges (email, payments, time), including their failure paths; and coverage used to find untested risky code, not as a goal in itself. Tests run before every commit and in CI, and are never weakened to pass.

📝Module quiz

10 questions · 10 marks each · 100 marks in all · pass mark 70% · retry as often as you like ·

1. Which kind of backend test should there be most of?

Why each option is right or wrong

  • “End-to-end tests, because they check the whole system at once” (Incorrect): They're valuable but slow and fragile, and a failure doesn't say where the problem is.
  • “Manual tests by a person clicking through the app before release” (Incorrect): Manual checks are slow and easy to skip; automated tests run every time.
  • “Load tests, which show how many users the server can handle” (Incorrect): Load matters, but correctness tests come first and should be most common.
  • “Unit tests: fast, and they pinpoint what broke” is correct.

    Unit tests check one function or service at a time with no network or database, so hundreds run in seconds and a failure points straight at the cause. Fewer API tests check routes are wired correctly, and a handful of end-to-end tests check the whole system.

    • Test pyramid: many unit tests, some API tests, few end-to-end tests.
    • Fast tests: get run often, so they catch problems early.

📖 Revise: Lesson 1: What to test in a backend

Question ID module-46-q1 · Report a mistake in this question
2. Why are permission tests so important?

Why each option is right or wrong

  • “They're the only tests that run quickly enough to use in CI” (Incorrect): Most unit tests are fast; permission tests matter because failures are silent.
  • “Permission bugs usually crash the server, so they're easy to spot” (Incorrect): Permission bugs usually don't crash anything, which is why they need tests.
  • “They make sign-in faster for the people using the app” (Incorrect): Tests don't change speed; they check who may do what.
  • “A missing check lets people see or change data unnoticed” is correct.

    When a permission check is missing, nothing looks broken: the page works, just for the wrong people. Only a test that tries the forbidden action, like staff deleting an enquiry, reveals it before an attacker does.

    • Test both: allowed users succeed, others get 403 or 404.
    • Every role: check each one against each sensitive action.

📖 Revise: Lesson 1: What to test in a backend

Question ID module-46-q2 · Report a mistake in this question
3. Why use beforeEach to set up fresh data?

Why each option is right or wrong

  • “It makes the whole test suite run noticeably faster” (Incorrect): Resetting adds a little work; the benefit is reliable results.
  • “Tests can't create data unless beforeEach is used” (Incorrect): Tests can create data themselves; beforeEach just avoids repetition.
  • “It runs the tests in a random order each time” (Incorrect): It doesn't change the order; it resets data before each test.
  • “So each test starts the same and isn't affected by others” is correct.

    If tests share data, one test's changes can make another pass or fail depending on the order they run. Resetting before each test makes every test independent and its result repeatable.

    • Symptom of shared state: tests pass alone but fail together.
    • In-memory repositories: make a fresh one in beforeEach.

📖 Revise: Lesson 2: Unit tests for services

Question ID module-46-q3 · Report a mistake in this question
4. A test passes on Monday and fails on Saturday. What might cause it?

Why each option is right or wrong

  • “The test server is slower at weekends” (Incorrect): Speed doesn't change a result from pass to fail; the date does.
  • “Test runners skip some tests on weekends” (Incorrect): Runners don't check the day; the code under test does.
  • “The test files were changed by someone on Friday” (Incorrect): Then it would fail every day after, not only on Saturday.
  • “The code depends on the current date; pass the date in or fake timers” is correct.

    Code that calls new Date() inside, for example to check working days or overdue status, gives different answers on different days. Passing "now" in as a parameter, or using fake timers in tests, makes the result predictable.

    • Example: isOverdue(enquiry, now).
    • Node test runner and Vitest: both support mocking timers.

📖 Revise: Lesson 2: Unit tests for services

Question ID module-46-q4 · Report a mistake in this question
5. Why can Supertest test the app without starting a server?

Why each option is right or wrong

  • “It only checks the code's types, without running any of it” (Incorrect): Supertest really runs the routes and checks responses.
  • “It downloads a copy of the live site and tests that instead” (Incorrect): It tests your local app, not the live site.
  • “Express apps don't need a server to handle requests in production” (Incorrect): In production you do listen on a port; Supertest just avoids it in tests.
  • “It uses the exported app directly, kept separate from the code that listens” is correct.

    Because app.ts exports the Express app and server.ts calls listen, a test can pass app to Supertest, which sends requests to it internally. No port is needed, so tests are quick and don't clash.

    • Example: await request(app).get("/enquiries").expect(200).
    • Requires: the app/server split from earlier.

📖 Revise: Lesson 3: API tests with Supertest

Question ID module-46-q5 · Report a mistake in this question
6. Why not send real emails during tests?

Why each option is right or wrong

  • “Email services refuse to send anything from test code” (Incorrect): They'll send it, which is exactly the risk.
  • “Emails can't contain the test data needed for checking” (Incorrect): They can; the problems are speed, reliability and real recipients.
  • “It makes the tests pass when they should fail” (Incorrect): The issue isn't false passes; it's slowness and emailing real people.
  • “It's slow, unreliable to check and would email real people” is correct.

    Real email depends on outside services, takes time, and is hard to verify automatically. Worse, a test with real addresses can spam customers. A fake email sender records messages so tests can check them instantly.

    • Test double: fakeMailer.sent holds the messages.
    • Check: the right person, subject and link.

📖 Revise: Lesson 4: Mocks and test doubles

Question ID module-46-q6 · Report a mistake in this question
7. What's the risk of mocking everything?

Why each option is right or wrong

  • “Mocks make tests too slow to run often” (Incorrect): Mocks usually make tests faster; the risk is false confidence.
  • “Test runners limit how many mocks you can use” (Incorrect): There's no such limit; the problem is tests drifting from reality.
  • “Mocked tests can't fail even when the code is wrong” (Incorrect): They can fail, but they may also pass when the real system is broken.
  • “Tests can pass while the real system is broken” is correct.

    Mocks behave the way you told them to. If the real database or service behaves differently, every test still passes because none touch the real thing. Keep some tests that use real components, like a test database.

    • Mock: slow, costly or outside services such as email and payments.
    • Keep real: your own code and, in some tests, the database.

📖 Revise: Lesson 4: Mocks and test doubles

Question ID module-46-q7 · Report a mistake in this question
8. Coverage is 100%. Is the code bug-free?

Why each option is right or wrong

  • “Yes, 100% coverage means every possible case was tested” (Incorrect): Coverage measures lines run, not every input or case.
  • “Yes, as long as all the tests are passing as well” (Incorrect): Tests can pass while checking little or nothing.
  • “No, because coverage tools can only count up to 90%” (Incorrect): Tools can report 100%; it still isn't proof.
  • “No, coverage shows which code ran, not whether results were checked” is correct.

    A test can run every line without asserting anything useful. Coverage also can't spot missing code, like a permission check nobody wrote. It's a guide to untested areas, not proof of correctness.

    • Useful for: finding code no test touches.
    • Ask: do the tests check the right results and edge cases?

📖 Revise: Lesson 5: Coverage and test habits

Question ID module-46-q8 · Report a mistake in this question
9. What does request.agent(app) do that request(app) doesn't?

Why each option is right or wrong

  • “Sends requests from several users at the same time” (Incorrect): It acts as one browser keeping its cookies, not many users.
  • “Pretends to be a mobile phone instead of a computer” (Incorrect): It isn't about device type; it keeps cookies.
  • “Starts a real server on a port for the tests” (Incorrect): Neither starts a real server; the agent just keeps cookies.
  • “Keeps cookies between requests, like a browser” is correct.

    Each request(app) call is independent, so a sign-in cookie from one isn't sent with the next. An agent stores cookies, so you can sign in once and then test the routes that need a session.

    • Example: const agent = request.agent(app); await agent.post("/login")....
    • Then: await agent.get("/enquiries").expect(200).

📖 Revise: Lesson 3: API tests with Supertest

Question ID module-46-q9 · Report a mistake in this question
10. Where should you focus test coverage?

Why each option is right or wrong

  • “On the shortest functions, since they're quickest to test” (Incorrect): Easy-to-test code isn't necessarily where the risk is.
  • “Equally on every line, so the coverage number looks high” (Incorrect): A high number matters less than testing what's risky.
  • “On code written by AI, and never on code you wrote yourself” (Incorrect): All code can have bugs; risk decides where to focus.
  • “On risky code: permissions, money, validation and errors” is correct.

    Bugs in permissions leak data, bugs with money cost real amounts, and missing validation lets bad data in. Test these areas thoroughly, including unhappy paths, before simple code like formatting a label.

    • Ask: what would hurt most if it broke?
    • Include: failure cases, not just success.

📖 Revise: Lesson 5: Coverage and test habits

Question ID module-46-q10 · Report a mistake in this question

ProLearners Assist