Java edition
Optional
Operators that change how an Optional is made, checked and unwrapped. Together they ask whether
your tests have both a value and no value wherever the code tells them apart.
| Operator | Changes | Set |
|---|---|---|
optional/swap-present | isPresent and isEmpty | extended |
optional/empty | Optional.of(x) to Optional.empty() | extended |
optional/remove-filter | Optional.filter, removed | extended |
optional/always-default | opt.orElse(x) to x | extended |
optional/no-default | opt.orElse(x) to opt.get() | extended |
Each example is a class in samples/operator-docs with a test that misses its mutant, and each card
is what mutastic run samples/operator-docs --operators=all prints for it. Run these with
--operators=defaults,extended.
Optional presence swap
Section titled “Optional presence swap”optional/swap-present, in the extended set. isPresent and isEmpty swap places, on Optional
and its primitive forms.
The bug it stands for: a check the wrong way round, so that the code handles a missing value as if it were there. It costs on the path a test rarely tries: the customer who isn’t found, the setting that isn’t set.
/// What a profile shows: the nickname if there is one, and whether it's a nickname. public record Badge(String shown, boolean nickname) {}
public static Badge badge(String name, Optional<String> nickname) { return new Badge(nickname.orElse(name), nickname.isPresent()); }The test checks the name each badge shows, with a nickname and without, but never whether it says it’s a nickname:
@Test void theNicknameIsShown() { assertEquals("Annie", Badges.badge("Ann", Optional.of("Annie")).shown()); assertEquals("Ann", Badges.badge("Ann", Optional.empty()).shown()); } ✗ MISSED no test tells isPresent from isEmpty in badge() ┌─ src/main/java/com/example/optional/Badges.java:10 │ 9 │ public static Badge badge(String name, Optional<String> nickname) { 10 │ return new Badge(nickname.orElse(name), nickname.isPresent()); │ ^^^^^^^^^ isPresent changed to isEmpty 11 │ } │ = mutant - return new Badge(nickname.orElse(name), nickname.isPresent()); + return new Badge(nickname.orElse(name), nickname.isEmpty()); = note: covered by BadgesTest#theNicknameIsShown (its 1 covering test still passes) = help: add a test with an empty Optional and one with a value, and assert the result of each
done when mutastic verify m82cb842a samples/operator-docs passes reproduce mutastic run --only=m82cb842a samples/operator-docs or ignore mutastic ignore m82cb842a --reason="<why>" samples/operator-docsThe test that catches it: one with a value and one without, asserting what each does.
@Test void aNicknameIsMarkedAsOne() { assertTrue(Badges.badge("Ann", Optional.of("Annie")).nickname()); assertFalse(Badges.badge("Ann", Optional.empty()).nickname()); }Edition notes: isEmpty came in Java 11: in a class file for an older release the mutant is
skipped as unviable:, as the method may not be there, and a missing method would look like a
catch. The Rust edition has this operator too, optional/swap-present, in stronger, for is_some
and is_none, and is_ok and is_err, since Option and Result are everyday Rust rather than a
modern API.
Optional made empty
Section titled “Optional made empty”optional/empty, in the extended set. An Optional made with a value is empty instead:
Optional.of(x) and ofNullable(x) become Optional.empty(), and x is still worked out.
The bug it stands for: code that gives back nothing where it should give a value, which callers treat as “not found” without complaint. It costs in lookups and settings: an empty result looks just like a real absence, so a test that only checks the call doesn’t fail still passes.
/// The chosen page size, or the standard one when none is chosen. public int size(Integer chosen) { Optional<Integer> custom = Optional.ofNullable(chosen); return custom.orElse(standard); }The test’s chosen size is the standard one, so it comes out the same whether or not the choice is
kept. A choice of null gives the same answer as well, so the card holds both:
@Test void aChosenSize() { assertEquals(20, new Pages(20).size(20)); } ✗ MISSED no test depends on the value of chosen in size() · m528413d3 ┌─ src/main/java/com/example/optional/Pages.java:14 │ 13 │ public int size(Integer chosen) { 14 │ Optional<Integer> custom = Optional.ofNullable(chosen); │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ parameter is null 15 │ return custom.orElse(standard); │ = change chosen is null from the start of the method (not shown in the source) = also ✗ no test checks the value in the Optional made by ofNullable(…) in size() · m02d5e708 + Optional<Integer> custom = Optional.empty(); = note: covered by PagesTest#aChosenSize, PagesTest#theStandardSize (all 2 covering tests still pass) = help: add a test whose result depends on chosen, and assert it
// in PagesTest @Test void sizeWithChosen0ForStandard20() { var pages = new Pages(20); // as PagesTest#aChosenSize builds it assertEquals(0, pages.size(0)); // today 0 · with m528413d3 20 } check today's value is the one you intend: the test pins what the code does now
done when mutastic verify m528413d3,m02d5e708 samples/operator-docs passes reproduce mutastic run --only=m528413d3,m02d5e708 samples/operator-docs or ignore mutastic ignore <id> --reason="<why>" samples/operator-docs for each of m528413d3, m02d5e708The test that catches it: one that gets the value out, with a value unlike the default.
@Test void aChosenSizeIsKept() { assertEquals(50, new Pages(20).size(50)); }Edition notes: the Rust edition has this operator too, optional/empty, in stronger:
Some(x) becomes None, where it isn’t returned.
Optional filter removal
Section titled “Optional filter removal”optional/remove-filter, in the extended set. Optional.filter is removed, so the value goes on
whatever the predicate says.
The bug it stands for: a check that’s missing, or wrong, in a chain: the value is used when it shouldn’t be. It costs in rules that hide in one call: only an active customer, only a discount that’s still valid, only a nickname the rules allow.
/// The nickname asked for, if the rules allow it. public Optional<String> claim(Optional<String> asked) { return asked.filter(allowed); }The test only asks for a nickname the rules allow:
@Test void anAllowedNickname() { var nicknames = new Nicknames(nickname -> !nickname.equals("admin")); assertEquals(Optional.of("annie"), nicknames.claim(Optional.of("annie"))); } ✗ MISSED no test has a value .filter(…) must turn away in claim() ┌─ src/main/java/com/example/optional/Nicknames.java:15 │ 14 │ public Optional<String> claim(Optional<String> asked) { 15 │ return asked.filter(allowed); │ ^^^^^^ filter removed 16 │ } │ = mutant - return asked.filter(allowed); + return asked; = note: covered by NicknamesTest#anAllowedNickname (its 1 covering test still passes) = help: add a test with a value the filter rejects, and assert the Optional comes out empty
done when mutastic verify m34668ba0 samples/operator-docs passes reproduce mutastic run --only=m34668ba0 samples/operator-docs or ignore mutastic ignore m34668ba0 --reason="<why>" samples/operator-docsThe test that catches it: one with a value the filter must turn away.
@Test void aNicknameTheRulesForbidIsRefused() { var nicknames = new Nicknames(nickname -> !nickname.equals("admin")); assertEquals(Optional.empty(), nicknames.claim(Optional.of("admin"))); }Edition notes: the Rust edition has this operator too, optional/remove-filter, in extended,
for opt.filter(p) where opt isn’t visibly an iterator.
Optional default
Section titled “Optional default”optional/always-default and optional/no-default, in the extended set. Each takes away one side
of orElse. opt.orElse(x) becomes x, so the value in the Optional is never used.
opt.orElse(x) and opt.orElseGet(s) become opt.get(), so an empty Optional throws.
The bug it stands for: a default that’s always used, so the configured value is ignored; or no default at all, so a missing value throws. It costs in configuration: a setting that does nothing until someone’s production value matters, or a start-up failure when it’s left out.
public int port(Optional<Integer> configured) { return configured.orElse(defaultPort); }The test configures the default port, so whether the configured value is used never shows:
@Test void theConfiguredPort() { assertEquals(8080, new Server(8080).port(Optional.of(8080))); } ✗ MISSED no test needs the value in the Optional before .orElse(…) in port() ┌─ src/main/java/com/example/optional/Server.java:13 │ 12 │ public int port(Optional<Integer> configured) { 13 │ return configured.orElse(defaultPort); │ ^^^^^^ now always the default 14 │ } │ = mutant - return configured.orElse(defaultPort); + return defaultPort; = note: covered by ServerTest#theConfiguredPort, ServerTest#theDefaultPort (all 2 covering tests still pass) = help: add a test where the Optional has a value other than the default, and assert it comes back
done when mutastic verify md962bd31 samples/operator-docs passes reproduce mutastic run --only=md962bd31 samples/operator-docs or ignore mutastic ignore md962bd31 --reason="<why>" samples/operator-docsThe other side: a test that always has a value never needs the default.
/// The largest of `values`, or `ifNone` when there are none. public static int largest(List<Integer> values, int ifNone) { return values.stream().max(Integer::compare).orElse(ifNone); } @Test void theLargest() { assertEquals(9, Numbers.largest(List.of(3, 9), 0)); } ✗ MISSED no test has an empty Optional reach .orElse(…) in largest() ┌─ src/main/java/com/example/optional/Numbers.java:8 │ 7 │ public static int largest(List<Integer> values, int ifNone) { 8 │ return values.stream().max(Integer::compare).orElse(ifNone); │ ^^^^^^ throws when empty 9 │ } │ = mutant - return values.stream().max(Integer::compare).orElse(ifNone); + return values.stream().max(Integer::compare).get(); = note: inputs seen: ifNone = 0 covered by NumbersTest#theLargest (its 1 covering test still passes) = help: add a test where the Optional is empty, and assert the default comes back
done when mutastic verify m232b5b62 samples/operator-docs passes reproduce mutastic run --only=m232b5b62 samples/operator-docs or ignore mutastic ignore m232b5b62 --reason="<why>" samples/operator-docsThe test that catches it: two tests, one for each side: a value that isn’t the default, and an
empty Optional.
@Test void aConfiguredPortIsUsed() { assertEquals(9090, new Server(8080).port(Optional.of(9090))); } @Test void noValuesGiveTheFallback() { assertEquals(0, Numbers.largest(List.of(), 0)); }Edition notes: removing opt.ifPresent(…) is
remove void call’s change, as it returns nothing.
orElseThrow’s supplier is left alone: which exception is thrown is rarely worth a test. The Rust
edition has both operators too, optional/always-default and optional/no-default, in stronger,
for unwrap_or, unwrap_or_else, unwrap_or_default and map_or.