Title: Testing Tdd — LearnCoachAssist

Q: What is a unit test?
A: A unit test verifies the behavior of a single, isolated piece of code (e.g., a function or method). Key characteristics:
- Tests one logical unit in isolation
- Dependencies are mocked or stubbed
- Runs fast (milliseconds)
- Deterministic and repeatable
Example in Jest:
test('add returns sum', () => { expect(add(2, 3)).toBe(5); });

Q: What is an integration test?
A: An integration test verifies that multiple components work correctly together. Unlike unit tests, it exercises real interactions:
- Tests communication between modules, services, or layers
- May use real databases, APIs, or file systems
- Slower than unit tests but catches interface bugs
- Example: testing a controller that queries a real database

Q: What is an end-to-end (E2E) test?
A: An E2E test simulates real user workflows through the entire application stack. It validates the system from the user's perspective:
- Uses tools like Playwright, Cypress, or Selenium
- Tests full user journeys (login → action → logout)
- Slowest and most brittle test type
- Catches integration issues across the whole system

Q: What is the Test Pyramid?
A: The Test Pyramid is a testing strategy that recommends:
- Base (most): Unit tests — fast, cheap, numerous
- Middle: Integration tests — moderate speed and count
- Top (fewest): E2E tests — slow, expensive, few
The idea is to have many small, fast tests and fewer large, slow ones. This gives fast feedback while still covering the full stack. Coined by Mike Cohn.

Q: What is TDD (Test-Driven Development)?
A: TDD is a development practice where you write tests before writing production code. The cycle is:
1. Red: Write a failing test for the desired behavior
2. Green: Write the minimum code to make the test pass
3. Refactor: Clean up the code while keeping tests green
Benefits: better design, fewer bugs, living documentation, and confidence in refactoring.

Q: What does "Red-Green-Refactor" mean in TDD?
A: Red-Green-Refactor is the TDD cycle:
- Red: Write a test that fails (no implementation yet)
- Green: Write just enough code to pass the test
- Refactor: Improve code structure without changing behavior
The key rule: never write production code without a failing test first. Each iteration should be small (minutes, not hours).

Q: What is mocking in testing?
A: Mocking replaces a real dependency with a controlled fake object that can:
- Return predefined values
- Track how it was called (arguments, call count)
- Verify interactions between the system under test and its dependencies
Example in Jest:
const mockFn = jest.fn().mockReturnValue(42);
Mocks are useful for isolating units and testing behavior without side effects.

Q: What is the difference between a mock and a stub?
A: Stub: Provides canned answers to calls. Used for state verification — you check the result.
Mock: Pre-programmed with expectations. Used for behavior verification — you check that specific methods were called.
Example:
- Stub: getUser() always returns { name: "Alice" }
- Mock: Asserts getUser() was called exactly once with ID 5

Q: What is a spy in testing?
A: A spy wraps a real function, allowing it to execute normally while also recording information about its calls:
- Tracks call count, arguments, return values
- Does not replace the implementation (unlike mocks/stubs)
- Useful for verifying side effects without changing behavior
Example in Jest:
const spy = jest.spyOn(obj, 'method');
expect(spy).toHaveBeenCalledWith('arg');

Q: What are the five types of test doubles?
A: Test doubles replace real dependencies in tests:
1. Dummy: Passed around but never used (fills parameter lists)
2. Stub: Provides canned responses to calls
3. Spy: Records calls while executing real logic
4. Mock: Pre-programmed with expectations to verify behavior
5. Fake: Working implementation with shortcuts (e.g., in-memory database)
Term coined by Gerard Meszaros in xUnit Test Patterns.

Q: What is BDD (Behavior-Driven Development)?
A: BDD extends TDD by writing tests in natural language that describe behavior from a user's perspective:
- Uses ubiquitous language shared by devs, QA, and stakeholders
- Tests are structured as scenarios with Given-When-Then
- Tools: Cucumber, SpecFlow, Behave
BDD focuses on what the system does, not how it's implemented.

Q: What is Gherkin syntax in BDD?
A: Gherkin is a structured language for writing BDD scenarios:
Feature: User login
  Scenario: Valid credentials
    Given a registered user
    When they enter valid credentials
    Then they should see the dashboard
Keywords: Feature, Scenario, Given, When, Then, And, But. It's human-readable and executable.

Q: What is test coverage?
A: Test coverage measures how much of your code is executed during testing. Common metrics:
- Line coverage: % of lines executed
- Branch coverage: % of decision branches taken
- Function coverage: % of functions called
- Statement coverage: % of statements executed
Tools: Istanbul/nyc (JS), coverage.py (Python), JaCoCo (Java). High coverage ≠ good tests.

Q: What is the difference between line coverage and branch coverage?
A: Line coverage: Measures whether each line of code was executed at least once.
Branch coverage: Measures whether each branch of every decision point (if/else, switch, ternary) was taken.
Example:
if (x > 0) { doA(); } else { doB(); }
A test with x = 1 gives 50% line coverage for this block but only 50% branch coverage. You need x = -1 too for full branch coverage.

Q: What is path coverage?
A: Path coverage measures whether every possible execution path through a function has been tested. It considers all combinations of branches:
- More thorough than branch coverage
- Number of paths grows exponentially with branches
- Often impractical for complex functions
For n independent if statements, there are 2^n paths. It's the most rigorous coverage metric but rarely achievable at 100%.

Q: What is the AAA pattern in testing?
A: The AAA (Arrange-Act-Assert) pattern structures tests into three clear phases:
1. Arrange: Set up test data, mocks, and preconditions
2. Act: Execute the code under test
3. Assert: Verify the expected outcome
Example:
// Arrange
const calc = new Calculator();
// Act
const result = calc.add(2, 3);
// Assert
expect(result).toBe(5);

Q: What are assertion patterns and matchers?
A: Assertions verify expected outcomes. Matchers are methods that define how values are compared:
- toBe(value) — strict equality
- toEqual(obj) — deep equality
- toContain(item) — array/string inclusion
- toThrow() — exception thrown
- toBeGreaterThan(n) — numeric comparison
- toMatch(/regex/) — pattern matching
Frameworks: Jest, Chai, AssertJ, Hamcrest.

Q: What is snapshot testing?
A: Snapshot testing captures the output of a component or function and compares it against a stored reference:
- First run: saves output as a snapshot file
- Subsequent runs: compares output to saved snapshot
- If different, the test fails — developer must accept or reject changes
Common in React with Jest:
expect(render(<Button />)).toMatchSnapshot();
Useful for UI testing but can become brittle if overused.

Q: What is property-based testing?
A: Property-based testing generates random inputs to verify that properties (invariants) hold for all cases:
- Instead of specific examples, you define properties the code must satisfy
- The framework generates hundreds/thousands of test cases
- On failure, it shrinks the input to the minimal failing case
Tools: QuickCheck (Haskell), Hypothesis (Python), fast-check (JS).
Example property: reverse(reverse(list)) == list

Q: What are test fixtures?
A: Test fixtures provide a fixed, known environment for tests. They include:
- Predefined data (database records, files)
- Object instances in a known state
- Environment configuration
Setup/teardown hooks manage fixtures:
beforeEach() / afterEach() — per test
beforeAll() / afterAll() — per suite
Good fixtures are isolated, repeatable, and independent of test execution order.

Q: What is setup and teardown in testing?
A: Setup prepares the test environment before tests run. Teardown cleans up afterward.
- beforeEach: Runs before each test (reset state)
- afterEach: Runs after each test (cleanup)
- beforeAll: Runs once before the suite
- afterAll: Runs once after the suite
Purpose: ensure test isolation — each test starts with a clean slate and doesn't affect other tests.

Q: What is test isolation?
A: Test isolation means each test is independent and doesn't depend on or affect other tests:
- No shared mutable state between tests
- Tests can run in any order and still pass
- Each test sets up and tears down its own data
Violations cause flaky tests — tests that pass or fail depending on execution order or timing. Use fresh fixtures and avoid global state.

Q: What is regression testing?
A: Regression testing verifies that previously working features still work after code changes:
- Re-runs existing test suites after modifications
- Catches unintended side effects of new code
- Often automated in CI/CD pipelines
Strategies:
- Full regression: Run all tests
- Selective regression: Run tests related to changed code
- Risk-based: Prioritize tests for critical features

Q: What is smoke testing?
A: Smoke testing (aka build verification testing) is a quick, shallow test to check if the most critical functions work:
- Runs before deeper testing begins
- Answers: "Does the build even work?"
- Checks basic operations: app starts, login works, main page loads
- Named after hardware testing — "turn it on and see if smoke comes out"
If smoke tests fail, the build is rejected immediately.

Q: What is sanity testing?
A: Sanity testing is a focused, narrow test after a specific change to verify the fix works:
- Subset of regression testing
- Verifies a particular bug fix or feature change
- Not exhaustive — just checks the specific area
Smoke vs Sanity:
Smoke = broad, shallow (does the whole system boot?)
Sanity = narrow, deep (does this specific fix work correctly?)

Q: What is performance testing?
A: Performance testing evaluates how a system behaves under various conditions:
- Load testing: Behavior under expected load
- Stress testing: Behavior beyond normal capacity
- Spike testing: Sudden large load increases
- Endurance testing: Sustained load over time
Metrics: response time, throughput, resource utilization.
Tools: JMeter, k6, Gatling, Locust.

Q: What is load testing?
A: Load testing simulates expected concurrent users/requests to verify the system meets performance requirements:
- Identifies bottlenecks under realistic conditions
- Measures response time, throughput, and error rates
- Establishes performance baselines
Example with k6:
export default function() {
  http.get('https://api.example.com');
  sleep(1);
}

Q: What is Jest?
A: Jest is a JavaScript testing framework by Meta (Facebook):
- Zero-config setup for most JS projects
- Built-in mocking, spying, and assertion library
- Snapshot testing support
- Code coverage reporting via --coverage
- Parallel test execution for speed
Example:
describe('math', () => {
  test('adds numbers', () => {
    expect(1 + 2).toBe(3);
  });
});

Q: What is pytest?
A: pytest is a popular Python testing framework known for its simplicity:
- Uses plain assert statements (no special API)
- Auto-discovers test files and functions
- Powerful fixtures with dependency injection
- Rich plugin ecosystem (pytest-cov, pytest-mock, pytest-xdist)
Example:
def test_add():
    assert add(2, 3) == 5
Run with: pytest -v

Q: What is JUnit?
A: JUnit is the standard unit testing framework for Java:
- JUnit 5 (Jupiter) is the latest version
- Uses annotations: @Test, @BeforeEach, @AfterAll, @DisplayName
- Supports parameterized tests with @ParameterizedTest
- Integrated with Maven, Gradle, and all Java IDEs
Example:
@Test void shouldAdd() {
  assertEquals(5, calc.add(2, 3));
}

Q: What is Mocha?
A: Mocha is a flexible JavaScript test framework for Node.js and browsers:
- BDD-style syntax with describe and it
- Requires separate assertion library (e.g., Chai)
- Requires separate mocking library (e.g., Sinon)
- Supports async tests with callbacks, promises, and async/await
Example:
describe('Array', () => {
  it('should return -1 when not found', () => {
    assert.equal([1,2,3].indexOf(4), -1);
  });
});

Q: What is contract testing?
A: Contract testing verifies that services (consumer and provider) agree on the API interface:
- Consumer-driven: Consumer defines expected interactions
- Provider verifies: Provider tests against consumer contracts
- Catches breaking API changes early
Tool: Pact is the most popular contract testing framework.
Unlike integration tests, each side is tested independently using the shared contract.

Q: What is mutation testing?
A: Mutation testing evaluates test quality by introducing small code changes (mutants) and checking if tests catch them:
- Mutants: Change + to -, == to !=, remove statements
- Killed mutant: A test fails (good — tests caught the bug)
- Survived mutant: No test fails (bad — tests have gaps)
Mutation score = killed / total mutants.
Tools: Stryker (JS), PITest (Java), mutmut (Python).

Q: What are flaky tests?
A: Flaky tests pass or fail intermittently without code changes. Common causes:
- Shared mutable state between tests
- Timing/race conditions in async code
- External dependency failures (network, DB)
- Order-dependent tests
Remedies:
- Isolate tests properly
- Use deterministic waits instead of sleep()
- Mock external services
- Quarantine flaky tests and fix them

Q: How do you handle flaky tests in CI/CD?
A: Strategies for managing flaky tests in CI/CD:
- Quarantine: Move flaky tests to a separate suite that doesn't block deploys
- Retry: Auto-retry failed tests (e.g., jest --retry 2)
- Track: Log flaky test history to identify patterns
- Fix root cause: Prioritize fixing over retrying
- Timeout tuning: Use explicit waits, not arbitrary sleeps
Never ignore flaky tests — they erode trust in the test suite.

Q: What is CI/CD testing strategy?
A: A CI/CD testing strategy defines what tests run at each pipeline stage:
- Pre-commit: Linting, unit tests (fast feedback)
- CI build: Unit + integration tests
- Staging: E2E tests, smoke tests
- Pre-deploy: Contract tests, security scans
- Post-deploy: Smoke tests, synthetic monitoring
Goal: shift left — catch bugs as early as possible. Fast tests run first; slow tests gate later stages.

Q: What does "shift left" mean in testing?
A: Shift left means moving testing activities earlier in the development lifecycle:
- Write tests during development, not after
- Run automated tests on every commit
- Include security and performance testing early
- Review testability during design
Benefits: bugs found earlier are cheaper to fix. A bug caught in development costs 10x less than one found in production.

Q: What is test independence?
A: Test independence means each test can run in any order and produce the same result:
- No shared state between tests
- Each test creates its own data and cleans up
- No reliance on test execution sequence
Violation example: Test B depends on data created by Test A. If A is skipped or runs after B, B fails.
Use beforeEach/afterEach hooks to ensure a fresh state.

Q: What is a test suite?
A: A test suite is a collection of related test cases grouped together:
- Organized by feature, module, or test type
- Can be nested (suites within suites)
- Run as a unit for targeted testing
Example in Jest:
describe('UserService', () => {
  describe('createUser', () => {
    test('creates with valid data', ...);
    test('rejects invalid email', ...);
  });
});

Q: What is a test runner?
A: A test runner is the tool that discovers, executes, and reports test results:
- Finds test files by naming convention or config
- Executes tests (optionally in parallel)
- Reports pass/fail status and timing
- Supports filtering, watching, and CI integration
Examples: jest (JS), pytest (Python), gradle test (Java), phpunit (PHP), go test (Go).

Q: What is code coverage and why can 100% coverage be misleading?
A: Code coverage measures the percentage of code exercised by tests. 100% can be misleading because:
- Covered ≠ tested — code may execute without meaningful assertions
- Doesn't test edge cases, error handling, or concurrency
- Can incentivize writing trivial tests to hit targets
- Misses logical errors in covered code
Better approach: aim for meaningful coverage (80-90%) with high-quality assertions, not 100% with weak tests.

Q: What is the Given-When-Then pattern?
A: Given-When-Then is a BDD test structuring pattern:
- Given: The initial context or preconditions
- When: The action or event that occurs
- Then: The expected outcome
Equivalent to AAA: Given=Arrange, When=Act, Then=Assert.
Example:
Given a logged-in user
When they click "Delete Account"
Then their account is deactivated

Q: What is a fake in testing?
A: A fake is a working implementation with simplified behavior used for testing:
- Has real logic but takes shortcuts
- Examples: in-memory database, fake file system, local email server
- More realistic than stubs but lighter than real dependencies
Unlike mocks, fakes don't verify interactions. Unlike stubs, fakes have working business logic. Example: FakeUserRepository backed by a HashMap instead of a real database.

Q: What is a dummy object in testing?
A: A dummy object is a placeholder that is passed around but never actually used:
- Fills required parameters in method signatures
- Has no behavior and no assertions on it
- Simplest form of test double
Example:
const dummyLogger = {} as Logger;
const service = new UserService(realRepo, dummyLogger);
Here, dummyLogger satisfies the constructor but the test doesn't exercise logging.

Q: What is parameterized testing?
A: Parameterized testing runs the same test logic with different input/output pairs:
- Reduces code duplication for similar test cases
- Each parameter set is a separate test case
Jest example:
test.each([
  [1, 2, 3],
  [0, 0, 0],
])('add(%i, %i) = %i', (a, b, expected) => {
  expect(add(a, b)).toBe(expected);
});
Also available in JUnit (@ParameterizedTest), pytest (@pytest.mark.parametrize).

Q: What is test-driven development's impact on code design?
A: TDD leads to better code design because:
- Forces you to think about the API before implementation
- Untestable code signals poor design (tight coupling, hidden dependencies)
- Encourages SOLID principles, dependency injection, and small functions
- Results in modular, loosely coupled code
Code that is easy to test is typically easy to maintain, extend, and understand.

Q: What is the difference between black-box and white-box testing?
A: Black-box testing: Tests functionality without knowing internal implementation.
- Based on requirements and specifications
- Tester sees only inputs and outputs
White-box testing: Tests with knowledge of internal code structure.
- Based on code paths, branches, and logic
- Tester examines source code
Unit tests are often white-box; E2E tests are typically black-box. Both are needed for comprehensive coverage.

Q: What is test debt?
A: Test debt is a form of technical debt where the test suite becomes a liability:
- Slow tests that delay CI/CD feedback
- Flaky tests that erode confidence
- Missing tests for critical paths
- Tightly coupled tests that break on refactoring
- Excessive mocking that hides real bugs
Manage it by: regularly reviewing test quality, deleting low-value tests, refactoring test code, and treating tests as production code.

Q: What is acceptance testing?
A: Acceptance testing verifies that the system meets business requirements and is ready for delivery:
- User Acceptance Testing (UAT): End users validate real-world scenarios
- Business Acceptance Testing: Stakeholders verify business rules
- Often the final testing phase before release
Can be automated using BDD tools (Cucumber, SpecFlow) with Gherkin scenarios written by product owners.

Q: What is the difference between assertEquals and assertSame?
A: assertEquals checks value equality — do two objects have the same content?
assertSame checks reference equality — are they the exact same object in memory?
Example (Java):
String a = new String("hello");
String b = new String("hello");
assertEquals(a, b); // ✅ passes
assertSame(a, b); // ❌ fails (different objects)
In Jest: toBe ≈ assertSame, toEqual ≈ assertEquals.