Back to Repositories

Validating JsonObject Map Interface Compliance in Google Gson

This test suite implements comprehensive Map interface validation for JsonObject’s asMap() functionality in the Gson library. It utilizes Google’s collection testing framework to verify map operations and behavior compliance.

Test Coverage Overview

The test suite provides extensive coverage of Map interface compliance for JsonObject.asMap().

Key functionality tested includes:
  • Null value handling and queries
  • Key restrictions (String only)
  • Value restrictions (JsonElement only)
  • Insertion order preservation
  • Map modification operations (put, remove)

Implementation Analysis

The implementation uses MapTestSuiteBuilder from Google’s collection testing framework to generate comprehensive test cases.

Key patterns include:
  • Custom TestMapGenerator implementation
  • Sample element generation with various JsonElement types
  • Explicit feature declaration through MapFeature and CollectionFeature enums

Technical Details

Testing tools and configuration:
  • JUnit AllTests runner
  • Google Common Collections Testing framework
  • MapTestSuiteBuilder for test case generation
  • Custom sample elements including JsonNull, JsonPrimitive, JsonArray, and JsonObject types

Best Practices Demonstrated

The test suite exemplifies several testing best practices:

  • Comprehensive feature coverage through explicit feature declarations
  • Clear separation of test data generation and test execution
  • Proper handling of type restrictions and null cases
  • Complementary testing approach alongside standard unit tests

google/gson

gson/src/test/java/com/google/gson/JsonObjectAsMapSuiteTest.java

            
package com.google.gson;

import com.google.common.collect.testing.MapTestSuiteBuilder;
import com.google.common.collect.testing.SampleElements;
import com.google.common.collect.testing.TestMapGenerator;
import com.google.common.collect.testing.features.CollectionFeature;
import com.google.common.collect.testing.features.CollectionSize;
import com.google.common.collect.testing.features.MapFeature;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import junit.framework.Test;
import org.junit.runner.RunWith;
import org.junit.runners.AllTests;

/**
 * Dynamic {@link MapTestSuiteBuilder Map test suite} for {@link JsonObject#asMap()}. This
 * complements {@link JsonObjectAsMapTest}, which can cover some cases which are not covered here,
 * e.g. making sure changes in the {@code Map} view are visible in the {@code JsonObject}.
 */
@RunWith(AllTests.class)
public class JsonObjectAsMapSuiteTest {
  private static class MapGenerator implements TestMapGenerator<String, JsonElement> {
    @Override
    public SampleElements<Entry<String, JsonElement>> samples() {
      return new SampleElements<>(
          Map.entry("one", JsonNull.INSTANCE),
          Map.entry("two", new JsonPrimitive(true)),
          Map.entry("three", new JsonPrimitive("test")),
          Map.entry("four", new JsonArray()),
          Map.entry("five", new JsonObject()));
    }

    @Override
    public Map<String, JsonElement> create(Object... elements) {
      JsonObject object = new JsonObject();
      // This is not completely accurate: Because there is no way to directly construct JsonObject
      // or its Map view with existing entries, this has to add the entries individually with
      // `Map#put`
      var map = object.asMap();
      for (Object element : elements) {
        var entry = (Entry<?, ?>) element;
        map.put((String) entry.getKey(), (JsonElement) entry.getValue());
      }
      return map;
    }

    @SuppressWarnings("unchecked")
    @Override
    public Entry<String, JsonElement>[] createArray(int length) {
      return (Entry<String, JsonElement>[]) new Entry<?, ?>[length];
    }

    @Override
    public Iterable<Entry<String, JsonElement>> order(
        List<Entry<String, JsonElement>> insertionOrder) {
      // Preserves insertion order
      return insertionOrder;
    }

    @Override
    public String[] createKeyArray(int length) {
      return new String[length];
    }

    @Override
    public JsonElement[] createValueArray(int length) {
      return new JsonElement[length];
    }
  }

  // Special method recognized by JUnit's `AllTests` runner
  public static Test suite() {
    return MapTestSuiteBuilder.using(new MapGenerator())
        .withFeatures(
            CollectionSize.ANY,
            MapFeature.ALLOWS_ANY_NULL_QUERIES,
            MapFeature.RESTRICTS_KEYS, // Map only allows String keys
            MapFeature.RESTRICTS_VALUES, // Map only allows JsonElement values
            MapFeature.SUPPORTS_PUT,
            MapFeature.SUPPORTS_REMOVE,
            // Affects keySet, values and entrySet (?)
            CollectionFeature.KNOWN_ORDER, // Map preserves insertion order
            CollectionFeature.SUPPORTS_ITERATOR_REMOVE)
        .named("JsonObject#asMap")
        .createTestSuite();
  }
}