Back to Repositories

Testing Java Object Conversion for gRPC Integration in Alibaba Arthas

This comprehensive test suite evaluates the JavaObjectConverter functionality in Arthas, focusing on converting various Java objects to their gRPC-compatible representations. The tests cover basic data types, arrays, collections, maps, and complex nested objects, ensuring reliable object serialization for remote debugging and monitoring.

Test Coverage Overview

The test suite provides extensive coverage of Java object conversion scenarios:
  • Basic type conversions (String, Integer)
  • Array handling (single and multi-dimensional)
  • Collection processing (List, ArrayList)
  • Map operations (HashMap)
  • Complex object structures with nested fields
  • Null value handling in different contexts

Implementation Analysis

The testing approach employs JUnit to validate the JavaObjectConverter’s capabilities:
  • Systematic verification of object transformation accuracy
  • Detailed assertion chains for property validation
  • Edge case handling for null values and complex structures
  • Integration with gRPC-specific data types and protocols

Technical Details

Testing infrastructure includes:
  • JUnit 4 testing framework
  • Custom test objects and nested class structures
  • Arthas gRPC service protobuf definitions
  • Assert utilities for comprehensive validation
  • Mock object generation for complex scenarios

Best Practices Demonstrated

The test suite exemplifies several testing best practices:
  • Isolated test cases for specific conversion scenarios
  • Comprehensive assertion coverage
  • Clear test method naming conventions
  • Proper test data setup and cleanup
  • Structured test organization for maintainability

alibaba/arthas

labs/arthas-grpc-web-proxy/src/test/java/com/taobao/arthas/grpcweb/grpc/service/JavaObjectConverterTest.java

            
package com.taobao.arthas.grpcweb.grpc.service;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import com.taobao.arthas.grpcweb.grpc.objectUtils.JavaObjectConverter;
import org.junit.Assert;
import org.junit.Test;

import io.arthas.api.ArthasServices.ArrayElement;
import io.arthas.api.ArthasServices.ArrayValue;
import io.arthas.api.ArthasServices.BasicValue;
import io.arthas.api.ArthasServices.CollectionValue;
import io.arthas.api.ArthasServices.JavaField;
import io.arthas.api.ArthasServices.JavaFields;
import io.arthas.api.ArthasServices.JavaObject;
import io.arthas.api.ArthasServices.MapEntry;
import io.arthas.api.ArthasServices.MapValue;
import io.arthas.api.ArthasServices.NullValue;

public class JavaObjectConverterTest {

    @Test
    public void testString() {
        JavaObject javaObject = JavaObjectConverter.toJavaObject("sss");
        System.err.println(javaObject);
        assertNotNull(javaObject);
        assertEquals("java.lang.String", javaObject.getClassName());
        assertTrue(javaObject.hasBasicValue());
        assertEquals("sss", javaObject.getBasicValue().getString());
    }

    @Test
    public void testToJavaObjectWithBasicType() {
        int intValue = 123;
        JavaObject javaObject = JavaObjectConverter.toJavaObject(intValue);
        assertNotNull(javaObject);
        assertEquals("java.lang.Integer", javaObject.getClassName());
        assertTrue(javaObject.hasBasicValue());
        assertEquals(intValue, javaObject.getBasicValue().getInt());
    }

    @Test
    public void testToJavaObjectWithArray() {
        int[] intArray = { 1, 2, 3 };
        JavaObject javaObject = JavaObjectConverter.toJavaObject(intArray);
        assertNotNull(javaObject);
        assertEquals("[I", javaObject.getClassName());
        assertTrue(javaObject.hasArrayValue());
        ArrayValue arrayValue = javaObject.getArrayValue();
        assertNotNull(arrayValue);
        assertEquals("int", arrayValue.getClassName());
        assertEquals(3, arrayValue.getElementsCount());

        ArrayElement element = arrayValue.getElements(1);
        assertEquals(2, element.getBasicValue().getInt());
    }

    @Test
    public void testToJavaObjectWithMultiDimensionalArray() {
        int[][] multiDimensionalArray = { { 1, 2, 3 }, { 4, 5, 6 } };

        JavaObject javaObject = JavaObjectConverter.toJavaObjectWithExpand(multiDimensionalArray,2);
        assertNotNull(javaObject);
        assertEquals("[[I", javaObject.getClassName());
        assertTrue(javaObject.hasArrayValue());
        ArrayValue arrayValue = javaObject.getArrayValue();
        assertNotNull(arrayValue);
        assertEquals("[I", arrayValue.getClassName());
        assertEquals(2, arrayValue.getElementsCount());

        ArrayElement element = arrayValue.getElements(0);
        assertTrue(element.hasArrayValue());

        ArrayValue arrayValue1 = element.getArrayValue();
        assertEquals("int", arrayValue1.getClassName());
        ArrayElement element1 = arrayValue1.getElements(0);
        assertEquals(3, arrayValue1.getElementsCount());

        assertTrue(element1.hasBasicValue());
        BasicValue basicValue = element1.getBasicValue();
        assertEquals(1, basicValue.getInt());
    }

    @Test
    public void testToJavaObjectWithCollection() {
        List<String> stringList = new ArrayList<>();
        stringList.add("foo");
        stringList.add("bar");
        stringList.add("baz");
        JavaObject javaObject = JavaObjectConverter.toJavaObject(stringList);
        assertNotNull(javaObject);
        assertEquals("java.util.ArrayList", javaObject.getClassName());
        assertTrue(javaObject.hasCollection());
        CollectionValue collectionValue = javaObject.getCollection();
        assertNotNull(collectionValue);
        assertEquals(3, collectionValue.getElementsCount());

        JavaObject object3 = collectionValue.getElements(2);
        assertEquals("baz", object3.getBasicValue().getString());
    }

    @Test
    public void testToJavaObjectWithMap() {
        Map<String, Integer> stringIntegerMap = new HashMap<>();
        stringIntegerMap.put("one", 1);
        stringIntegerMap.put("two", 2);
        JavaObject javaObject = JavaObjectConverter.toJavaObject(stringIntegerMap);
        assertNotNull(javaObject);
        assertEquals("java.util.HashMap", javaObject.getClassName());
        assertTrue(javaObject.hasMap());
        MapValue mapValue = javaObject.getMap();
        assertNotNull(mapValue);
        assertEquals(2, mapValue.getEntriesCount());

        MapEntry mapEntry = mapValue.getEntries(0);

        JavaObject key = mapEntry.getKey();
        assertEquals("one", key.getBasicValue().getString());
        JavaObject value = mapEntry.getValue();
        assertEquals(1, value.getBasicValue().getInt());
    }

    @Test
    public void testToJavaObject() {
        // 创建一个复杂的 Object
        ComplexObject complexObject = createComplexObject();

        // 转换为 JavaObject
        JavaObject javaObject = JavaObjectConverter.toJavaObject(complexObject);

        // 对转换后的 JavaObject 进行断言,验证各个 field 的值是否一致
        Assert.assertNotNull(javaObject);
        Assert.assertEquals(ComplexObject.class.getName(), javaObject.getClassName());

        JavaFields fields = javaObject.getFields();

        Map<String, JavaField> fieldMap = fields.getFieldsList().stream()
                .collect(Collectors.toMap(JavaField::getName, field -> field));

        // 验证基础类型字段
        BasicValue basicValue = fieldMap.get("basicValue").getBasicValue();
        Assert.assertEquals(5, basicValue.getInt());

        // 验证集合字段
        JavaField collection = fieldMap.get("collection");
        CollectionValue collectionValue = collection.getCollection();

        Assert.assertEquals(2, collectionValue.getElementsCount());

        // 验证数组字段
        JavaField array = fieldMap.get("arrayValue");
        ArrayValue arrayValue = array.getArrayValue();
        Assert.assertEquals(2, arrayValue.getElementsCount());

        // 验证嵌套对象字段
        JavaField nestedObject = fieldMap.get("nestedObject");
        JavaObject nestedJavaObject = nestedObject.getObjectValue();
        JavaFields nestedObjectFields = nestedJavaObject.getFields();
        Assert.assertEquals(1, nestedObjectFields.getFieldsCount());
        JavaField nestedObjectField = nestedObjectFields.getFields(0);
        Assert.assertEquals("stringValue", nestedObjectField.getName());
        Assert.assertEquals("nestedValue", nestedObjectField.getBasicValue().getString());
    }

    private ComplexObject createComplexObject() {
        ComplexObject complexObject = new ComplexObject();
        complexObject.setBasicValue(5);
        complexObject.setCollection(Arrays.asList("element1", "element2"));
        complexObject.setArrayValue(new int[] { 1, 2 });
        complexObject.setNestedObject(new NestedObject("nestedValue"));
        return complexObject;
    }

    private static class ComplexObject {
        private int basicValue;
        private Collection<String> collection;
        private int[] arrayValue;
        private NestedObject nestedObject;

        public int getBasicValue() {
            return basicValue;
        }

        public void setBasicValue(int basicValue) {
            this.basicValue = basicValue;
        }

        public Collection<String> getCollection() {
            return collection;
        }

        public void setCollection(Collection<String> collection) {
            this.collection = collection;
        }

        public int[] getArrayValue() {
            return arrayValue;
        }

        public void setArrayValue(int[] arrayValue) {
            this.arrayValue = arrayValue;
        }

        public NestedObject getNestedObject() {
            return nestedObject;
        }

        public void setNestedObject(NestedObject nestedObject) {
            this.nestedObject = nestedObject;
        }
    }

    private static class NestedObject {
        private String stringValue;

        public NestedObject(String stringValue) {
            this.stringValue = stringValue;
        }

        public String getStringValue() {
            return stringValue;
        }

        public void setStringValue(String stringValue) {
            this.stringValue = stringValue;
        }
    }

    private static class TestObject {
        private Double[] doubleArray;

        public Double[] getDoubleArray() {
            return doubleArray;
        }

        public void setDoubleArray(Double[] doubleArray) {
            this.doubleArray = doubleArray;
        }
    }

    @Test
    public void testObjectWithDubboArrayField() {
        // 创建测试对象
        TestObject testObject = new TestObject();
        testObject.setDoubleArray(new Double[] { 1.0, 2.0, 3.0 });

        // 转换为JavaObject
        JavaObject javaObject = JavaObjectConverter.toJavaObject(testObject);

        // 检查各个field的值是否一致
        for (int i = 0; i < testObject.getDoubleArray().length; i++) {
            Double expectedValue = testObject.getDoubleArray()[i];
            ArrayValue arrayValue = javaObject.getFields().getFields(0).getArrayValue();
            ArrayElement arrayElement = arrayValue.getElements(i);
            Double actualValue = arrayElement.getBasicValue().getDouble();
            Assert.assertEquals(expectedValue, actualValue);
        }
    }

    @Test
    public void testToJavaObjectWithNullValue() {
        JavaObject result = JavaObjectConverter.toJavaObject(null);
        assertNotNull(result);
        assertTrue(result.hasNullValue());
        assertEquals(NullValue.getDefaultInstance(), result.getNullValue());
    }

    @Test
    public void testToJavaObjectWithNullKeyInMap() {
        Map<String, Object> map = new HashMap<>();
        map.put(null, "value");

        JavaObject result = JavaObjectConverter.toJavaObject(map);
        assertNotNull(result);
        assertTrue(result.hasMap());
        MapValue mapValue = result.getMap();
        assertEquals(1, mapValue.getEntriesCount());

        MapEntry entry = mapValue.getEntries(0);
        assertNotNull(entry.getKey());
        assertTrue(entry.getKey().hasNullValue());
        assertEquals(NullValue.getDefaultInstance(), entry.getKey().getNullValue());

        assertNotNull(entry.getValue());
        assertTrue(entry.getValue().hasBasicValue());

        assertEquals("value", entry.getValue().getBasicValue().getString());
    }

    @Test
    public void testToJavaObjectWithNullValueInArray() {
        Object[] array = new Object[3];
        array[0] = "value";
        array[1] = null;
        array[2] = 123;

        JavaObject result = JavaObjectConverter.toJavaObject(array);
        assertNotNull(result);
        assertTrue(result.hasArrayValue());
        ArrayValue arrayValue = result.getArrayValue();
        assertEquals(3, arrayValue.getElementsCount());

        ArrayElement element1 = arrayValue.getElements(0);
        assertTrue(element1.hasObjectValue());
        JavaObject objectValue1 = element1.getObjectValue();
        assertTrue(objectValue1.hasBasicValue());
        assertEquals("value", objectValue1.getBasicValue().getString());

        ArrayElement element2 = arrayValue.getElements(1);
        assertNotNull(element2.getNullValue());

        ArrayElement element3 = arrayValue.getElements(2);
        assertTrue(element3.hasObjectValue());
        JavaObject objectValue3 = element3.getObjectValue();
        assertTrue(objectValue3.hasBasicValue());
        assertEquals(123, objectValue3.getBasicValue().getInt());
    }
}