SwadhyAI Notes

All Low-Level Design notes

Production LLD

Testing an LLD design

You finished park and exit on the whiteboard. The interviewer asks how you would test this Low-Level Design (class-level design on the whiteboard). If the answer is "I would run main and type," you have no proof the second gate cannot steal a spot.

Testing LLD means proving the design with focused tests: happy paths, edge cases, and concurrency where shared state exists. Those tests read like the requirements: park a car, reject a full lot, freeze two hours, check the fee. Drive the . Assert an .

What actually happens when you only run main

The typed demo in the hero only proves the path you just drew. It does not reject a full lot or freeze two hours for a fee. Those requirements become named tests in the matrix below, not extra commentary after a Scanner loop (keyboard input into main).

A useful suite is small and reads like the spec you were given. Name each test after the requirement. One scenario per test: a method named test1 that asserts true is not documentation. The next section is the shape of one such test; the layers after that decide how many suites you bother to write.

The shape of one unit test

The story above says what to prove. is how one test proves it: set up a lot you control, call the , and check a ticket, a receipt, or an exception.

Arrange

Lot with one spot, nearest allocation, hourly price, and Clock.fixed(...) (a clock stuck on one instant). Fake TicketRepository if persistence is a port (an interface the domain owns).

Act

Call park(Vehicle) or exit(TicketId). Never a UI loop, never Thread.sleep to wait for a fee.

Assert

Receipt amount, ticket status, or exception type. Query isFull() or ticket(id). Reflection (reading private fields at runtime) on spots means the API is missing a query.

Simple rule of thumb

Name tests after the requirement. One scenario per test. Arrange with a frozen Clock (see Arrange above). Act through the service. Assert a ticket, a receipt, or an exception. Do not assert private occupancy maps (see Assert above).

Layers you actually need

Arrange-act-assert is one test. These bars are how much suite you write at each layer, from use cases through value objects (compared by value, not identity) down to adapters (port implementations) and UI. In an interview, the top bar is the one that matters: use-case tests through ParkingService.

Use-case tests through ParkingService

This is the suite that matters in a forty-five minute interview.

Value-object tests (Money, TicketId)

Reject negative amounts; compare Money and TicketId by value, not identity.

Adapter tests (optional)

Skip these adapter tests unless persistence itself is the problem.

UI / Scanner

Do not test Scanner or UI; keep both out of the domain.

Requirement matrix

Tests that read like the spec

The layers above say ParkingService is the suite that matters. Here is what that suite looks like as names, then as Java. Each row is one requirement, one test name, and one assert.

RequirementTest nameAssert
Park a carissuesTicketWhenSpotFreeticket status OPEN, spot taken
Reject a full lotcannotParkWhenLotIsFullLotFullException
Fee after two hoursairportFeeAfterFirstHourMoney equals tariff
No double occupancycannotIssueTwoTicketsForOneSpotsecond park fails
Closed stays closedcannotReactivateClosedTicketexception or no-op
Two gatestwoGatesCannotTakeSameSpotone success, one reject

A test that reads like the spec

with a frozen Clock. Time is a value you control. Sleeping until midnight is not a test.

java
@Test
void cannotIssueTwoTicketsForOneSpot() {
  Clock clock = Clock.fixed(Instant.parse("2026-01-15T10:00:00Z"), ZoneOffset.UTC);
  ParkingLot lot = new ParkingLot(oneSpot(), nearest(), hourly(Money.rupees(10)), clock);
  Ticket first = lot.park(car("KA-01"));
  assertThrows(LotFullException.class, () -> lot.park(car("KA-02")));
  assertEquals(first.spotId(), lot.ticket(first.id()).spotId());
}

@Test
void airportFeeAfterTwoHours() {
  MutableClock clock = MutableClock.at("2026-01-15T10:00:00Z");
  ParkingLot lot = new ParkingLot(oneSpot(), nearest(), airportTariff(), clock);
  Ticket ticket = lot.park(car("KA-01"));
  clock.advance(Duration.ofHours(2));
  Receipt receipt = lot.exit(ticket.id());
  assertEquals(Money.rupees(80), receipt.amount());
}

What not to write

java
@Test
void test1() {
  ParkingLot lot = new ParkingLot();          // real now(), real Random, Scanner
  lot.mainLoop();                             // types into stdin
  assertTrue(lot.spots.get(0).occupied);      // private field
}

You cannot assert a with stdin (typed input). Keep IO out of the domain so tests can exist. The next refactor that changes a private map should not break a product that still parks correctly.

Forty-five minutes

How to explain this in an interview

You will not type the whole matrix in forty-five minutes. Two named tests plus a spoken list still scores; zero tests and a God object (one class that knows everything) does not.

0–30

Working park/exit

Domain first; tests wait until the park and exit API exists.

30–38

Two tests

Write a happy path plus the invariant they asked about.

38–42

Speak the rest

Speak illegal transitions, a race, and the money edge.

42–45

Clock and fakes

Show how you would freeze time so a fee test does not wait.

You may only have time to write cannotParkWhenLotIsFull and airportFeeAfterTwoHours. Say the others. Mention Clock.fixed so the fee test is not a midnight flake (a failure only when the clock rolls over). Mention that a test would use two threads or a latch (a barrier that starts both parks together) if you claimed thread safety. That spoken list is part of the design, not leftover commentary.

Write

Happy path plus the they asked about (full lot, unique spot, fee).

Say

Illegal transitions, a closed ticket, two gates, and a negative Money constructor.

Common mistakes

Testing through main and Scanner

See What not to write above: main and Scanner are not a suite.

Asserting private occupancy maps

See Assert above: query isFull() or ticket status, not a private map.

Coverage of getters

Getter coverage such as assertEquals(ticket.getPlate(), "KA-01") teaches nothing; illegal states and fees lock the design.

Wrap-up

You have seen the failure of running main, the shape of one test, and what still scores in forty-five minutes. Check the ideas stuck, then follow the related notes.

Check yourself

  1. Write the test name for "EV cannot park in a compact-only lot." What do you assert?
  2. A colleague tests occupancy via reflection. Which query should exist instead?
  3. How do you prove a two-hour airport fee without waiting two hours?

Key takeaway

Name tests after requirements. Two solid tests through the beat a God object with none.