Java Module 14
Testing, build tools and code quality interview questions
Interview questions on testing, build tools and code quality: warm-ups, then what each harder question tests and how a good answer starts.
Warm-up questions
Short questions with short answers, each answered in full.
-
Fresher
What are the JUnit Platform, JUnit Jupiter and JUnit Vintage?
-
Fresher
What is the difference between @BeforeEach and @BeforeAll in JUnit?
Answered in the lesson Parameterised tests, lifecycle and AssertJ
-
Fresher
What is the difference between a stub and a mock?
-
Fresher
What are the three steps of the red, green, refactor cycle in test-driven development?
-
Fresher
What is the difference between a Maven phase and a goal?
Answered in the lesson Maven: POM, lifecycle and dependencies
-
Fresher
What does the Gradle wrapper do, and which of its files do you commit?
Answered in the lesson Gradle: Kotlin DSL, toolchains and tasks
-
Fresher to mid
How does Maven choose a version when two dependencies need different versions of the same library?
Answered in the lesson Dependency management and supply-chain safety
-
Fresher to mid
What do the javac options -Xlint:all and -Werror do?
Answered in the lesson Code quality: javac lint, javadoc, formatting
-
Fresher to mid
What is System.Logger, and how does it relate to java.util.logging?
Questions with model answers
What each question tests, the first step of a good answer and the follow-ups to expect.
-
Mid to senior
A test passes when you run its class alone in the IDE but fails in CI, where the whole suite runs, and nobody has changed it. What do you suspect, how do you prove it, and how do you stop it from coming back?
- What it tests
- Test isolation: state that outlives one test, order dependence, what the per-method lifecycle does and does not reset, and reproducing an order-dependent failure.
- First step
- Suspect state that outlives a test: a static field, a singleton or cache, a system property, the default locale or time zone, or a file another test left behind. The per-method lifecycle resets instance fields only. Reproduce the failure by running the suite in the order CI used, then narrow down which earlier test changes the state.
- Follow-ups
-
- How do you reproduce the CI order on your machine?
- Why does a new test instance per method not protect you here?
- How do you stop the same kind of failure from coming back?
From the lesson Unit testing with JUnit 6
Model answer, Pro. Model answers open with any pass; prices are in the notice on this page.Opening the model answers with your pass…
Model answers open with any MySmartCoPilot pass.
- Pro pass · 1 week₹79$2.99
- 30 days₹179$5.99
- 1 year₹1,699$45
One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.
Passes can’t be bought right now, so the model answers can’t be opened at the moment. If you have a pass, enter your licence key.
It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).
In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Fresher to mid
Your team's build failures read like "expected: <true> but was: <false>" or "expected: <84> but was: <85>", and each one needs a debugger to understand. What would you change in the tests, and how would you check that the change worked?
- What it tests
- Choosing the assertion that fits the check, the expected-then-actual order, useful messages, behaviour names and one behaviour per test.
- First step
- Read a failure as a stranger would see it: the test name, the message and the line. Then fix the three usual causes, in this order: assertTrue on comparisons, expected and actual values passed the wrong way round, and test names that name a method instead of a behaviour.
- Follow-ups
-
- When is a custom message worth adding?
- Why pass a Supplier<String> instead of a String?
- How many assertions belong in one test?
From the lesson Unit testing with JUnit 6
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
You replace twenty copied test methods for a postage calculator with one parameterised test of eight rows. The build stays green. How do you show that the new test still catches every bug the old ones caught?
- What it tests
- Telling coverage from test strength, mutation testing, boundary rows, equivalent mutants and keeping a refactoring of tests honest.
- First step
- A green build proves only that both versions pass on correct code. Run both against the same deliberately broken copies of the calculator, called mutants, and compare which each version kills: a refactoring of tests must not let through a mutant that the old tests killed.
- Follow-ups
-
- Line coverage is 100 % for both versions. Why is that not enough?
- One mutant survives every version of the tests. What do you do?
- How would you run this check on a real project?
From the lesson Parameterised tests, lifecycle and AssertJ
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Fresher to mid
In a code review you see a @CsvSource whose rows have three columns, one of them an expected error message, while the test method takes two parameters. All its tests are green. What is happening, how risky is it, and what do you change?
- What it tests
- How JUnit maps CSV columns to parameters, why extra arguments are ignored by default, strict argument counting and reviewing tests for checks that never run.
- First step
- Say what JUnit does with the third column: by default it drops arguments that the method does not declare, so the expected message is never compared with anything. The test is green because it checks less than it appears to.
- Follow-ups
-
- How do you make such a mismatch fail the build everywhere?
- Should the fix add a parameter or delete the column?
- What else in a test can look like a check without checking anything?
From the lesson Parameterised tests, lifecycle and AssertJ
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
A teammate speeds up a slow test class by adding @TestInstance(PER_CLASS) and moving its setup from @BeforeEach to @BeforeAll. The suite drops from 90 to 20 seconds, but some CI runs now fail in that class. Explain the failures and decide what to keep.
- What it tests
- What the per-method lifecycle guarantees, state shared through one instance, order-dependent failures, and separating expensive read-only setup from state that tests change.
- First step
- With one instance per class, everything set up in @BeforeAll is shared by all its tests, so a test that changes it changes what the next test starts from, and which tests fail depends on the order they run in.
- Follow-ups
-
- How do you prove that the failures depend on the order?
- Which part of the setup can safely stay shared?
- How would you keep most of the speed without sharing mutable state?
From the lesson Parameterised tests, lifecycle and AssertJ
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
After a refactoring that changes no behaviour, 40 tests fail with "Wanted but not invoked" and a few others now pass for the wrong reason. What does that say about the tests, and how do you fix them without losing what they protect?
- What it tests
- State against interaction checks, over-specified mocks, unused stubs that hide wrong results, fakes with contract tests, and migrating a test suite without lowering its strength.
- First step
- Separate the two symptoms. "Wanted but not invoked" means tests checked calls the code no longer needs to make. Tests that pass for the wrong reason are worse: a stub for a method the code stopped calling returns a default, so the code takes another path while the test stays green.
- Follow-ups
-
- Which of the 40 checks should stay as verify calls?
- How do you find the tests that now pass for the wrong reason?
- How do you stop a fake from drifting away from the real implementation?
From the lesson Test doubles, fakes and Mockito
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to staff
Your service calls a payment provider through the provider's Java SDK. A colleague's 30 tests mock the SDK's classes call by call. What problems do you expect, and what would you do instead?
- What it tests
- Why mocking types you do not own is fragile, wrapping a third-party API behind your own interface, fakes for that interface, and the few integration tests the real SDK still needs.
- First step
- Point out that the mocks encode your guess of how the SDK behaves, not how it does: an SDK upgrade, a changed exception or an extra required call breaks production while all 30 tests stay green, and every refactoring of your own code breaks the tests.
- Follow-ups
-
- What does the wrapper interface look like, and who owns it?
- How do you still test the code that calls the real SDK?
- When is mocking a third-party type acceptable?
From the lesson Test doubles, fakes and Mockito
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Fresher to mid
A booking service's tests pass on developers' laptops in India but fail on the build server, which runs in UTC, every evening from 18:30 to midnight UTC. What is the likely cause, and how do you fix the code and the tests?
- What it tests
- Time and time zones as hidden inputs, LocalDate.now() against an injected Clock, and testing date boundaries deterministically.
- First step
- Notice the offset: India is five and a half hours ahead of UTC, so from 18:30 UTC it is already the next day in India. The code or the tests read the date from the system clock and time zone, so the server and the laptops disagree about what today is for those hours.
- Follow-ups
-
- Why do the laptops never see the failure?
- Which clock and zone should production code use?
- How do you test the exact moment the date changes?
From the lesson Test doubles, fakes and Mockito
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Fresher to mid
In TDD you run each new test before writing the code and watch it fail. A colleague says this wastes time, because the test obviously fails when the code does not exist. Why does seeing it fail matter, and what does it catch?
- What it tests
- Understanding that a test must be shown able to fail, tests that pass by accident, and how the red step guards the test itself.
- First step
- Point out that the red run tests the test. A test that passes before the change proves nothing about the change: it may assert on the wrong value, never run, or check behaviour that already existed.
- Follow-ups
-
- Give two ways a new test can pass without the code being right.
- What do you do when a new test passes straight away?
- Does the red step matter when fixing a reported bug?
From the lesson TDD kata: amounts in Indian words
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
A team adopted TDD for an invoicing module. Six months later the suite takes eight minutes, most changes break dozens of tests, and people have started skipping the refactor step. What went wrong, and how would you practise TDD differently?
- What it tests
- Testing behaviour through public interfaces, test granularity, coupling of tests to structure, test speed, and why the refactor step depends on tests that survive refactoring.
- First step
- Look at what the tests are coupled to. If they test private helpers, internal classes or the exact calls between objects, every structural change breaks them, so refactoring becomes expensive and is skipped, which is the opposite of what TDD needs.
- Follow-ups
-
- How do you tell a test of behaviour from a test of structure?
- Where does the eight minutes usually go, and how do you get it down?
- How do you migrate the existing suite without stopping work?
From the lesson TDD kata: amounts in Indian words
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Fresher to mid
You are asked to write a method that turns cheque amounts into words. Before writing any code, list the test cases you would start with, the order you would add them in, and how you would know when the list is complete.
- What it tests
- Building a test list, ordering cases from simple to general, boundaries and equivalence classes, and oracles that cover what hand-picked examples cannot.
- First step
- Start with the simplest case, zero, then one example per rule, in the order each rule appears: units, the irregular teens, tens with and without units, hundreds, then each larger group. Add the boundaries of every rule, such as 19 and 20 or the largest amount allowed, and the inputs that must be refused.
- Follow-ups
-
- Which rows catch a mistake in a word that no example uses?
- How do you test rounding of paise?
- When is the list complete?
From the lesson TDD kata: amounts in Indian words
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Fresher to mid
A Maven build passes on your laptop but fails in CI on the same commit. The CI image was updated last week and nobody changed the POM. How do you find out what changed, and how do you stop it from happening again?
- What it tests
- Knowing that unpinned plugins follow the Maven version, reading versions from the log, and fixing them with pluginManagement, the Maven Wrapper and an enforcer rule.
- First step
- Compare the two logs: every goal prints its plugin and version, as in --- compiler:3.16.0:compile. If the versions differ, the POM leaves them to the defaults of each machine's Maven, and mvn --version on both machines will show two Maven versions.
- Follow-ups
-
- Which plugin versions does a POM get when it names none?
- How do you stop a new unpinned plugin from slipping in later?
- The wrapper is committed, yet CI still runs another Maven. Why?
From the lesson Maven: POM, lifecycle and dependencies
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Fresher to mid
A service is built with JDK 25 but runs on servers with Java 21. The POM sets maven.compiler.source and target to 21. It builds and starts, then a request fails with NoClassDefFoundError: java/lang/IO. Explain what happened and which setting prevents it.
- What it tests
- The difference between -source and -target and --release, what a class-file version does and does not check, and catching platform API use at compile time.
- First step
- Notice that the class loaded on Java 21, so its class-file version was fine: the failure is a missing class. -source and -target only set the language level and the class-file version; javac still compiled against JDK 25's library, where java.lang.IO exists.
- Follow-ups
-
- Why did the service start before failing?
- What does javac print when it compiles with -source but no --release?
- The service must run on Java 21 and 25. What do you build with?
From the lesson Maven: POM, lifecycle and dependencies
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
A command-line tool's tests all pass in CI, yet the tool fails on its first run in production with NoClassDefFoundError for a class of a JSON library. The POM declares that library with scope provided. Explain the failure, fix it, and say how you would catch the next one before release.
- What it tests
- Which class paths each scope joins, why tests stay green, and a release check that runs the packaged program on its runtime class path.
- First step
- Start from the definition: provided is on the compile and test class paths but not on the runtime class path, and something else, a container or the JDK, is expected to supply it. A plain command-line tool has no container, so nothing supplies the library.
- Follow-ups
-
- Why did every test pass?
- When is provided the right scope?
- What check in CI would have failed before the release?
From the lesson Maven: POM, lifecycle and dependencies
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 2 minutes
-
Fresher to mid
You add a Java 25 toolchain to a Gradle build. It works on every laptop, but CI fails with "Cannot find a Java installation on your machine matching languageVersion=25". The CI image has only Java 21. What do you do, and how do you keep the laptops and CI on the same JDK afterwards?
- What it tests
- The difference between the JDK that runs Gradle and the toolchain, how Gradle finds toolchains, and choosing between installing, naming and auto-provisioning a JDK.
- First step
- Read the error as Gradle means it: Gradle itself started on Java 21, but no JDK it knows of matches version 25. Run gradle -q javaToolchains on CI and on a laptop: the laptops have a Java 25 that auto-detection found, CI has none.
- Follow-ups
-
- Why does changing JAVA_HOME on CI not help?
- What does the foojay resolver plugin change?
- How do you make sure everyone uses the same Java 25 build?
From the lesson Gradle: Kotlin DSL, toolchains and tasks
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 2 minutes
-
Mid to senior
A custom task that generates source code from a schema runs on every Gradle build, even when nothing changed, and adds about 40 seconds to each build. How do you find out why Gradle never skips it, and how do you fix it?
- What it tests
- How up-to-date checks work, declaring inputs and outputs, reading Gradle's reasons with --info, and wiring a generated-sources task into compilation.
- First step
- Run the build twice with --info and read the reason Gradle prints for the task. If it says the task has not declared any outputs, or names an input that changes on every run, such as a timestamp, that is the cause.
- Follow-ups
-
- Which inputs and outputs should a code generator declare?
- The task declares both, and still reruns. What else changes on every run?
- How should compileJava depend on the generated sources?
From the lesson Gradle: Kotlin DSL, toolchains and tasks
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 2 minutes
-
Mid to senior
Your team converted a Maven project to Gradle with gradle init. Before CI switches over, how do you show that the Gradle build produces the same thing as the Maven build, and what differences do you expect to find?
- What it tests
- Comparing builds by evidence: tests, compiled classes, the JAR contents and the resolved dependencies, and knowing what conversion usually gets wrong.
- First step
- Decide what "the same" means before comparing: the same tests run and pass, the same class files for the same Java version, the same JAR contents and the same versions of every library on the runtime class path. Then compare each with a command, not by reading build files.
- Follow-ups
-
- What did gradle init get wrong on the exercise pack?
- Why can the two builds resolve different library versions from the same declarations?
- Which differences in the JAR are expected and harmless?
From the lesson Gradle: Kotlin DSL, toolchains and tasks
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 2 minutes
-
Mid to senior
After your team adds one in-house library, a service passes all its tests but throws NoSuchMethodError from inside a JSON library in production. Nobody changed the JSON library's version in the POM. How do you find the cause, fix it, and keep it from coming back?
- What it tests
- Reading the error as a class-path problem, Maven's nearest-wins rule, dependency:tree -Dverbose, BOMs and dependencyManagement, and rules that fail the build in CI.
- First step
- Read the error as a class-path problem, not a code bug: some class was compiled against one version of the JSON library and runs against another. Run mvn dependency:tree -Dverbose and look for that library's lines marked omitted for conflict: adding the new library changed which version Maven keeps.
- Follow-ups
-
- Why did no test fail?
- Which fix do you choose, and why?
- How do you stop the next conflict from reaching production?
From the lesson Dependency management and supply-chain safety
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Senior to staff
A CI token that could publish to your company's Maven repository leaked and was usable for two days. How do you find out whether any artifact your services build with was replaced, and what do you change so that a replaced artifact fails the build next time?
- What it tests
- Why checksums stored in the same repository prove nothing after a compromise, pinned hashes with trusted checksums or verification metadata, immutable releases and scoping an incident.
- First step
- Treat the repository's own checksum files as untrusted, because whoever could publish could rewrite them. Compare what the repository serves now with hashes recorded before the leak: hashes kept in version control, build caches from before the leak, or the build records of the teams that published those releases.
- Follow-ups
-
- Why does --strict-checksums not help here?
- What would you record, and where?
- What should the repository itself refuse to do?
From the lesson Dependency management and supply-chain safety
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
On a Friday evening the scanner reports 16 advisories in a payment service's dependencies, one rated critical in a library you never declared. How do you decide what to fix now and what can wait, and how do you change the version of a library you do not declare?
- What it tests
- Triage by severity, fixed versions and reachability, pinning transitive versions with dependencyManagement or constraints, and suppressions with reasons and expiry dates.
- First step
- Group the findings by library and version, not by advisory: 16 advisories often come from two or three old versions. For each, find the nearest fixed version in the same release line, then ask whether the service reaches the vulnerable code, starting with the critical one.
- Follow-ups
-
- How do you pin the version of a transitive dependency?
- The fixed version needs a new major release. Now what?
- When is a suppression acceptable?
From the lesson Dependency management and supply-chain safety
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
A 400,000-line Java service compiles with 3,200 warnings once you switch on -Xlint:all. You want -Werror in CI without stopping feature work for a month. How do you get there, and how do you stop new warnings meanwhile?
- What it tests
- Planning a gradual adoption of lint, counting warnings by category, a ratchet that blocks new warnings, narrow suppressions, and the phases in which javac reports warnings.
- First step
- Measure before deciding: count the warnings by category, because a few categories usually hold most of them. Then fix or switch off whole categories one at a time, and make CI fail when the count of any category grows, so that the number can only go down.
- Follow-ups
-
- Which categories would you fix first, and why?
- How does a ratchet work in CI?
- Why can the count rise after you fix warnings?
From the lesson Code quality: javac lint, javadoc, formatting
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
Users of your library keep copying examples from its Javadoc that no longer compile after API changes. How would you make every documentation example part of the build, so that a broken example fails it before a release?
- What it tests
- External @snippet files compiled and run as code, regions, doclint in CI, and keeping inline snippets small.
- First step
- Move every example longer than a few lines out of the comment into a source file under snippet-files, include it with @snippet and a region, and compile and run those files in the build like tests. An API change that breaks an example then breaks the build.
- Follow-ups
-
- What does javadoc check about an external snippet, and what not?
- How do the snippet files get compiled and run in the build?
- When is an inline snippet still fine?
From the lesson Code quality: javac lint, javadoc, formatting
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
In a team of 12, developers keep reformatting each other's code, and pull requests show hundreds of changed lines that change nothing. How do you introduce one formatter without blocking the open pull requests and without losing the history of who changed what?
- What it tests
- Choosing and pinning a formatter, a one-off reformatting commit, ignoring it in blame, check in CI and apply on laptops, and timing the switch.
- First step
- Pick one formatter and pin its version and the build plugin's, then reformat the whole code base in one commit that changes nothing else. Record that commit in a file that git blame is told to ignore, and only then make CI fail on unformatted files.
- Follow-ups
-
- What do you do with the open pull requests?
- How do you keep git blame useful after the reformatting?
- Why pin the formatter's version?
From the lesson Code quality: javac lint, javadoc, formatting
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 2 minutes
-
Mid to senior
A batch job processes 2 million records in 3 seconds, and one record comes out wrong. A colleague sets a conditional breakpoint inside the loop, and the job now seems to hang. Why, and how do you find the bad record quickly?
- What it tests
- How conditional breakpoints work through JDWP, the cost of a stop, and cheaper alternatives: a temporary if, exception breakpoints, logging and shrinking the input.
- First step
- Ask what has to happen, on every pass of the loop, for the debugger to test the condition, and multiply that by 2 million passes. Then look for a way to make the program stop only once, at the record that matters.
- Follow-ups
-
- How long would 2 million stops take, roughly?
- What is the fastest way to stop only at the bad record?
- The bad value only shows later as an exception. What then?
From the lesson Debugging techniques and logging
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 2 minutes
-
Mid to senior
Support asks you to switch on DEBUG logging for the payment service in production for a day, to investigate failed payments. What do you check before you do it, how do you do it safely, and what can go wrong?
- What it tests
- Secrets and personal data at low levels, log volume and cost, per-logger levels and handler levels, suppliers, and time-boxing a change in production.
- First step
- Before switching anything on, read what the DEBUG lines of that code write and how often they run: in production nobody has looked at them before. Then estimate the volume from today's INFO rate, because DEBUG is often ten or a hundred times more.
- Follow-ups
-
- How do you switch on DEBUG for one package only?
- The DEBUG lines still do not appear. Why?
- How do you make sure it is switched off again?
From the lesson Debugging techniques and logging
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 2 minutes
-
Mid to staff
The security team finds lines in a payment service's logs that look like real refund approvals, but the code never wrote them. How could that happen, how do you stop it, and how would you notice the next attempt?
- What it tests
- Log injection (CWE-117), neutralising line breaks and control characters, structured logs, and treating logs as evidence that must be trustworthy.
- First step
- Suspect untrusted input in a log message: a value such as a user name containing a line break starts a new line that a reader or a parser takes for a separate record. Search the log for records whose surrounding line comes from code that logs user-supplied values.
- Follow-ups
-
- Where else besides user names can such input come from?
- Is escaping enough, or do you need structured logs?
- How do you make logs trustworthy as evidence?
From the lesson Debugging techniques and logging
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes