Appendix B Companion: Code, Explanations and Solutions
Tools and Reproducible Setup
OO Development and Agile Practices
1. Scope and use
This file accompanies Appendix B, Tools and Reproducible Setup. It supplies a complete small starter project, a scripted Git walk-through, explanations of each file and the answers to the activity in B.12.
The starter is an isolated Book example, not a lending-project version. Its only purpose is to show that the toolchain works: a Java 17 release target, Maven with pinned JUnit and Mockito, two Bash scripts and a hosted-check file. It does not change the project and its tests do not belong in the project’s test totals.
Save every listing in Section 2 at its path inside one new folder, for example toolchain-check. Use a Java 17 or later JDK, Git Bash or a Linux or macOS terminal, and Maven 3.9.11. From that folder:
The first Maven build needs access to Maven Central. If your machine has no network access, build once somewhere that has it and copy the populated local Maven repository folder across, then run with the two variables below. CHAPTER_M2 names that folder and CHAPTER_OFFLINE=1 makes Maven use only what it holds:
If mvn is not on the path, give the full path to the Maven launcher in CHAPTER_MVN (on Windows it ends in mvn.cmd).
2. Complete starter files
2.1 pom.xml
2.2 src/main/java/edu/se506/setup/ToolchainReport.java
2.3 src/test/java/edu/se506/setup/ToolchainReportTest.java
2.4 src/test/java/edu/se506/setup/GreeterMockTest.java
2.5 src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker
2.6 scripts/check-setup.sh
2.7 scripts/check.sh
2.8 .gitignore
2.9 .gitattributes
2.10 .github/workflows/verify.yml
2.11 README.md
3. Worked explanation
3.1 The POM
maven.compiler.release set to 17 makes the Compiler plugin pass --release 17 to javac, so the build rejects any API newer than Java 17 even on a newer JDK (B.1). project.build.sourceEncoding fixes the source encoding so that the build does not depend on the machine’s default.
Both dependencies have the scope test: they are on the class path for the tests and absent from the packaged application. junit-jupiter is an aggregate artifact that brings the API, the parameterised-test support and the engine. Every plugin that the book relies on has an exact version, so a build today and a build next year resolve the same plugins. The Surefire setting failIfNoTests makes a build with no tests fail. Without it, a mistyped test location can produce an empty run that looks green, the same reason Chapter 13’s pipeline fails a stage that selects no tests.
3.2 The two scripts
check-setup.sh deliberately does not use set -e. It has to examine every tool and report all the problems in one run, so it records a status and exits with it at the end. The helper need runs a tool’s version command and prints the first line of its output; the extra test [ -f "$1" ] accepts a tool named by a full file path, which command -v does not always accept for a Windows .cmd launcher. The compiler gets its own check because a machine can have a Java runtime and no compiler, and because a compiler older than 17 cannot honour --release 17.
check.sh is the opposite: it is a gate. set -euo pipefail stops it at the first failing command and makes unset variables an error, so the success message at the end cannot appear unless Maven succeeded. The script changes into the project folder first, so it works from any directory. It reads three optional variables and builds the Maven argument list from them, which keeps the same file usable on a laptop, on a machine with no network and in a hosted check.
3.3 The tests
ToolchainReportTest has four methods and six invocations because one method is parameterised with three values. The first method checks that the Java running the test can run code built for the target. The summary test pins the exact wording that reports the target. The parameterised test checks the boundary at 17 from above, and the last method checks it from below with 16, the same two-sided reading you practised on the borrowing limit in Appendix A. GreeterMockTest creates a mock of an interface, tells it what to return and verifies the call. It checks that Mockito is configured, not any domain behaviour; do not copy that pattern into a design test.
3.4 Repository files
.gitignore keeps generated output out of the repository. .gitattributes keeps shell scripts in LF form on Windows. The workflow runs scripts/check.sh after installing Java 17, so the hosted run and the local run are the same command. The workflow’s action versions are those named in B.1; check the current versions before you reuse it, because tags and runner images change. It has no effect until it runs on a repository, and it can be started by hand through workflow_dispatch.
4. Expected output
The setup check on a machine where everything is in place (the version text will differ):
The build, reduced to its important lines:
The Java N figure is the runtime you used. Maven and Mockito may print the warnings described in B.4 and B.5 between these lines.
5. A small fault experiment
In a separate copy of the starter, make one change at a time, run the named command and then discard the copy. Each experiment checks that a particular gate notices a particular fault.
| Change | Command | What must happen |
|---|---|---|
Name a Maven command that does not exist: CHAPTER_MVN=/nonexistent/mvn | bash scripts/check-setup.sh | A MISSING maven line, SETUP INCOMPLETE and exit status 1. |
Put a fake javac that prints javac 1.8.0_999 first on the path | bash scripts/check-setup.sh | A TOO-OLD javac line and exit status 1. |
In ToolchainReport.canRunOn, change >= to > | bash scripts/check.sh | One failing invocation (the parameterised case for 17), BUILD FAILURE, a non-zero status, and no BUILD AND TESTS PASSED line. |
| The same change | mvn --batch-mode -DskipTests verify | Tests are skipped. and BUILD SUCCESS: the fault is not detected. |
| The same change | mvn --batch-mode -Dmaven.test.failure.ignore=true verify | The failure is printed, yet BUILD SUCCESS and exit status 0: the fault is hidden. |
The last two rows show why a green build proves nothing unless you know what was run. They also correct two common habits: skipping tests is not a way to confirm that a project works, and telling Maven to ignore failures does not make failures go away. These experiments check that the gates catch the chosen faults. They do not establish that the starter is correct for every input or on every platform.
6. The Git walk-through
git-walkthrough.sh copies the starter into a work folder and then performs every command of B.8 in order: create the repository and a first commit, branch, inspect, stage, commit, discard an edit, unstage a file, revert a commit, merge, share through a bare repository and clone it again. The bare repository is a folder on the same machine, so no account and no network are involved. The author identity is set for the new repository only, and the script touches no global Git setting.
Run it from any folder:
The commit hashes, dates and folder paths in the output will differ. The important lines are these:
Read step 3 carefully: after staging the file, git diff --stat prints nothing and git diff --staged --stat prints the change. An empty git diff after staging does not mean there is nothing to commit. In step 4, a file that has been staged shows A in the first column, and after git restore --staged it shows ?? again because it is untracked. Step 5 adds a third commit; the history now shows the original change and the commit that reverses it, not a rewritten past. Step 6 is a fast-forward because main had no commits of its own; on a branch that has diverged, the same command creates a merge commit.
On Windows you will also see lines such as warning: in the working copy of '...', LF will be replaced by CRLF the next time Git touches it. They are warnings from line-ending conversion (B.8), not failures. The script is not a model of a pull request: the review, the comments and the merge button belong to the hosting platform.
7. Answers to the activity in B.12
- Setup check.
SETUP OK. If a line startsMISSINGorTOO-OLD, the named tool is the problem. - First build. Seven invocations from five test methods: one in
GreeterMockTestand six inToolchainReportTest, because the parameterised method contributes three. A good setup note says “seven invocations”, not “seven tests”. - Fault. With
>the case 17 fails:expected: <true> but was: <false>. The script prints no success line and exits non-zero. Restoring>=makes it pass again. - Branch and commit.
git diffshows the edit; aftergit add,git diffis empty andgit diff --stagedshows it;git commitrecords it. - Setup note. There is no single right answer. A useful note names the versions your machine reported (compare them with B.1), lists the commands run and records every difference from this appendix.
The constructed AI candidate
The candidate in B.12 is constructed for teaching; it is not a captured model response.
| Candidate sentence | Verdict | Evidence and correction |
|---|---|---|
| “Any Java installation will do because the code is plain Java.” | Incorrect | The project needs a full JDK of version 17 or later: check-setup.sh reports a missing compiler and a compiler older than 17. A runtime alone cannot compile, and --release 17 needs a compiler that supports it. |
“To confirm a project works, run mvn install -DskipTests.” | Incorrect | -DskipTests prints Tests are skipped. and the build succeeds even when a test would fail (Section 5). It confirms that the code compiles and packages, not that it works. |
“Add -Dmaven.test.failure.ignore=true so the build always passes.” | Incorrect | The failing test is still printed, but the build ends in BUILD SUCCESS with status 0 (Section 5). Hiding a failure is the opposite of checking. |
“Share your work with git add . and git push --force.” | Unsafe | git add . stages everything, including files you did not mean to share; read git status and name the files. A forced push can overwrite commits other people have pulled. Push a branch normally and review it. |
An adequate record names the claim, the check and the conclusion. “The assistant was wrong” is weaker than naming the experiment that shows it.
8. Teaching and revision notes
Ask learners to predict each fault-experiment result before running it. The -DskipTests and failure-ignore rows are worth running in class: they take a minute and change how students read a green build. Learners on machines they do not control may be unable to install tools; the offline route is to run check-setup.sh on a lab machine, read the output and do B.12 steps 3 and 4 on paper.
Keep setup detail here and in Appendix B, not in the design chapters. When a pinned version changes, edit the table in B.1, the POM listings and the version lines in this file’s README, and leave the design explanations alone. Do not add this starter’s test count to the lending project’s totals.
References
- Appendix B lists the official Maven, Git, JUnit, Mockito, PMD and GitHub Actions references that support the commands in this file.