Skip to content

Commit f868366

Browse files
. d Add ARCHITECTURE.md and point AGENTS.md to it
Amp-Thread-ID: https://ampcode.com/threads/T-019c6cb3-61fb-70a9-9956-9bf4160d8636 Co-authored-by: Amp <amp@ampcode.com>
1 parent c9734b7 commit f868366

2 files changed

Lines changed: 128 additions & 1 deletion

File tree

AGENTS.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
- Tests run with `en_US` locale (surefire config). Java target is 1.8.
77

88
## Architecture
9-
Multi-module Maven project: **approvaltests** (core library), **approvaltests-util** (shared utilities), **approvaltests-tests** and **approvaltests-util-tests** (tests). Core API: `org.approvaltests.Approvals` with `verify()`, `verifyAll()`, `verifyAsJson()`. Pattern: Writers (generate output) → Namers (file naming) → Reporters (show diffs on failure).
9+
See **ARCHITECTURE.md** for the full codemap (module structure, key types, and the verify flow). In short: Writers generate output → Namers determine file pathsApprovers compare received vs. approved → Reporters show diffs on failure. Entry point: `Approvals.verify()`.
1010

1111
## Key Rules
1212
- **NEVER** approve a test (update `.approved.*` files) automatically — always prompt the user. Commit `.approved.*` files; never commit `.received.*` files.

ARCHITECTURE.md

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
# Architecture
2+
3+
This document describes the high-level architecture of ApprovalTests.Java.
4+
If you want to familiarize yourself with the codebase, this is a good place to start.
5+
6+
## Bird's Eye View
7+
8+
ApprovalTests is a library for unit testing that replaces hand-written assertions with a "golden master" approach:
9+
test output is saved to a file, and future test runs are compared against that saved snapshot.
10+
When a test produces new output, a **Writer** serializes it to a `.received.` file.
11+
A **Namer** determines the file paths for both `.received.` and `.approved.` files (derived from the test class and method names).
12+
An **Approver** compares the two files, and if they differ, a **Reporter** shows the difference (typically by launching a diff tool).
13+
14+
The user's main entry point is `Approvals.verify()`.
15+
16+
## Module Structure
17+
18+
The project is a multi-module Maven build. The two library modules that ship to users are `approvaltests-util` and `approvaltests`.
19+
The two `-tests` modules and `counter_display` are never published.
20+
21+
```
22+
approvaltests-util Shared utilities (no dependency on approvaltests)
23+
approvaltests Core approval testing library (depends on approvaltests-util)
24+
approvaltests-util-tests Tests for approvaltests-util
25+
approvaltests-tests Tests for approvaltests
26+
counter_display Internal developer tool
27+
```
28+
29+
## `approvaltests-util`
30+
31+
This module contains general-purpose utilities under two root packages:
32+
33+
- **`com.spun.util`** — Helper classes for strings, arrays, JSON, IO, reflection, and more. These are used heavily throughout `approvaltests` but have no dependency on it. Key types: `ObjectUtils`, `StringUtils`, `ArrayUtils`, `JsonUtils`, `FileUtils` (in `io/`).
34+
35+
- **`org.lambda`** — A lightweight functional programming library providing `Function0``FunctionN`, `Action0``ActionN`, and `Query` (a collection-querying API used in place of Java Streams throughout the codebase).
36+
37+
- **`org.packagesettings`** — A small framework for hierarchical configuration via `PackageSettings.java` files placed in Java packages. `PackageLevelSettings` walks the package hierarchy to resolve settings.
38+
39+
## `approvaltests`
40+
41+
This is the core library. Everything lives under `org.approvaltests`. The main concepts map to subpackages:
42+
43+
### `core/`
44+
45+
Defines the key interfaces and types that the rest of the system plugs into:
46+
47+
- **`ApprovalWriter`** — interface for writing test output to a `.received.` file.
48+
- **`ApprovalFailureReporter`** — interface invoked when a test fails (received ≠ approved).
49+
- **`Scrubber`** — interface for transforming output before comparison (e.g., replacing dates or GUIDs with deterministic placeholders).
50+
- **`Options`** — the main configuration object, carrying a scrubber, reporter, file extension, namer, writer, and comparator. Immutable-style builder via `with*()` methods.
51+
- **`Verifiable`** — interface for objects that know how to verify themselves.
52+
53+
### `writers/`
54+
55+
Implementations of `ApprovalWriter`. `ApprovalTextWriter` handles the common text case; others handle binary files, images, XML, AWT components, and directory-to-directory comparisons. `ApprovalWriterFactory` / `DefaultApprovalWriterFactory` control which writer `Options` uses by default.
56+
57+
### `namer/`
58+
59+
Implementations of `ApprovalNamer`. `StackTraceNamer` is the default — it inspects the call stack to derive `ClassName.methodName` and the source directory. `NamerFactory` and `NamerFactoryForOptions` provide labelling (e.g., parameterized test names). `ApprovalResults` offers static helpers for customizing the namer from within a test.
60+
61+
### `approvers/`
62+
63+
`ApprovalApprover` is the interface for the approve/report lifecycle (`approve()`, `reportFailure()`, `cleanUpAfterSuccess()`). `FileApprover` is the standard implementation — it orchestrates the core verify loop: write the received file, compare it with the approved file, and report on failure. `ApprovalTracker` detects duplicate `verify()` calls in a single test.
64+
65+
### `reporters/`
66+
67+
A large family of `ApprovalFailureReporter` implementations. Each one knows how to launch a specific diff tool (Beyond Compare, IntelliJ, VS Code, etc.) or perform a specific action (clipboard, JUnit assert, auto-approve).
68+
69+
- `DiffReporter` — the default, tries a platform-appropriate list of diff tools.
70+
- `FirstWorkingReporter` — tries reporters in order until one succeeds.
71+
- `UseReporter` — annotation for selecting a reporter at the test/class/package level.
72+
- `ReporterFactory` resolves the active reporter by checking `@UseReporter` annotations, then `PackageSettings`, then falling back to `DiffReporter`.
73+
- Platform subdirectories (`windows/`, `macosx/`, `linux/`, `intellij/`) contain OS-specific diff tool integrations.
74+
75+
### `scrubbers/`
76+
77+
Implementations of `Scrubber`: `DateScrubber`, `GuidScrubber`, `RegExScrubber`, `NormalizeSpacesScrubber`, and `Scrubbers` (a static utility combining them).
78+
79+
### `combinations/`
80+
81+
`CombinationApprovals` — verifies a function against all combinations of input parameters. The `pairwise/` subpackage provides pairwise combination reduction.
82+
83+
### `inline/`
84+
85+
An alternative approval mode where the expected value lives inside the test source code (as a string literal) rather than in a separate `.approved.` file. Key types: `InlineJavaReporter`, `InlineComparator`, `InlineOptions`.
86+
87+
### `awt/`
88+
89+
`AwtApprovals` — approval testing for graphical AWT/Swing components and animated GIF sequences.
90+
91+
### `legacycode/`
92+
93+
`LegacyApprovals` — utilities for approval-testing hard-to-test legacy code via lock-down tests over input permutations.
94+
95+
### `testcommitrevert/`
96+
97+
`TestCommitRevertRunner` — a test runner for the TCR (Test && Commit || Revert) workflow.
98+
99+
### `integrations/junit5/`
100+
101+
JUnit 5 extensions for approval tests.
102+
103+
### Top-Level Classes
104+
105+
- **`Approvals`** — the primary public API. `verify()`, `verifyAll()`, `verifyAsJson()`, and friends. All overloads ultimately funnel through `verify(ApprovalWriter, Options)`.
106+
- **`JsonApprovals`**, **`JsonJacksonApprovals`**, **`JsonJackson3Approvals`**, **`JsonXstreamApprovals`** — JSON-specific verify methods using different serialization libraries.
107+
- **`StoryBoard`** / **`MarkdownStoryBoard`** / **`StoryBoardApprovals`** — for verifying sequences of state changes (e.g., Game of Life frames).
108+
- **`ReporterFactory`** — resolves which reporter to use for a given test, walking annotations and package settings.
109+
- **`ApprovalSettings`** / **`ApprovalUtilities`** — global settings and helpers.
110+
111+
## The Verify Flow
112+
113+
A call to `Approvals.verify("hello")` proceeds roughly as follows:
114+
115+
1. `Options` creates an `ApprovalTextWriter` (via its `ApprovalWriterFactory`).
116+
2. `Approvals` obtains an `ApprovalNamer` (by default a `StackTraceNamer` that reads the call stack).
117+
3. `Options` scrubs the output through its `Scrubber` (default is `NoOpScrubber`).
118+
4. A `FileApprover` is constructed with the writer, namer, and comparator.
119+
5. `FileApprover.approve()` writes the `.received.` file and compares it with the `.approved.` file.
120+
6. If they differ, `ReporterFactory` resolves the active `ApprovalFailureReporter` and calls `report()`.
121+
7. The reporter (e.g., `DiffReporter`) launches a diff tool or throws an assertion error.
122+
123+
## Test Layout
124+
125+
Tests mirror the main source structure. Approved files (`.approved.txt`, `.approved.json`, etc.) live alongside the test source files and are checked into version control. Received files (`.received.*`) are generated at test time and should not be committed.
126+
127+
Test files are in `approvaltests-tests/src/test/java/org/approvaltests/` and `approvaltests-util-tests/src/test/java/`.

0 commit comments

Comments
 (0)