{"record":{"id":"b61d9ae67874c74d","repo":"google/guava","slug":"should-not-call-merge-function-if-key-was-absent","errorCode":null,"errorMessage":"Should not call merge function if key was absent","messagePattern":"Should not call merge function if key was absent","errorType":"exception","errorClass":"AssertionFailedError","httpStatus":null,"severity":"error","filePath":"android/guava-testlib/src/com/google/common/collect/testing/testers/MapMergeTester.java","lineNumber":58,"sourceCode":" * A generic JUnit test which tests {@link Map#merge}. Can't be invoked directly; please see {@link\n * com.google.common.collect.testing.MapTestSuiteBuilder}.\n *\n * @author Louis Wasserman\n */\n@GwtCompatible\n@IgnoreJRERequirement // We opt into library desugaring for our tests.\npublic class MapMergeTester<K, V> extends AbstractMapTester<K, V> {\n  @MapFeature.Require(SUPPORTS_PUT)\n  public void testAbsent() {\n    assertEquals(\n        \"Map.merge(absent, value, function) should return value\",\n        v3(),\n        getMap()\n            .merge(\n                k3(),\n                v3(),\n                (oldV, newV) -> {\n                  throw new AssertionFailedError(\n                      \"Should not call merge function if key was absent\");\n                }));\n    expectAdded(e3());\n  }\n\n  @MapFeature.Require({SUPPORTS_PUT, ALLOWS_NULL_VALUES})\n  @CollectionSize.Require(absent = ZERO)\n  public void testMappedToNull() {\n    initMapWithNullValue();\n    assertEquals(\n        \"Map.merge(keyMappedToNull, value, function) should return value\",\n        v3(),\n        getMap()\n            .merge(\n                getKeyForNullValue(),\n                v3(),\n                (oldV, newV) -> {\n                  throw new AssertionFailedError(","sourceCodeStart":40,"sourceCodeEnd":76,"githubUrl":"https://github.com/google/guava/blob/94f39958baf7ad51ddf9c70e406ed6b188194daa/android/guava-testlib/src/com/google/common/collect/testing/testers/MapMergeTester.java#L40-L76","documentation":"Thrown inside the remapping function of MapMergeTester.testAbsent as an AssertionFailedError. By the Map.merge contract, when the key is absent (or mapped to null) the remapping function MUST NOT be invoked. This assertion failing means the Map implementation under test incorrectly calls the function for an absent key.","triggerScenarios":"Running the guava-testlib MapTestSuiteBuilder against your custom Map. Your Map.merge implementation invokes the BiFunction even when the key is absent, instead of simply inserting the value. The tester's @MapFeature.Require(SUPPORTS_PUT) testAbsent case trips this.","commonSituations":"Custom Map implementations that unconditionally compute newValue = remappingFunction.apply(old, new) before checking whether the key exists; copy-pasting an AbstractMap merge that does not short-circuit on absent keys.","solutions":["In your Map.merge, check containsKey(k) (or get(k) == null && !containsKey) first; only call the function when a non-null existing value is present.","Match the JDK HashMap.merge algorithm: if no mapping or mapping is null -> put value directly; else compute merged value.","Re-run the suite; testAbsent should pass without invoking the function."],"exampleFix":"// before\n@Override public V merge(K k, V v, BiFunction<? super V,? super V,? extends V> f) {\n  V old = get(k);\n  V merged = f.apply(old, v); // BUG: called even when old is absent\n  put(k, merged);\n  return merged;\n}\n\n// after\n@Override public V merge(K k, V v, BiFunction<? super V,? super V,? extends V> f) {\n  V old = get(k);\n  if (old == null) { put(k, v); return v; } // absent: do not call f\n  V merged = f.apply(old, v);\n  if (merged == null) remove(k); else put(k, merged);\n  return merged;\n}","handlingStrategy":"validation","validationCode":"// Direct unit check before running the full suite.\nMap<K,V> m = newMap();\nm.merge(absentKey, v, (o,n) -> { throw new AssertionFailedError(\"f must not be called\"); });\nassertEquals(v, m.get(absentKey));","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Implement merge() by checking absence/null-value first and only invoking the function for a present non-null value.","Mirror HashMap.merge semantics in custom Map implementations."],"tags":["guava-testlib","map-contract","merge","assertion"],"backgroundTag":null,"analyzedSha":"94f39958baf7ad51ddf9c70e406ed6b188194daa","analyzedAt":"2026-08-13T22:50:30.265Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}