Testing and Benchmarking¶
Testing searches for defects and documents examples of expected behavior. Correctness proofs establish claims over a model. Neither replaces the other.
Test dimensions¶
- representative examples;
- boundary cases: empty, singleton, duplicates, extremes;
- invalid inputs and exception contracts;
- properties that hold across generated inputs;
- regression cases for previously discovered defects;
- integration behavior at real boundaries.
For sorting, useful properties include nondecreasing output, preservation of length and multiset, idempotence, and agreement with a trusted oracle.
@ParameterizedTest
@MethodSource("arrays")
void insertionSortMatchesTheJdk(int[] input) {
int[] expected = input.clone();
Arrays.sort(expected);
int[] actual = input.clone();
insertionSort(actual);
assertArrayEquals(expected, actual);
}
Unit, integration, and system scope¶
A unit test isolates a small contract and runs quickly. An integration test checks collaboration with a real boundary such as a database or HTTP server. A system test observes the deployed application externally. Test doubles are useful at selected boundaries but cannot establish that a real integration works. See test doubles, contract tests, and property-based testing for their distinct guarantees and failure modes.
Benchmarking Java¶
Use JMH or an equivalent harness. A sound report states inputs, warm-up, measurement iterations, forks, JVM, hardware, statistical result, and uncertainty. Prevent dead-code elimination and avoid combining setup work with the operation under measurement.
System.nanoTime() is a clock primitive, not a benchmarking methodology. JIT
compilation, garbage collection, adaptive optimization, CPU caches, and operating
system noise all distort naive one-shot measurements.
For investigations outside a controlled microbenchmark, follow the debugging, profiling, and memory-analysis workflow.