Qs: Testing


Why Testing?

Reduction in bugs reported after deployments, which led to faster release cycles and improved customer satisfaction.

Instill a culture of quality within the team.


How would you implement a more robust testing strategy?

Included both unit tests and end-to-end tests.

Assess current testing practices and identify key areas where additional testing could be most beneficial.

Used mental models like the Testing Pyramid to design our testing efforts and agreed on a desired code coverage.

In the initial step, we would be happy with anything above 60%.


What's A Testing Pyramid?

Metaphor for structuring automated tests.

Shape: pyramid — wide at the bottom, narrow at the top.

Core Idea:

  1. Lots of unit tests (fast, isolated).

  2. Fewer integration tests.

  3. Even fewer E2E/UI tests (slow, brittle).

From top to bottom:

  • Manual and Exploratory Tests

  • UI and API Testing

  • Integration Testing

  • Component Testing

  • Unit Testing


What's Unit Testing?

Individual pieces of code — the smaller the better.


What's Component Testing?

Tests objects independently, such as modules, classes, and programs. More thorough than unit testing. Often need access to a database or other components. Slower than unit tests.


What's Integration Testing?

Combines the individual software modules and tests them together as a group.


What's Manual and Exploratory Tests?

TODO


Code Coverage

Software testing metric that measures the percentage of a program's source code that is executed when a test suite is run.

Measure % of functions that are tested.


AAA Pattern

Technique to write adequate test cases.

A = Arrange A = Act A = Assert


"Given > When > Then" Pattern

technique to write adequate test cases.


What is a Test Double?

A generic term (Umbrella Term) for any kind of object that replaces a real component in testing.

Includes dummy, stub, fake, mock, and spy.


Why Test Doubles?

To isolate the system under test (SUT) from external complexity.

Typically: test a function in isolation.

To do so, we need to provide a placeholder or replacement.


Types of Test Doubles?

Type
Purpose
Behavior
Example Use Case

Dummy

Placeholder

Does nothing

Fill unused params

Stub

Provide test data

Returns predefined values

Feed input to SUT

Fake

Lightweight version

Works but not production-ready

External Calls: APIs, DBs...

Mock

Verify interactions

Checks if calls were made

Ensure method was called

Summary:

  1. Dummy = exists but unused

  2. Stub = provides answers

  3. Fake = simplified real thing

  4. Mock = checks expectations


What's a Dummy?

Passed around only because the method signature requires it.

Never actually used in the test.


What's a Stub?

Provides predefined responses.

Used when the SUT needs some data.

No verification of interactions.


Why Stubs? Use Cases?

  1. Avoiding External Calls - i.e. APIs, DBs - isolation & speed

  2. Controlling Edge Cases - i.e. simulate errors or unusual data

  3. Replacing Expensive or Random Behavior

  4. Simulating Slow or Complex Logic - skip heavy processing


What's a Fake?

Has a simplified but working implementation.

Not suitable for production.

Example: in-memory DB.


Why use Mocks in Unit Tests?

If we do not provide a mock, the test will become an INTEGRATION TEST - not a unit test - as it will evaluate the function and its dependencies together.

Used to verify interactions with dependencies.

Typically created with a mocking framework (like Jest).


Are test doubles only used in Unit Testing?

No — test doubles are not only used in unit testing.

But unit testing is where they are most common and essential.

Test doubles can also be useful outside of unit tests:

  • Integration Testing: Sometimes you still want to isolate part of the system, e.g., replace a payment provider with a fake service to avoid hitting real APIs.

  • System / End-to-End (E2E) Testing: Often discouraged, but still possible when external services are too costly or unstable. Example: Using a mocked email server to capture emails instead of sending real ones.


To write a unit test for the following function we need to also ...

  1. mock the validateUser function so the test is constrained to the current function

  2. stub the fetch call, and return mock data to make the test independent of the availability/response of the backend


What are Spies?

Spy function records the calls to the external function

For instance, to spy on a specific method, and make sure it was called with the right inputs.


When testing with mocks, why use model without relations?

"Without relations" means the type includes only the User's scalar fields, not the relation fields.

Scalar Field = stores a single, indivisible value — something that is not another object or record - i.e. string, int, boolean, date, etc

Relation fields = represents a relationship between two entities/models/tables - typically correspond to foreign keys - i.e. relations One-to-one, One-to-many, Many-to-many


Coverage threshold levels

Conservative (80% overall, 70% per-file)

  • Pros: Achievable quickly, less friction, focuses on critical paths

  • Cons: May miss edge cases, lower confidence in refactoring

  • Best for: Early projects, rapid iteration, small teams

Moderate (85% overall, 75% per-file)

  • Pros: Good balance, catches most issues, reasonable maintenance

  • Cons: Some gaps remain, requires consistent discipline

  • Best for: Most production projects, growing codebases

Strict (90% overall, 80% per-file)

  • Pros: High confidence, safer refactoring, catches edge cases

  • Cons: Higher maintenance, can slow development, may encourage low-value tests

  • Best for: Critical systems, mature codebases, regulated industries


Coverage metrics

Lines

  • Pros: Simple, fast, easy to understand

  • Cons: Misses untested branches/conditions, can be misleading

Statements

  • Pros: More accurate than lines, accounts for multi-line statements

  • Cons: Similar to lines, still misses branch coverage

Functions

  • Pros: Ensures functions are called, good for API coverage

  • Cons: Doesn't verify behavior, can pass with shallow tests

Branches

  • Pros: Catches untested if/else, switch cases, ternaries; most valuable

  • Cons: Harder to achieve, can be noisy with defensive code

All metrics (recommended)

  • Pros: Comprehensive, catches different failure modes

  • Cons: Strictest, requires thorough testing


Per-file vs global thresholds

Per-file threshold

  • Pros: Prevents files with 0% coverage, ensures consistent quality

  • Cons: Can be strict for utility files, may need exceptions

Global threshold only

  • Pros: Flexible, allows some files to be lower if others are higher

  • Cons: Can hide files with no coverage


TODOs

  • Testing REST APIs

  • Testing REST APIs: Postman?

  • Testing REST APIs: Integration Tests?

  • Testing REST APIs: Unit Tests? SuperTest library?

  • Testing REST APIs: cURL Scripts ?

Last updated