💻 Coding

Java Developer Prompt: Code Review with JUnit 5 Test Plan

Review a pasted Java class for money math, null handling, resource leaks, and thread safety, then get a minimal patch and the JUnit 5 tests that prove each fix.

0.0
0Reviews
P
October 17, 2026

Prompt

Act as a senior Java developer who reviews a pasted Java class or diff for correctness, exception handling, resource leaks, thread safety, and readability, then writes the JUnit 5 tests that prove each fix before anyone merges it.

Inputs:
- The Java code or diff to review: [JavaCode]
- Java version and build tool (Maven or Gradle): [JavaVersion]
- Framework and version, or "plain Java": [Framework]
- Test libraries already in the project (JUnit 5, Mockito, AssertJ): [TestStack]
- What the code is supposed to do: [Intent]
- What I cannot change (public method signatures, database schema, dependencies): [Constraints]

Generate:
1. Behavior summary: what JavaCode does today, in 3 to 5 lines, and where it differs from Intent.
2. Findings table: severity (bug, risk, style), method or line, the issue, why it matters, and the fix. Check money math with double, null inputs, swallowed exceptions, unclosed resources, shared mutable state and non-thread-safe classes, legacy java.util.Date use, and equals and hashCode pairs.
3. Constraint conflicts: any fix that would break Constraints, with a compatible option such as an overload or an adapter method.
4. Minimal patch: only the changed methods, using language features available in JavaVersion and APIs that exist in the JDK or the listed libraries.
5. JUnit 5 tests: one test per bug finding, named for the behavior, using only TestStack, with the input that fails today and the assertion that passes after the patch.
6. Run commands for the build tool in JavaVersion (for example mvn test or ./gradlew test) and the single-test command for the new tests.
7. Questions for the team: business decisions the code cannot answer, such as rounding rules.

Rules:
- Never invent library methods, annotations, or versions; if unsure an API exists in JavaVersion, say so.
- Keep secrets, keys, and customer data out of the pasted code; replace them with placeholders.
- Do not rewrite working code for style alone; style notes stay in the table.

Instructions

Paste one class or one diff at a time and remove secrets, keys, and customer data first. Works on ChatGPT, Claude, and Gemini. Run the generated tests before and after the patch so you see them fail first, and check any API you do not recognize against the JDK docs for your Java version.

Generated Output

This image was generated using the prompt above.

Java Developer Prompt: Code Review with JUnit 5 Test Plan - Result

Examples

Example Input

JavaCode:
public class InvoiceTotals {
  private static final SimpleDateFormat FMT = new SimpleDateFormat("yyyy-MM-dd");
  public double total(List<LineItem> items, double taxRate) {
    double sum = 0;
    for (LineItem i : items) sum += i.price() * i.qty();
    return sum + sum * taxRate;
  }
  public String dueDate(Date issued, int days) {
    return FMT.format(new Date(issued.getTime() + days * 86400000L));
  }
  public List<String> loadSkus(String path) {
    try {
      BufferedReader r = new BufferedReader(new FileReader(path));
      List<String> lines = new ArrayList<>();
      String l; while ((l = r.readLine()) != null) lines.add(l.trim());
      return lines;
    } catch (Exception e) { return null; }
  }
}
JavaVersion: Java 17, Maven
Framework: Spring Boot 3.2, InvoiceTotals is called from a @Service used by many requests at once
TestStack: JUnit 5, Mockito, AssertJ
Intent: exact invoice totals in dollars and cents, due dates in the business time zone, SKU list from a file
Constraints: keep the existing public method signatures; callers still use them

Example Output

1. BEHAVIOR SUMMARY
total() adds price x qty in double and applies tax, so cents can drift. dueDate() shares one SimpleDateFormat across threads and ignores time zones. loadSkus() never closes the file and returns null on any error. Intent asks for exact cents and business time zone dates.

2. FINDINGS
| Severity | Where | Issue | Why it matters | Fix |
| bug | total() | double money math | 0.10 x 3 sums to 0.30000000000000004 | BigDecimal with an agreed scale and rounding mode |
| bug | FMT, dueDate() | SimpleDateFormat is not thread-safe, shared static | concurrent calls from the @Service can return wrong dates | java.time LocalDate + DateTimeFormatter (immutable, thread-safe) |
| bug | loadSkus() | reader never closed | file handle leak per call | Files.readAllLines(Path.of(path)) or try-with-resources |
| risk | loadSkus() | catch (Exception) returns null | callers get NullPointerException later, cause lost | throw UncheckedIOException with the cause |
| risk | total() | null items list | NullPointerException | Objects.requireNonNull(items, "items") |
| style | dueDate() | 86400000L magic number | hard to read | plusDays(days) |

3. CONSTRAINT CONFLICTS
total() must keep returning double. Add totalExact(List<LineItem>, BigDecimal) returning BigDecimal, and have total() delegate and call doubleValue() so existing callers keep working. dueDate() keeps its Date parameter and converts inside.

4. MINIMAL PATCH
private static final DateTimeFormatter FMT = DateTimeFormatter.ISO_LOCAL_DATE;
private final ZoneId zone; // business zone, injected

public BigDecimal totalExact(List<LineItem> items, BigDecimal taxRate) {
  Objects.requireNonNull(items, "items");
  BigDecimal sum = items.stream()
      .map(i -> BigDecimal.valueOf(i.price()).multiply(BigDecimal.valueOf(i.qty())))
      .reduce(BigDecimal.ZERO, BigDecimal::add);
  return sum.add(sum.multiply(taxRate)).setScale(2, RoundingMode.HALF_UP); // confirm rule (see 7)
}
public double total(List<LineItem> items, double taxRate) {
  return totalExact(items, BigDecimal.valueOf(taxRate)).doubleValue();
}
public String dueDate(Date issued, int days) {
  return issued.toInstant().atZone(zone).toLocalDate().plusDays(days).format(FMT);
}
public List<String> loadSkus(String path) {
  try {
    return Files.readAllLines(Path.of(path)).stream().map(String::trim).toList();
  } catch (IOException e) {
    throw new UncheckedIOException("Cannot read SKU file " + path, e);
  }
}

5. JUNIT 5 TESTS
@Test void totalExact_keepsCents() {
  var items = List.of(new LineItem(0.10, 3));
  assertThat(totals.totalExact(items, BigDecimal.ZERO)).isEqualByComparingTo("0.30");
}
@Test void dueDate_usesBusinessZone() {
  Date issued = Date.from(Instant.parse("2026-03-01T03:00:00Z"));
  assertThat(nyTotals.dueDate(issued, 30)).isEqualTo("2026-03-30"); // 22:00 on Feb 28 in New York
}
@Test void loadSkus_missingFile_throwsWithCause() {
  assertThatThrownBy(() -> totals.loadSkus("missing.txt"))
      .isInstanceOf(UncheckedIOException.class).hasCauseInstanceOf(IOException.class);
}
@Test void totalExact_nullItems_failsFast() {
  assertThatNullPointerException().isThrownBy(() -> totals.totalExact(null, BigDecimal.ZERO));
}

6. RUN
mvn test
mvn -Dtest=InvoiceTotalsTest test

7. QUESTIONS FOR THE TEAM
- Rounding: round tax per line or per invoice, and HALF_UP or HALF_EVEN?
- Which ZoneId is the business time zone?
- Should a missing SKU file stop the request or return an empty list?

Reviews (0)

Please login to leave a review.
Loading reviews...