This is an automated email from the ASF dual-hosted git repository.

delei pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/fesod.git


The following commit(s) were added to refs/heads/main by this push:
     new 50404dec test: add unit tests for utils (#821)
50404dec is described below

commit 50404dec010cddb1e6af4cdd4ffa34a1c2bff1dc
Author: Bengbengbalabalabeng 
<[email protected]>
AuthorDate: Sun Feb 8 12:21:24 2026 +0800

    test: add unit tests for utils (#821)
---
 .../apache/fesod/sheet/util/BeanMapUtilsTest.java  |  95 +++++
 .../apache/fesod/sheet/util/ClassUtilsTest.java    | 350 +++++++++++++++++
 .../fesod/sheet/util/ConverterUtilsTest.java       | 275 +++++++++++++
 .../org/apache/fesod/sheet/util/DateUtilsTest.java | 370 ++++++++++++++++++
 .../apache/fesod/sheet/util/FieldUtilsTest.java    | 242 ++++++++++++
 .../org/apache/fesod/sheet/util/FileUtilsTest.java | 308 +++++++++++++++
 .../sheet/util/NumberDataFormatterUtilsTest.java   | 110 ++++++
 .../apache/fesod/sheet/util/NumberUtilsTest.java   | 270 +++++++++++++
 .../apache/fesod/sheet/util/ParameterUtilTest.java | 166 ++++++++
 .../org/apache/fesod/sheet/util/PoiUtilsTest.java  |  81 ++++
 .../apache/fesod/sheet/util/SheetUtilsTest.java    | 180 +++++++++
 .../apache/fesod/sheet/util/WorkBookUtilTest.java  | 381 ++++++++++++++++++
 .../fesod/sheet/util/WriteHandlerUtilsTest.java    | 424 +++++++++++++++++++++
 13 files changed, 3252 insertions(+)

diff --git 
a/fesod-sheet/src/test/java/org/apache/fesod/sheet/util/BeanMapUtilsTest.java 
b/fesod-sheet/src/test/java/org/apache/fesod/sheet/util/BeanMapUtilsTest.java
new file mode 100644
index 00000000..19eb919a
--- /dev/null
+++ 
b/fesod-sheet/src/test/java/org/apache/fesod/sheet/util/BeanMapUtilsTest.java
@@ -0,0 +1,95 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.fesod.sheet.util;
+
+import org.apache.fesod.shaded.cglib.beans.BeanMap;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests {@link BeanMapUtils}
+ */
+class BeanMapUtilsTest {
+
+    public static class TestUser {
+        private String name;
+        private int age;
+
+        public String getName() {
+            return name;
+        }
+
+        public void setName(String name) {
+            this.name = name;
+        }
+
+        public int getAge() {
+            return age;
+        }
+
+        public void setAge(int age) {
+            this.age = age;
+        }
+    }
+
+    @Test
+    void test_create_Functionality() {
+        TestUser user = new TestUser();
+        user.setName("Fesod");
+        user.setAge(18);
+
+        BeanMap beanMap = BeanMapUtils.create(user);
+
+        Assertions.assertNotNull(beanMap);
+        Assertions.assertEquals("Fesod", beanMap.get("name"));
+        Assertions.assertEquals(18, beanMap.get("age"));
+        beanMap.put("name", "Fesod");
+        Assertions.assertEquals("Fesod", user.getName());
+    }
+
+    @Test
+    void test_create_NamingPolicy() {
+        TestUser user = new TestUser();
+        BeanMap beanMap = BeanMapUtils.create(user);
+
+        String generatedClassName = beanMap.getClass().getName();
+
+        Assertions.assertTrue(generatedClassName.contains("ByFesodCGLIB"));
+    }
+
+    @Test
+    void test_NamingPolicy_tag() {
+        BeanMapUtils.FesodSheetNamingPolicy policy = 
BeanMapUtils.FesodSheetNamingPolicy.INSTANCE;
+
+        Assertions.assertDoesNotThrow(() -> {
+            java.lang.reflect.Method getTagMethod = 
policy.getClass().getDeclaredMethod("getTag");
+            getTagMethod.setAccessible(true);
+            String tag = (String) getTagMethod.invoke(policy);
+            Assertions.assertEquals("ByFesodCGLIB", tag);
+        });
+    }
+
+    @Test
+    void test_create_null() {
+        Assertions.assertThrows(NullPointerException.class, () -> {
+            BeanMapUtils.create(null);
+        });
+    }
+}
diff --git 
a/fesod-sheet/src/test/java/org/apache/fesod/sheet/util/ClassUtilsTest.java 
b/fesod-sheet/src/test/java/org/apache/fesod/sheet/util/ClassUtilsTest.java
new file mode 100644
index 00000000..da41bae8
--- /dev/null
+++ b/fesod-sheet/src/test/java/org/apache/fesod/sheet/util/ClassUtilsTest.java
@@ -0,0 +1,350 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.fesod.sheet.util;
+
+import java.lang.reflect.Field;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import org.apache.fesod.shaded.cglib.beans.BeanMap;
+import org.apache.fesod.sheet.annotation.ExcelIgnore;
+import org.apache.fesod.sheet.annotation.ExcelProperty;
+import org.apache.fesod.sheet.annotation.format.DateTimeFormat;
+import org.apache.fesod.sheet.annotation.format.NumberFormat;
+import org.apache.fesod.sheet.converters.Converter;
+import org.apache.fesod.sheet.converters.string.StringStringConverter;
+import org.apache.fesod.sheet.enums.CacheLocationEnum;
+import org.apache.fesod.sheet.metadata.FieldCache;
+import org.apache.fesod.sheet.metadata.FieldWrapper;
+import org.apache.fesod.sheet.metadata.GlobalConfiguration;
+import org.apache.fesod.sheet.metadata.property.DateTimeFormatProperty;
+import org.apache.fesod.sheet.metadata.property.ExcelContentProperty;
+import org.apache.fesod.sheet.metadata.property.FontProperty;
+import org.apache.fesod.sheet.metadata.property.NumberFormatProperty;
+import org.apache.fesod.sheet.metadata.property.StyleProperty;
+import org.apache.fesod.sheet.write.metadata.holder.WriteHolder;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+/**
+ * Tests {@link ClassUtils}
+ */
+@ExtendWith(MockitoExtension.class)
+class ClassUtilsTest {
+
+    @Mock
+    private WriteHolder writeHolder;
+
+    @Mock
+    private GlobalConfiguration globalConfiguration;
+
+    @BeforeEach
+    void setUp() {
+        
Mockito.lenient().when(writeHolder.globalConfiguration()).thenReturn(globalConfiguration);
+    }
+
+    @AfterEach
+    void tearDown() {
+        ClassUtils.removeThreadLocalCache();
+        ClassUtils.FIELD_CACHE.clear();
+        ClassUtils.CONTENT_CACHE.clear();
+        ClassUtils.CLASS_CONTENT_CACHE.clear();
+    }
+
+    private static class SimpleEntity {
+        @ExcelProperty("Name")
+        private String name;
+
+        @ExcelProperty(value = "Age", order = 1)
+        private Integer age;
+    }
+
+    private static class ComplexEntity {
+        @ExcelProperty(index = 0)
+        private String id;
+
+        @ExcelProperty(index = 2)
+        private String name;
+
+        @ExcelProperty(order = 10)
+        private String email;
+
+        @ExcelIgnore
+        private String ignoredField;
+
+        private String noAnnotationField;
+    }
+
+    private static class FormatEntity {
+        @DateTimeFormat("yyyy-MM-dd")
+        private Date date;
+
+        @NumberFormat("#.00")
+        private Double money;
+
+        @ExcelProperty(converter = StringStringConverter.class)
+        private String customConvert;
+    }
+
+    @Test
+    void test_declaredFields_cache_memory() {
+        
Mockito.when(globalConfiguration.getFiledCacheLocation()).thenReturn(CacheLocationEnum.MEMORY);
+
+        FieldCache cache1 = ClassUtils.declaredFields(SimpleEntity.class, 
writeHolder);
+        Assertions.assertNotNull(cache1);
+
+        Assertions.assertFalse(ClassUtils.FIELD_CACHE.isEmpty());
+
+        FieldCache cache2 = ClassUtils.declaredFields(SimpleEntity.class, 
writeHolder);
+        Assertions.assertSame(cache1, cache2);
+    }
+
+    @Test
+    void test_declaredFields_cache_ThreadLocal() throws NoSuchFieldException, 
IllegalAccessException {
+        
Mockito.when(globalConfiguration.getFiledCacheLocation()).thenReturn(CacheLocationEnum.THREAD_LOCAL);
+
+        FieldCache cache1 = ClassUtils.declaredFields(SimpleEntity.class, 
writeHolder);
+        Assertions.assertNotNull(cache1);
+
+        Assertions.assertTrue(ClassUtils.FIELD_CACHE.isEmpty());
+
+        FieldCache cache2 = ClassUtils.declaredFields(SimpleEntity.class, 
writeHolder);
+        Assertions.assertSame(cache1, cache2);
+    }
+
+    @Test
+    void test_declaredFields_non_cache() {
+        
Mockito.when(globalConfiguration.getFiledCacheLocation()).thenReturn(CacheLocationEnum.NONE);
+
+        FieldCache cache1 = ClassUtils.declaredFields(SimpleEntity.class, 
writeHolder);
+        FieldCache cache2 = ClassUtils.declaredFields(SimpleEntity.class, 
writeHolder);
+
+        Assertions.assertNotSame(cache1, cache2);
+        Assertions.assertTrue(ClassUtils.FIELD_CACHE.isEmpty());
+    }
+
+    @Test
+    void test_declaredFields_ordering() {
+        
Mockito.when(globalConfiguration.getFiledCacheLocation()).thenReturn(CacheLocationEnum.NONE);
+
+        FieldCache fieldCache = ClassUtils.declaredFields(ComplexEntity.class, 
writeHolder);
+        Map<Integer, FieldWrapper> sortedMap = fieldCache.getSortedFieldMap();
+
+        Assertions.assertTrue(sortedMap.containsKey(0));
+        Assertions.assertEquals("id", sortedMap.get(0).getFieldName());
+
+        Assertions.assertTrue(sortedMap.containsKey(2));
+        Assertions.assertEquals("name", sortedMap.get(2).getFieldName());
+
+        Assertions.assertTrue(sortedMap.containsKey(1));
+        Assertions.assertEquals("email", sortedMap.get(1).getFieldName());
+
+        Assertions.assertTrue(sortedMap.containsKey(3));
+        Assertions.assertEquals("noAnnotationField", 
sortedMap.get(3).getFieldName());
+    }
+
+    @Test
+    void test_declaredFields_ignore() {
+        
Mockito.when(globalConfiguration.getFiledCacheLocation()).thenReturn(CacheLocationEnum.NONE);
+
+        FieldCache fieldCache = ClassUtils.declaredFields(ComplexEntity.class, 
writeHolder);
+
+        boolean containsIgnored =
+                fieldCache.getSortedFieldMap().values().stream().anyMatch(f -> 
"ignoredField".equals(f.getFieldName()));
+
+        Assertions.assertFalse(containsIgnored);
+    }
+
+    @Test
+    void test_declaredFields_WriteHolder_exclude() {
+        
Mockito.when(globalConfiguration.getFiledCacheLocation()).thenReturn(CacheLocationEnum.NONE);
+
+        
Mockito.when(writeHolder.excludeColumnFieldNames()).thenReturn(Collections.singleton("name"));
+        Mockito.when(writeHolder.ignore(Mockito.anyString(), 
Mockito.anyInt())).thenReturn(false);
+        Mockito.when(writeHolder.ignore(Mockito.eq("name"), 
Mockito.anyInt())).thenReturn(true);
+
+        FieldCache fieldCache = ClassUtils.declaredFields(SimpleEntity.class, 
writeHolder);
+
+        Map<Integer, FieldWrapper> map = fieldCache.getSortedFieldMap();
+
+        boolean hasName = map.values().stream().anyMatch(f -> 
"name".equals(f.getFieldName()));
+        Assertions.assertFalse(hasName);
+
+        boolean hasAge = map.values().stream().anyMatch(f -> 
"age".equals(f.getFieldName()));
+        Assertions.assertTrue(hasAge);
+    }
+
+    @Test
+    void test_declaredFields_resort() {
+        
Mockito.when(globalConfiguration.getFiledCacheLocation()).thenReturn(CacheLocationEnum.NONE);
+
+        Mockito.when(writeHolder.orderByIncludeColumn()).thenReturn(true);
+        List<String> include = Arrays.asList("age", "name");
+        
Mockito.when(writeHolder.includeColumnFieldNames()).thenReturn(include);
+
+        FieldCache fieldCache = ClassUtils.declaredFields(SimpleEntity.class, 
writeHolder);
+        Map<Integer, FieldWrapper> map = fieldCache.getSortedFieldMap();
+
+        Assertions.assertEquals("age", map.get(0).getFieldName());
+        Assertions.assertEquals("name", map.get(1).getFieldName());
+    }
+
+    @Test
+    void test_declaredFields_resort_byIndex() {
+        
Mockito.when(globalConfiguration.getFiledCacheLocation()).thenReturn(CacheLocationEnum.NONE);
+        Mockito.when(writeHolder.orderByIncludeColumn()).thenReturn(true);
+        Mockito.when(writeHolder.includeColumnFieldNames()).thenReturn(null);
+        
Mockito.when(writeHolder.includeColumnIndexes()).thenReturn(Arrays.asList(2, 
0));
+        Mockito.when(writeHolder.ignore(Mockito.anyString(), 
Mockito.anyInt())).thenReturn(false);
+
+        FieldCache fieldCache = ClassUtils.declaredFields(ComplexEntity.class, 
writeHolder);
+        Map<Integer, FieldWrapper> sortedMap = fieldCache.getSortedFieldMap();
+
+        Assertions.assertEquals(2, sortedMap.size());
+
+        Assertions.assertTrue(sortedMap.containsKey(0));
+        Assertions.assertEquals("name", sortedMap.get(0).getFieldName());
+
+        Assertions.assertTrue(sortedMap.containsKey(1));
+        Assertions.assertEquals("id", sortedMap.get(1).getFieldName());
+    }
+
+    @Test
+    void test_declaredExcelContentProperty() {
+        
Mockito.when(globalConfiguration.getFiledCacheLocation()).thenReturn(CacheLocationEnum.NONE);
+
+        ExcelContentProperty property =
+                ClassUtils.declaredExcelContentProperty(null, 
FormatEntity.class, "date", writeHolder);
+
+        Assertions.assertNotNull(property);
+        Assertions.assertNotNull(property.getDateTimeFormatProperty());
+        Assertions.assertEquals(
+                "yyyy-MM-dd", 
property.getDateTimeFormatProperty().getFormat());
+    }
+
+    @Test
+    void test_declaredExcelContentProperty_cache_memory() {
+        
Mockito.when(globalConfiguration.getFiledCacheLocation()).thenReturn(CacheLocationEnum.MEMORY);
+
+        ExcelContentProperty property =
+                ClassUtils.declaredExcelContentProperty(null, 
FormatEntity.class, "date", writeHolder);
+
+        Assertions.assertNotNull(property);
+        Assertions.assertNotNull(property.getDateTimeFormatProperty());
+        Assertions.assertEquals(
+                "yyyy-MM-dd", 
property.getDateTimeFormatProperty().getFormat());
+    }
+
+    @Test
+    void test_declaredExcelContentProperty_cache_ThreadLocal() {
+        
Mockito.when(globalConfiguration.getFiledCacheLocation()).thenReturn(CacheLocationEnum.THREAD_LOCAL);
+
+        ExcelContentProperty property =
+                ClassUtils.declaredExcelContentProperty(null, 
FormatEntity.class, "date", writeHolder);
+
+        Assertions.assertNotNull(property);
+        Assertions.assertNotNull(property.getDateTimeFormatProperty());
+        Assertions.assertEquals(
+                "yyyy-MM-dd", 
property.getDateTimeFormatProperty().getFormat());
+    }
+
+    @Test
+    void test_declaredExcelContentProperty_BeanMap() {
+        BeanMap beanMapMocked = Mockito.mock(BeanMap.class);
+        FormatEntity beanMocked = Mockito.mock(FormatEntity.class);
+
+        
Mockito.when(globalConfiguration.getFiledCacheLocation()).thenReturn(CacheLocationEnum.NONE);
+        Mockito.when(beanMapMocked.getBean()).thenReturn(beanMocked);
+
+        ExcelContentProperty property =
+                ClassUtils.declaredExcelContentProperty(beanMapMocked, 
FormatEntity.class, "date", writeHolder);
+
+        Assertions.assertNotNull(property);
+        Assertions.assertNotNull(property.getDateTimeFormatProperty());
+        Assertions.assertEquals(
+                "yyyy-MM-dd", 
property.getDateTimeFormatProperty().getFormat());
+    }
+
+    @Test
+    void test_declaredExcelContentProperty_converter() {
+        
Mockito.when(globalConfiguration.getFiledCacheLocation()).thenReturn(CacheLocationEnum.NONE);
+
+        ExcelContentProperty property =
+                ClassUtils.declaredExcelContentProperty(null, 
FormatEntity.class, "customConvert", writeHolder);
+
+        Assertions.assertNotNull(property);
+        Assertions.assertNotNull(property.getConverter());
+        Assertions.assertInstanceOf(StringStringConverter.class, 
property.getConverter());
+    }
+
+    @Test
+    void test_combineExcelContentProperty() throws NoSuchFieldException {
+        Field field = SimpleEntity.class.getDeclaredField("name");
+        Converter converter = Mockito.mock(Converter.class);
+        DateTimeFormatProperty dateTimeFormatProperty = 
Mockito.mock(DateTimeFormatProperty.class);
+        NumberFormatProperty numberFormatProperty = 
Mockito.mock(NumberFormatProperty.class);
+        StyleProperty styleProperty = Mockito.mock(StyleProperty.class);
+        FontProperty fontProperty = Mockito.mock(FontProperty.class);
+
+        ExcelContentProperty propertyMocked = 
Mockito.mock(ExcelContentProperty.class);
+        Mockito.when(propertyMocked.getField()).thenReturn(field);
+        Mockito.when(propertyMocked.getConverter()).thenReturn(converter);
+        
Mockito.when(propertyMocked.getDateTimeFormatProperty()).thenReturn(dateTimeFormatProperty);
+        
Mockito.when(propertyMocked.getNumberFormatProperty()).thenReturn(numberFormatProperty);
+        
Mockito.when(propertyMocked.getContentStyleProperty()).thenReturn(styleProperty);
+        
Mockito.when(propertyMocked.getContentFontProperty()).thenReturn(fontProperty);
+
+        ExcelContentProperty combine = new ExcelContentProperty();
+
+        ClassUtils.combineExcelContentProperty(combine, propertyMocked);
+
+        Assertions.assertEquals(field, combine.getField());
+        Assertions.assertEquals(converter, combine.getConverter());
+        Assertions.assertEquals(dateTimeFormatProperty, 
combine.getDateTimeFormatProperty());
+        Assertions.assertEquals(numberFormatProperty, 
combine.getNumberFormatProperty());
+        Assertions.assertEquals(styleProperty, 
combine.getContentStyleProperty());
+        Assertions.assertEquals(fontProperty, 
combine.getContentFontProperty());
+    }
+
+    interface InterfaceA {}
+
+    interface InterfaceB extends InterfaceA {}
+
+    static class ClassImpl implements InterfaceB {}
+
+    @Test
+    void test_getAllInterfaces() {
+        List<Class<?>> interfaces = 
ClassUtils.getAllInterfaces(ClassImpl.class);
+
+        Assertions.assertNull(ClassUtils.getAllInterfaces(null));
+        Assertions.assertNotNull(interfaces);
+        Assertions.assertTrue(interfaces.contains(InterfaceB.class));
+        Assertions.assertTrue(interfaces.contains(InterfaceA.class));
+        Assertions.assertEquals(InterfaceB.class, interfaces.get(0));
+    }
+}
diff --git 
a/fesod-sheet/src/test/java/org/apache/fesod/sheet/util/ConverterUtilsTest.java 
b/fesod-sheet/src/test/java/org/apache/fesod/sheet/util/ConverterUtilsTest.java
new file mode 100644
index 00000000..8f8e69cd
--- /dev/null
+++ 
b/fesod-sheet/src/test/java/org/apache/fesod/sheet/util/ConverterUtilsTest.java
@@ -0,0 +1,275 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.fesod.sheet.util;
+
+import java.lang.reflect.Field;
+import java.math.BigDecimal;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.TreeMap;
+import org.apache.fesod.sheet.context.AnalysisContext;
+import org.apache.fesod.sheet.converters.Converter;
+import org.apache.fesod.sheet.converters.ConverterKeyBuild;
+import org.apache.fesod.sheet.converters.NullableObjectConverter;
+import org.apache.fesod.sheet.enums.CellDataTypeEnum;
+import org.apache.fesod.sheet.exception.ExcelDataConvertException;
+import org.apache.fesod.sheet.metadata.data.ReadCellData;
+import org.apache.fesod.sheet.metadata.property.ExcelContentProperty;
+import org.apache.fesod.sheet.read.metadata.holder.ReadRowHolder;
+import org.apache.fesod.sheet.read.metadata.holder.ReadSheetHolder;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+/**
+ * Tests {@link ConverterUtils}
+ */
+@ExtendWith(MockitoExtension.class)
+class ConverterUtilsTest {
+
+    @Mock
+    private AnalysisContext context;
+
+    @Mock
+    private ReadSheetHolder readSheetHolder;
+
+    @Mock
+    private ReadRowHolder readRowHolder;
+
+    @Mock
+    private Converter stringConverter;
+
+    @Mock
+    private Converter integerConverter;
+
+    private Map<ConverterKeyBuild.ConverterKey, Converter<?>> converterMap;
+
+    @BeforeEach
+    void setUp() {
+        converterMap = new HashMap<>();
+
+        
Mockito.lenient().when(context.readSheetHolder()).thenReturn(readSheetHolder);
+        
Mockito.lenient().when(context.readRowHolder()).thenReturn(readRowHolder);
+        
Mockito.lenient().when(readSheetHolder.converterMap()).thenReturn(converterMap);
+        Mockito.lenient().when(readRowHolder.getRowIndex()).thenReturn(1);
+    }
+
+    @Test
+    void test_convertToStringMap_normal() throws Exception {
+        // {0: "A", 1: "B"}
+        Map<Integer, ReadCellData<?>> cellDataMap = new TreeMap<>();
+        cellDataMap.put(0, new ReadCellData<>("A"));
+        cellDataMap.put(1, new ReadCellData<>("B"));
+
+        ConverterKeyBuild.ConverterKey key = 
ConverterKeyBuild.buildKey(String.class, CellDataTypeEnum.STRING);
+        converterMap.put(key, stringConverter);
+        
Mockito.when(stringConverter.convertToJavaData(Mockito.any())).thenReturn("A", 
"B");
+
+        Map<Integer, String> result = 
ConverterUtils.convertToStringMap(cellDataMap, context);
+
+        Assertions.assertEquals(2, result.size());
+        Assertions.assertEquals("A", result.get(0));
+        Assertions.assertEquals("B", result.get(1));
+    }
+
+    @Test
+    void test_convertToStringMap_withGap() throws Exception {
+        // {0: "A", 2: "C"}
+        Map<Integer, ReadCellData<?>> cellDataMap = new TreeMap<>();
+        cellDataMap.put(0, new ReadCellData<>("A"));
+        cellDataMap.put(2, new ReadCellData<>("C"));
+
+        ConverterKeyBuild.ConverterKey key = 
ConverterKeyBuild.buildKey(String.class, CellDataTypeEnum.STRING);
+        converterMap.put(key, stringConverter);
+        
Mockito.when(stringConverter.convertToJavaData(Mockito.any())).thenReturn("A", 
"C");
+
+        Map<Integer, String> result = 
ConverterUtils.convertToStringMap(cellDataMap, context);
+
+        // 0->A, 1->null, 2->C
+        Assertions.assertEquals(3, result.size());
+        Assertions.assertEquals("A", result.get(0));
+        Assertions.assertNull(result.get(1));
+        Assertions.assertEquals("C", result.get(2));
+    }
+
+    @Test
+    void test_convertToStringMap_emptyCell() throws Exception {
+        Map<Integer, ReadCellData<?>> cellDataMap = new HashMap<>();
+        cellDataMap.put(0, new ReadCellData<>(CellDataTypeEnum.EMPTY));
+
+        Map<Integer, String> result = 
ConverterUtils.convertToStringMap(cellDataMap, context);
+
+        Assertions.assertNull(result.get(0));
+        Mockito.verify(stringConverter, 
Mockito.never()).convertToJavaData(Mockito.any());
+    }
+
+    @Test
+    void test_convertToStringMap_noConverter() {
+        Map<Integer, ReadCellData<?>> cellDataMap = new HashMap<>();
+        cellDataMap.put(0, new ReadCellData<>(CellDataTypeEnum.BOOLEAN));
+
+        Assertions.assertThrows(ExcelDataConvertException.class, () -> {
+            ConverterUtils.convertToStringMap(cellDataMap, context);
+        });
+    }
+
+    class DemoData {
+        private String stringField;
+        private ReadCellData<Integer> cellDataIntField;
+        private ReadCellData rawCellDataField;
+    }
+
+    @Test
+    void test_convertToJavaData_simpleConversion() throws Exception {
+        ReadCellData<?> cellData = new ReadCellData<>(new BigDecimal("123"));
+
+        ConverterKeyBuild.ConverterKey key = 
ConverterKeyBuild.buildKey(Integer.class, CellDataTypeEnum.NUMBER);
+        converterMap.put(key, integerConverter);
+        
Mockito.when(integerConverter.convertToJavaData(Mockito.any())).thenReturn(123);
+
+        Object result = ConverterUtils.convertToJavaObject(
+                cellData, null, Integer.class, null, null, converterMap, 
context, 1, 0);
+
+        Assertions.assertEquals(123, result);
+    }
+
+    @Test
+    void test_convertToJavaData() throws Exception {
+        ReadCellData<?> cellData = new ReadCellData<>("123");
+
+        ConverterKeyBuild.ConverterKey key = 
ConverterKeyBuild.buildKey(String.class, CellDataTypeEnum.STRING);
+        converterMap.put(key, stringConverter);
+        
Mockito.when(stringConverter.convertToJavaData(Mockito.any())).thenReturn("123");
+
+        Object result1 =
+                ConverterUtils.convertToJavaObject(cellData, null, null, null, 
null, converterMap, context, 1, 0);
+
+        Field field = DemoData.class.getDeclaredField("stringField");
+        Object result2 =
+                ConverterUtils.convertToJavaObject(cellData, field, null, 
null, null, converterMap, context, 1, 0);
+
+        Assertions.assertEquals("123", result1);
+        Assertions.assertEquals("123", result2);
+    }
+
+    @Test
+    void test_ReadCellData_generic_inference() throws Exception, 
NoSuchFieldException {
+        ReadCellData<?> cellData = new ReadCellData<>(new BigDecimal("100"));
+        Field field = DemoData.class.getDeclaredField("cellDataIntField");
+
+        ConverterKeyBuild.ConverterKey key = 
ConverterKeyBuild.buildKey(Integer.class, CellDataTypeEnum.NUMBER);
+        converterMap.put(key, integerConverter);
+        
Mockito.when(integerConverter.convertToJavaData(Mockito.any())).thenReturn(100);
+
+        Object result = ConverterUtils.convertToJavaObject(
+                cellData, field, ReadCellData.class, null, null, converterMap, 
context, 1, 0);
+
+        Assertions.assertInstanceOf(ReadCellData.class, result);
+        ReadCellData<?> resultData = (ReadCellData<?>) result;
+        Assertions.assertEquals(100, resultData.getData());
+    }
+
+    @Test
+    void test_ReadCellData_raw_defaultString() throws Exception, 
NoSuchFieldException {
+        ReadCellData<?> cellData = new ReadCellData<>("test");
+        Field field = DemoData.class.getDeclaredField("rawCellDataField");
+
+        ConverterKeyBuild.ConverterKey key = 
ConverterKeyBuild.buildKey(String.class, CellDataTypeEnum.STRING);
+        converterMap.put(key, stringConverter);
+        
Mockito.when(stringConverter.convertToJavaData(Mockito.any())).thenReturn("test");
+
+        Object result = ConverterUtils.convertToJavaObject(
+                cellData, field, ReadCellData.class, null, null, converterMap, 
context, 1, 0);
+
+        Assertions.assertTrue(result instanceof ReadCellData);
+        Mockito.verify(stringConverter).convertToJavaData(Mockito.any());
+    }
+
+    @Test
+    void test_Priority_ContentProperty() throws Exception {
+        ReadCellData<?> cellData = new ReadCellData<>("test");
+
+        Converter globalConverter = Mockito.mock(Converter.class);
+        Converter customConverter = Mockito.mock(Converter.class);
+        converterMap.put(ConverterKeyBuild.buildKey(String.class, 
CellDataTypeEnum.STRING), globalConverter);
+
+        ExcelContentProperty property = 
Mockito.mock(ExcelContentProperty.class);
+        Mockito.when(property.getConverter()).thenReturn(customConverter);
+        
Mockito.when(customConverter.convertToJavaData(Mockito.any())).thenReturn("Custom");
+
+        Object result = ConverterUtils.convertToJavaObject(
+                cellData, null, String.class, null, property, converterMap, 
context, 1, 0);
+
+        Assertions.assertEquals("Custom", result);
+        Mockito.verify(customConverter).convertToJavaData(Mockito.any());
+        Mockito.verify(globalConverter, 
Mockito.never()).convertToJavaData(Mockito.any());
+    }
+
+    @Test
+    void test_EmptyCell_NormalConverter() throws Exception, 
NoSuchFieldException {
+        ReadCellData<?> cellData = new ReadCellData<>(CellDataTypeEnum.EMPTY);
+        ExcelContentProperty property = 
Mockito.mock(ExcelContentProperty.class);
+        Mockito.when(property.getConverter()).thenReturn(stringConverter);
+
+        Object result = ConverterUtils.convertToJavaObject(
+                cellData, null, String.class, null, property, converterMap, 
context, 1, 0);
+
+        Assertions.assertNull(result);
+        Mockito.verify(stringConverter, 
Mockito.never()).convertToJavaData(Mockito.any());
+    }
+
+    @Test
+    void test_EmptyCell_NullableConverter() throws Exception {
+        ReadCellData<?> cellData = new ReadCellData<>(CellDataTypeEnum.EMPTY);
+        ExcelContentProperty property = 
Mockito.mock(ExcelContentProperty.class);
+
+        Converter nullableConverter =
+                Mockito.mock(Converter.class, 
Mockito.withSettings().extraInterfaces(NullableObjectConverter.class));
+        Mockito.when(property.getConverter()).thenReturn(nullableConverter);
+        
Mockito.when(nullableConverter.convertToJavaData(Mockito.any())).thenReturn("HandledNull");
+
+        Object result = ConverterUtils.convertToJavaObject(
+                cellData, null, String.class, null, property, converterMap, 
context, 1, 0);
+
+        Assertions.assertEquals("HandledNull", result);
+        Mockito.verify(nullableConverter).convertToJavaData(Mockito.any());
+    }
+
+    @Test
+    void test_exception_wrapping() throws Exception {
+        ReadCellData<?> cellData = new ReadCellData<>("ErrorData");
+        ConverterKeyBuild.ConverterKey key = 
ConverterKeyBuild.buildKey(String.class, CellDataTypeEnum.STRING);
+        converterMap.put(key, stringConverter);
+
+        
Mockito.when(stringConverter.convertToJavaData(Mockito.any())).thenThrow(new 
RuntimeException("Inner error"));
+
+        ExcelDataConvertException ex = 
Assertions.assertThrows(ExcelDataConvertException.class, () -> {
+            ConverterUtils.convertToJavaObject(cellData, null, String.class, 
null, null, converterMap, context, 99, 88);
+        });
+
+        Assertions.assertEquals(99, ex.getRowIndex());
+        Assertions.assertEquals(88, ex.getColumnIndex());
+        Assertions.assertTrue(ex.getMessage().contains("Convert data"));
+    }
+}
diff --git 
a/fesod-sheet/src/test/java/org/apache/fesod/sheet/util/DateUtilsTest.java 
b/fesod-sheet/src/test/java/org/apache/fesod/sheet/util/DateUtilsTest.java
new file mode 100644
index 00000000..43460cfc
--- /dev/null
+++ b/fesod-sheet/src/test/java/org/apache/fesod/sheet/util/DateUtilsTest.java
@@ -0,0 +1,370 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.fesod.sheet.util;
+
+import java.lang.reflect.Field;
+import java.math.BigDecimal;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.Locale;
+import java.util.Map;
+import java.util.TimeZone;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.CsvSource;
+import org.junit.jupiter.params.provider.ValueSource;
+
+/**
+ * Tests {@link DateUtils}
+ */
+class DateUtilsTest {
+
+    @AfterEach
+    void tearDown() {
+        DateUtils.removeThreadLocalCache();
+    }
+
+    @Test
+    void test_switchDateFormat() {
+        Assertions.assertEquals(DateUtils.DATE_FORMAT_19, 
DateUtils.switchDateFormat("2026-01-01 12:00:00"));
+        Assertions.assertEquals(
+                DateUtils.DATE_FORMAT_19_FORWARD_SLASH, 
DateUtils.switchDateFormat("2026/01/01 12:00:00"));
+
+        Assertions.assertEquals(DateUtils.DATE_FORMAT_16, 
DateUtils.switchDateFormat("2026-01-01 12:00"));
+        Assertions.assertEquals(DateUtils.DATE_FORMAT_16_FORWARD_SLASH, 
DateUtils.switchDateFormat("2026/01/01 12:00"));
+
+        Assertions.assertEquals(DateUtils.DATE_FORMAT_17, 
DateUtils.switchDateFormat("20260101 12:00:00"));
+        Assertions.assertEquals(DateUtils.DATE_FORMAT_14, 
DateUtils.switchDateFormat("20260101120000"));
+        Assertions.assertEquals(DateUtils.DATE_FORMAT_10, 
DateUtils.switchDateFormat("2026-01-01"));
+
+        Assertions.assertThrows(
+                IllegalArgumentException.class, () -> 
DateUtils.switchDateFormat("invalid_datestring_length"));
+    }
+
+    @Test
+    void test_parseDate() throws ParseException {
+        String dateStr = "2026-10-01 12:30:45";
+        Date date1 = DateUtils.parseDate(dateStr);
+
+        Calendar cal1 = Calendar.getInstance();
+        cal1.setTime(date1);
+        Assertions.assertEquals(2026, cal1.get(Calendar.YEAR));
+        Assertions.assertEquals(Calendar.OCTOBER, cal1.get(Calendar.MONTH));
+        Assertions.assertEquals(30, cal1.get(Calendar.MINUTE));
+
+        Date date2 = DateUtils.parseDate(dateStr, "");
+
+        Calendar cal2 = Calendar.getInstance();
+        cal2.setTime(date2);
+        Assertions.assertEquals(2026, cal2.get(Calendar.YEAR));
+        Assertions.assertEquals(Calendar.OCTOBER, cal2.get(Calendar.MONTH));
+        Assertions.assertEquals(30, cal2.get(Calendar.MINUTE));
+    }
+
+    @Test
+    void test_parseLocalDateTime() {
+        String dateStr = "2026-10-01 12:30:45";
+        String format = "yyyy-MM-dd HH:mm:ss";
+        LocalDateTime usResult = DateUtils.parseLocalDateTime(dateStr, format, 
Locale.US);
+
+        Assertions.assertEquals(2026, usResult.getYear());
+        Assertions.assertEquals(10, usResult.getMonthValue());
+        Assertions.assertEquals(1, usResult.getDayOfMonth());
+        Assertions.assertEquals(12, usResult.getHour());
+        Assertions.assertEquals(30, usResult.getMinute());
+        Assertions.assertEquals(45, usResult.getSecond());
+
+        LocalDateTime result = DateUtils.parseLocalDateTime(dateStr, format, 
null);
+
+        Assertions.assertEquals(2026, result.getYear());
+        Assertions.assertEquals(10, result.getMonthValue());
+        Assertions.assertEquals(1, result.getDayOfMonth());
+        Assertions.assertEquals(12, result.getHour());
+        Assertions.assertEquals(30, result.getMinute());
+        Assertions.assertEquals(45, result.getSecond());
+
+        LocalDateTime autoDetectFormatResult = 
DateUtils.parseLocalDateTime(dateStr, "", null);
+
+        Assertions.assertEquals(2026, autoDetectFormatResult.getYear());
+        Assertions.assertEquals(10, autoDetectFormatResult.getMonthValue());
+        Assertions.assertEquals(1, autoDetectFormatResult.getDayOfMonth());
+        Assertions.assertEquals(12, autoDetectFormatResult.getHour());
+        Assertions.assertEquals(30, autoDetectFormatResult.getMinute());
+        Assertions.assertEquals(45, autoDetectFormatResult.getSecond());
+    }
+
+    @Test
+    void test_parseLocalDate() {
+        String dateStr = "2026-10-01";
+        String format = "yyyy-MM-dd";
+        LocalDate usResult = DateUtils.parseLocalDate(dateStr, format, 
Locale.US);
+
+        Assertions.assertEquals(2026, usResult.getYear());
+        Assertions.assertEquals(10, usResult.getMonthValue());
+        Assertions.assertEquals(1, usResult.getDayOfMonth());
+
+        LocalDate result = DateUtils.parseLocalDate(dateStr, format, null);
+
+        Assertions.assertEquals(2026, result.getYear());
+        Assertions.assertEquals(10, result.getMonthValue());
+        Assertions.assertEquals(1, result.getDayOfMonth());
+
+        LocalDate autoDetectFormatResult = DateUtils.parseLocalDate(dateStr, 
"", null);
+
+        Assertions.assertEquals(2026, autoDetectFormatResult.getYear());
+        Assertions.assertEquals(10, autoDetectFormatResult.getMonthValue());
+        Assertions.assertEquals(1, autoDetectFormatResult.getDayOfMonth());
+    }
+
+    @Test
+    void test_format_default() {
+        Date now = new Date();
+        String result = DateUtils.format(now);
+        Assertions.assertNotNull(result);
+        // yyyy-MM-dd HH:mm:ss
+        Assertions.assertEquals(19, result.length());
+
+        Assertions.assertNull(DateUtils.format(null));
+    }
+
+    @Test
+    void test_format_LocalDateTime() {
+        LocalDateTime ldt = LocalDateTime.of(2026, 10, 1, 12, 0, 0);
+        String format = "dd-MMM-yyyy";
+
+        String usResult = DateUtils.format(ldt, format, Locale.US);
+        Assertions.assertEquals("01-Oct-2026", usResult);
+
+        String cnResult = DateUtils.format(ldt, format, 
Locale.SIMPLIFIED_CHINESE);
+        Assertions.assertNotNull(cnResult);
+
+        Assertions.assertNull(DateUtils.format((LocalDateTime) null, format, 
Locale.US));
+
+        String defaultUSResult = DateUtils.format(ldt, "", Locale.US);
+        Assertions.assertEquals("2026-10-01 12:00:00", defaultUSResult);
+    }
+
+    @Test
+    void test_format_LocalDate() {
+        LocalDate ld = LocalDate.of(2026, 10, 1);
+        String format = "dd-MMM-yyyy";
+
+        String usResult = DateUtils.format(ld, format, Locale.US);
+        Assertions.assertEquals("01-Oct-2026", usResult);
+
+        String cnResult = DateUtils.format(ld, format, 
Locale.SIMPLIFIED_CHINESE);
+        Assertions.assertNotNull(cnResult);
+
+        Assertions.assertNull(DateUtils.format((LocalDate) null, format, 
Locale.US));
+
+        String defaultUSResult = DateUtils.format(ld, "", Locale.US);
+        Assertions.assertEquals("2026-10-01", defaultUSResult);
+
+        String defaultFormatResult = DateUtils.format(ld, "", null);
+        Assertions.assertEquals("2026-10-01", defaultFormatResult);
+
+        String defaultFormatResult2 = DateUtils.format(ld, "");
+        Assertions.assertEquals("2026-10-01", defaultFormatResult2);
+    }
+
+    @Test
+    void test_format_BigDecimal() {
+        // 43831 = 2020-01-01
+        BigDecimal excelDate = new BigDecimal("43831.5");
+
+        String result = DateUtils.format(excelDate, false, 
DateUtils.DATE_FORMAT_19);
+        Assertions.assertNotNull(result);
+        Assertions.assertTrue(result.contains("2020-01-01"));
+
+        Assertions.assertNull(DateUtils.format(null, false, 
DateUtils.DATE_FORMAT_19));
+    }
+
+    @ParameterizedTest
+    @CsvSource({
+        "1.0, 1900-01-01 00:00:00",
+        "32.0, 1900-02-01 00:00:00",
+        "61.0, 1900-03-01 00:00:00",
+        "43831.5, 2020-01-01 12:00:00"
+    })
+    void test_getJavaDate_1900(double excelValue, String expectedStr) throws 
ParseException {
+        Date date = DateUtils.getJavaDate(excelValue, false);
+
+        SimpleDateFormat sdf = new SimpleDateFormat(DateUtils.DATE_FORMAT_19);
+        sdf.setTimeZone(TimeZone.getDefault());
+
+        Assertions.assertEquals(expectedStr, sdf.format(date));
+    }
+
+    @ParameterizedTest
+    @CsvSource({"0.0, 1904-01-01 00:00:00", "1.0, 1904-01-02 00:00:00", 
"42369.5, 2020-01-01 12:00:00"})
+    void test_getJavaDate_1904(double excelValue, String expectedStr) {
+        Date date = DateUtils.getJavaDate(excelValue, true);
+
+        SimpleDateFormat sdf = new SimpleDateFormat(DateUtils.DATE_FORMAT_19);
+        sdf.setTimeZone(TimeZone.getDefault());
+
+        Assertions.assertEquals(expectedStr, sdf.format(date));
+    }
+
+    @ParameterizedTest
+    @CsvSource({
+        "1.0, 1900-01-01 00:00:00",
+        "32.0, 1900-02-01 00:00:00",
+        "61.0, 1900-03-01 00:00:00",
+        "43831.5, 2020-01-01 12:00:00"
+    })
+    void test_getLocalDateTime_1900(double excelValue, String expectedStr) {
+        LocalDateTime date = DateUtils.getLocalDateTime(excelValue, false);
+        String formatted = date.atZone(TimeZone.getDefault().toZoneId())
+                .format(DateTimeFormatter.ofPattern(DateUtils.DATE_FORMAT_19));
+
+        Assertions.assertEquals(expectedStr, formatted);
+    }
+
+    @ParameterizedTest
+    @CsvSource({"0.0, 1904-01-01 00:00:00", "1.0, 1904-01-02 00:00:00", 
"42369.5, 2020-01-01 12:00:00"})
+    void test_getLocalDateTime_1904(double excelValue, String expectedStr) {
+        LocalDateTime date = DateUtils.getLocalDateTime(excelValue, true);
+
+        String formatted = date.atZone(TimeZone.getDefault().toZoneId())
+                .format(DateTimeFormatter.ofPattern(DateUtils.DATE_FORMAT_19));
+
+        Assertions.assertEquals(expectedStr, formatted);
+    }
+
+    @ParameterizedTest
+    @CsvSource({"1.0, 1900-01-01", "32.0, 1900-02-01", "61.0, 1900-03-01", 
"43831.5, 2020-01-01"})
+    void test_getLocalDate_1900(double excelValue, String expectedStr) {
+        LocalDate date = DateUtils.getLocalDate(excelValue, false);
+        Assertions.assertNotNull(date);
+
+        String formatted = 
date.format(DateTimeFormatter.ofPattern(DateUtils.DATE_FORMAT_10));
+        Assertions.assertEquals(expectedStr, formatted);
+    }
+
+    @ParameterizedTest
+    @CsvSource({"0.0, 1904-01-01", "1.0, 1904-01-02", "42369.5, 2020-01-01"})
+    void test_getLocalDate_1904(double excelValue, String expectedStr) {
+        LocalDate date = DateUtils.getLocalDate(excelValue, true);
+        Assertions.assertNotNull(date);
+
+        String formatted = 
date.format(DateTimeFormatter.ofPattern(DateUtils.DATE_FORMAT_10));
+        Assertions.assertEquals(expectedStr, formatted);
+    }
+
+    @Test
+    void test_isValidExcelDate() {
+        Assertions.assertTrue(DateUtils.isValidExcelDate(0.0));
+        Assertions.assertTrue(DateUtils.isValidExcelDate(100.0));
+        Assertions.assertFalse(DateUtils.isValidExcelDate(-1.0));
+    }
+
+    @Test
+    void test_getJavaCalendar_rounding() {
+        double base = 44000.0;
+        double halfDay = 0.5;
+
+        Calendar cal = DateUtils.getJavaCalendar(base + halfDay, false, null, 
true);
+        Assertions.assertNotNull(cal);
+        Assertions.assertEquals(12, cal.get(Calendar.HOUR_OF_DAY));
+        Assertions.assertEquals(0, cal.get(Calendar.SECOND));
+        Assertions.assertEquals(0, cal.get(Calendar.MILLISECOND));
+    }
+
+    @ParameterizedTest
+    @ValueSource(shorts = {0x0e, 0x0f, 0x16, 0x2d, 0x2f})
+    void test_isADateFormat_internal(short formatId) {
+        Assertions.assertTrue(DateUtils.isADateFormat(formatId, null));
+        Assertions.assertTrue(DateUtils.isADateFormat(formatId, "General"));
+    }
+
+    @ParameterizedTest
+    @ValueSource(
+            strings = {
+                "yyyy-mm-dd",
+                "mm/dd/yy",
+                "hh:mm:ss",
+                "yyyy年mm月dd日",
+                "[$-F800]dddd\\,\\ mmmm\\ dd\\,\\ yyyy",
+                "[DBNum1]yyyy年mm月dd日",
+                "yyyy/mm/dd;@",
+                "[h]:mm:ss",
+                "mm:ss.0",
+                "yyyy-MM-dd HH:mm:ss"
+            })
+    void test_isADateFormat_true(String formatString) {
+        Assertions.assertTrue(DateUtils.isADateFormat((short) 100, 
formatString));
+    }
+
+    @ParameterizedTest
+    @ValueSource(strings = {"General", "0.00", "#", "#,##0", "0%", "@", "_-* 
#,##0_-", ""})
+    void test_isADateFormat_false(String formatString) {
+        Assertions.assertFalse(DateUtils.isADateFormat((short) 100, 
formatString));
+
+        Assertions.assertFalse(DateUtils.isADateFormat(null, formatString));
+    }
+
+    @Test
+    void test_isADateFormat_Cache() throws NoSuchFieldException, 
IllegalAccessException {
+        short formatId = 200;
+        String formatStr = "yyyy-MM-dd";
+
+        boolean res1 = DateUtils.isADateFormat(formatId, formatStr);
+        Assertions.assertTrue(res1);
+
+        Field threadLocalField = 
DateUtils.class.getDeclaredField("DATE_THREAD_LOCAL");
+        threadLocalField.setAccessible(true);
+        ThreadLocal<Map<Short, Boolean>> tl = (ThreadLocal<Map<Short, 
Boolean>>) threadLocalField.get(null);
+
+        Map<Short, Boolean> cache = tl.get();
+        Assertions.assertNotNull(cache);
+        Assertions.assertTrue(cache.containsKey(formatId));
+        Assertions.assertTrue(cache.get(formatId));
+
+        boolean res2 = DateUtils.isADateFormat(formatId, formatStr);
+        Assertions.assertTrue(res2);
+    }
+
+    @Test
+    void test_removeThreadLocalCache() throws NoSuchFieldException, 
IllegalAccessException {
+        DateUtils.format(new Date(), "yyyy-MM-dd");
+        DateUtils.isADateFormat((short) 100, "yyyy-MM-dd");
+
+        Field f1 = DateUtils.class.getDeclaredField("DATE_THREAD_LOCAL");
+        Field f2 = 
DateUtils.class.getDeclaredField("DATE_FORMAT_THREAD_LOCAL");
+        f1.setAccessible(true);
+        f2.setAccessible(true);
+
+        Assertions.assertNotNull(((ThreadLocal<?>) f1.get(null)).get());
+        Assertions.assertNotNull(((ThreadLocal<?>) f2.get(null)).get());
+
+        DateUtils.removeThreadLocalCache();
+
+        Assertions.assertNull(((ThreadLocal<?>) f1.get(null)).get());
+        Assertions.assertNull(((ThreadLocal<?>) f2.get(null)).get());
+    }
+}
diff --git 
a/fesod-sheet/src/test/java/org/apache/fesod/sheet/util/FieldUtilsTest.java 
b/fesod-sheet/src/test/java/org/apache/fesod/sheet/util/FieldUtilsTest.java
new file mode 100644
index 00000000..7c4817a5
--- /dev/null
+++ b/fesod-sheet/src/test/java/org/apache/fesod/sheet/util/FieldUtilsTest.java
@@ -0,0 +1,242 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.fesod.sheet.util;
+
+import java.lang.reflect.Field;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.stream.Stream;
+import org.apache.fesod.shaded.cglib.beans.BeanMap;
+import org.apache.fesod.sheet.metadata.NullObject;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+import org.mockito.Mockito;
+
+/**
+ * Tests {@link FieldUtils}
+ */
+class FieldUtilsTest {
+
+    @Test
+    void test_getFieldClass_normalValue() {
+        Class<?> clazz = FieldUtils.getFieldClass(null, "any", "StringValue");
+
+        Assertions.assertEquals(String.class, clazz);
+    }
+
+    @Test
+    void test_getFieldClass_nullValue() {
+        Class<?> clazz = FieldUtils.getFieldClass(null, "any", null);
+
+        Assertions.assertEquals(NullObject.class, clazz);
+    }
+
+    @Test
+    void test_getFieldClass_BeanMap() {
+        BeanMap mockBeanMap = Mockito.mock(BeanMap.class);
+        
Mockito.when(mockBeanMap.getPropertyType("name")).thenReturn(Integer.class);
+
+        Class<?> clazz = FieldUtils.getFieldClass(mockBeanMap, "name", "123");
+
+        Assertions.assertEquals(Integer.class, clazz);
+    }
+
+    @Test
+    void test_getFieldClass_BeanMap_fallback() {
+        BeanMap mockBeanMap = Mockito.mock(BeanMap.class);
+        Mockito.when(mockBeanMap.getPropertyType("unknown")).thenReturn(null);
+
+        Class<?> clazz = FieldUtils.getFieldClass(mockBeanMap, "unknown", 
"Value");
+
+        Assertions.assertEquals(String.class, clazz);
+    }
+
+    @Test
+    void test_getFieldClass_normalMap() {
+        Map<String, Object> map = new HashMap<>();
+        Class<?> clazz = FieldUtils.getFieldClass(map, "any", 100L);
+
+        Assertions.assertEquals(Long.class, clazz);
+    }
+
+    @Test
+    void test_resolveCglibFieldName_nullField() {
+        Assertions.assertNull(FieldUtils.resolveCglibFieldName(null));
+    }
+
+    @ParameterizedTest
+    @MethodSource("cglibNameProvider")
+    void test_resolveCglibFieldName_resolveLogic(String fieldName, String 
expected) throws NoSuchFieldException {
+        Field field = FieldNameFixture.class.getDeclaredField(fieldName);
+
+        String result = FieldUtils.resolveCglibFieldName(field);
+        Assertions.assertEquals(expected, result);
+    }
+
+    static class FieldNameFixture {
+        private String a;
+        private String A;
+        private String ab;
+        private String AB;
+        private String string1;
+        private String STRING5;
+        private String String2;
+        private String sTring3;
+        private String aBc;
+    }
+
+    static Stream<Arguments> cglibNameProvider() {
+        return Stream.of(
+                Arguments.of("a", "a"),
+                Arguments.of("A", "A"),
+                // lower, lower
+                Arguments.of("ab", "ab"),
+                // Upper, Upper
+                Arguments.of("AB", "AB"),
+                // s(l), t(l) -> keep
+                Arguments.of("string1", "string1"),
+                // S(U), T(U) -> keep
+                Arguments.of("STRING5", "STRING5"),
+                // String2 -> string2
+                Arguments.of("String2", "string2"),
+                // sTring3 -> STring3
+                Arguments.of("sTring3", "STring3"),
+                // aBc -> a(l), B(U) -> A(U) + Bc -> ABc
+                Arguments.of("aBc", "ABc"));
+    }
+
+    @Test
+    void test_getField_nullClass() {
+        Assertions.assertThrows(IllegalArgumentException.class, () -> 
FieldUtils.getField(null, "any", true));
+    }
+
+    interface InterfaceA {
+        String interfaceField = "I_A";
+    }
+
+    interface InterfaceB {
+        String interfaceField = "I_B";
+    }
+
+    static class Parent {
+        public String parentPublic;
+        protected String parentProtected;
+        private String parentPrivate;
+    }
+
+    static class Child extends Parent implements InterfaceA {
+        public String childPublic;
+        private String childPrivate;
+    }
+
+    static class AmbiguousChild implements InterfaceA, InterfaceB {}
+
+    @Test
+    void test_getField_blankName() {
+        Assertions.assertThrows(IllegalArgumentException.class, () -> 
FieldUtils.getField(Child.class, "  ", true));
+    }
+
+    @Test
+    void test_getField_self_public() {
+        Field field = FieldUtils.getField(Child.class, "childPublic", false);
+        Assertions.assertNotNull(field);
+        Assertions.assertEquals("childPublic", field.getName());
+    }
+
+    @Test
+    void test_getField_self_private_force() {
+        Field field = FieldUtils.getField(Child.class, "childPrivate", true);
+        Assertions.assertNotNull(field);
+        Assertions.assertTrue(field.isAccessible());
+    }
+
+    @Test
+    void test_getField_self_private_noForce() {
+        Field field = FieldUtils.getField(Child.class, "childPrivate", false);
+
+        Assertions.assertNull(field);
+    }
+
+    @Test
+    void test_getField_super_public() {
+        Field field = FieldUtils.getField(Child.class, "parentPublic", false);
+        Assertions.assertNotNull(field);
+        Assertions.assertEquals(Parent.class, field.getDeclaringClass());
+    }
+
+    @Test
+    void test_getField_super_private_force() {
+        Field field = FieldUtils.getField(Child.class, "parentPrivate", true);
+
+        Assertions.assertNotNull(field);
+        Assertions.assertEquals("parentPrivate", field.getName());
+        Assertions.assertEquals(Parent.class, field.getDeclaringClass());
+    }
+
+    @Test
+    void test_getField_interface_field() {
+        Field field = FieldUtils.getField(Child.class, "interfaceField", 
false);
+        Assertions.assertNotNull(field);
+        Assertions.assertEquals(InterfaceA.class, field.getDeclaringClass());
+    }
+
+    @Test
+    void test_getField_notFound() {
+        Field field = FieldUtils.getField(Child.class, "notExists", true);
+        Assertions.assertNull(field);
+    }
+
+    @Test
+    void test_getField_ambiguous_interface() {
+        try {
+            FieldUtils.getField(AmbiguousChild.class, "interfaceField", true);
+        } catch (IllegalArgumentException e) {
+            Assertions.assertTrue(e.getMessage().contains("ambiguous"));
+        } catch (NoClassDefFoundError e) {
+        }
+    }
+
+    public static class PublicTarget {
+        public String publicField;
+    }
+
+    static class PackagePrivateTarget {
+        public String publicField;
+    }
+
+    @Test
+    void test_getField_publicClass_publicField() {
+        Field field = FieldUtils.getField(PublicTarget.class, "publicField");
+
+        Assertions.assertNotNull(field);
+        Assertions.assertFalse(field.isAccessible());
+    }
+
+    @Test
+    void test_getField_packagePrivateClass_publicField() {
+        Field field = FieldUtils.getField(PackagePrivateTarget.class, 
"publicField");
+
+        Assertions.assertNotNull(field);
+        Assertions.assertTrue(field.isAccessible());
+    }
+}
diff --git 
a/fesod-sheet/src/test/java/org/apache/fesod/sheet/util/FileUtilsTest.java 
b/fesod-sheet/src/test/java/org/apache/fesod/sheet/util/FileUtilsTest.java
new file mode 100644
index 00000000..87a60203
--- /dev/null
+++ b/fesod-sheet/src/test/java/org/apache/fesod/sheet/util/FileUtilsTest.java
@@ -0,0 +1,308 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.fesod.sheet.util;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.Field;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.UUID;
+import org.apache.fesod.sheet.exception.ExcelAnalysisException;
+import org.apache.fesod.sheet.exception.ExcelCommonException;
+import org.apache.poi.util.TempFile;
+import org.apache.poi.util.TempFileCreationStrategy;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
+import org.mockito.Mockito;
+
+/**
+ * Tests {@link FileUtils}
+ */
+class FileUtilsTest {
+
+    @TempDir
+    Path tempDir;
+
+    private String originalTempPrefix;
+    private String originalPoiFilesPath;
+    private String originalCachePath;
+
+    @BeforeEach
+    void setUp() {
+        originalTempPrefix = FileUtils.getTempFilePrefix();
+        originalPoiFilesPath = FileUtils.getPoiFilesPath();
+        originalCachePath = FileUtils.getCachePath();
+    }
+
+    @AfterEach
+    void tearDown() {
+        FileUtils.setTempFilePrefix(originalTempPrefix);
+        FileUtils.setPoiFilesPath(originalPoiFilesPath);
+        FileUtils.setCachePath(originalCachePath);
+    }
+
+    @Test
+    void test_ReadWrite_loop() throws IOException {
+        String content = "Hello, this is a test string.";
+        File targetFile = tempDir.resolve("test_rw.txt").toFile();
+        ByteArrayInputStream inputStream = new 
ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8));
+
+        FileUtils.writeToFile(targetFile, inputStream);
+
+        Assertions.assertTrue(targetFile.exists());
+        Assertions.assertTrue(targetFile.length() > 0);
+
+        byte[] readBytes = FileUtils.readFileToByteArray(targetFile);
+        String readContent = new String(readBytes, StandardCharsets.UTF_8);
+
+        Assertions.assertEquals(content, readContent);
+    }
+
+    @Test
+    void test_writeToFile_exception() {
+        InputStream badStream = new ByteArrayInputStream("test".getBytes());
+        try {
+            badStream.close();
+        } catch (IOException ignored) {
+        }
+
+        File directoryAsFile = tempDir.resolve("subdir").toFile();
+        directoryAsFile.mkdir();
+
+        InputStream validStream = new ByteArrayInputStream("test".getBytes());
+
+        Assertions.assertThrows(ExcelAnalysisException.class, () -> {
+            FileUtils.writeToFile(directoryAsFile, validStream);
+        });
+    }
+
+    @Test
+    void test_finally_inputStream_shouldClose() throws IOException {
+        File file = tempDir.resolve("close_test.txt").toFile();
+        InputStream mockInputStream = Mockito.mock(InputStream.class);
+        Mockito.when(mockInputStream.read(Mockito.any(), Mockito.anyInt(), 
Mockito.anyInt()))
+                .thenReturn(-1);
+
+        FileUtils.writeToFile(file, mockInputStream, true);
+
+        Mockito.verify(mockInputStream).close();
+    }
+
+    @Test
+    void test_finally_inputStream_shouldNotClose() throws IOException {
+        File file = tempDir.resolve("noclose_test.txt").toFile();
+        InputStream mockInputStream = Mockito.mock(InputStream.class);
+        Mockito.when(mockInputStream.read(Mockito.any(), Mockito.anyInt(), 
Mockito.anyInt()))
+                .thenReturn(-1);
+
+        FileUtils.writeToFile(file, mockInputStream, false);
+
+        Mockito.verify(mockInputStream, Mockito.never()).close();
+    }
+
+    @Test
+    void test_finally_inputStream_closeException() throws IOException {
+        File file = tempDir.resolve("ex_test.txt").toFile();
+        InputStream mockInputStream = Mockito.mock(InputStream.class);
+        Mockito.when(mockInputStream.read(Mockito.any(), Mockito.anyInt(), 
Mockito.anyInt()))
+                .thenReturn(-1);
+
+        Mockito.doThrow(new IOException("Close 
failed")).when(mockInputStream).close();
+
+        Assertions.assertThrows(ExcelAnalysisException.class, () -> {
+            FileUtils.writeToFile(file, mockInputStream, true);
+        });
+    }
+
+    @Test
+    void test_openInputStream_notFound() {
+        File nonExistentFile = 
tempDir.resolve(UUID.randomUUID().toString()).toFile();
+
+        Assertions.assertThrows(FileNotFoundException.class, () -> {
+            FileUtils.openInputStream(nonExistentFile);
+        });
+    }
+
+    @Test
+    void test_openInputStream_isDirectory() {
+        File dir = tempDir.resolve("test_dir").toFile();
+        dir.mkdirs();
+
+        IOException ex = Assertions.assertThrows(IOException.class, () -> {
+            FileUtils.openInputStream(dir);
+        });
+        Assertions.assertTrue(ex.getMessage().contains("exists but is a 
directory"));
+    }
+
+    @Test
+    void test_openInputStream_noRead() {
+        File fileMocked = Mockito.mock(File.class);
+
+        Mockito.when(fileMocked.exists()).thenReturn(true);
+        Mockito.when(fileMocked.isDirectory()).thenReturn(false);
+        Mockito.when(fileMocked.canRead()).thenReturn(false);
+        Mockito.when(fileMocked.toString()).thenReturn("/path/to/locked_file");
+
+        Assertions.assertThrows(IOException.class, () -> {
+            FileUtils.openInputStream(fileMocked);
+        });
+    }
+
+    @Test
+    void test_createDirectory() {
+        File nestedDir = tempDir.resolve("a/b/c").toFile();
+
+        File result = FileUtils.createDirectory(nestedDir);
+
+        Assertions.assertTrue(result.exists());
+        Assertions.assertTrue(result.isDirectory());
+    }
+
+    @Test
+    void test_createDirectory_exception() {
+        File dirMocked = Mockito.mock(File.class);
+
+        Mockito.when(dirMocked.exists()).thenReturn(false);
+        Mockito.when(dirMocked.mkdirs()).thenReturn(false);
+        Mockito.when(dirMocked.toString()).thenReturn("/path/to/locked_file");
+
+        Assertions.assertThrows(ExcelCommonException.class, () -> {
+            FileUtils.createDirectory(dirMocked);
+        });
+    }
+
+    @Test
+    void test_createTmpFile() {
+        String fileName = "my_temp.xlsx";
+        File tmpFile = FileUtils.createTmpFile(fileName);
+
+        Assertions.assertNotNull(tmpFile);
+        Assertions.assertFalse(tmpFile.exists());
+        Assertions.assertEquals(fileName, tmpFile.getName());
+
+        Assertions.assertTrue(tmpFile.getParentFile().exists());
+    }
+
+    @Test
+    void test_createCacheTmpFile() {
+        File cacheDir = FileUtils.createCacheTmpFile();
+
+        Assertions.assertNotNull(cacheDir);
+        Assertions.assertTrue(cacheDir.exists());
+        Assertions.assertTrue(cacheDir.isDirectory());
+        
Assertions.assertTrue(cacheDir.getAbsolutePath().contains(FileUtils.EX_CACHE));
+    }
+
+    @Test
+    void test_delete_recursive() throws IOException {
+        // root/
+        //   - file1.txt
+        //   - sub/
+        //     - file2.txt
+        File root = tempDir.resolve("root").toFile();
+        File sub = new File(root, "sub");
+        FileUtils.createDirectory(sub);
+
+        File file1 = new File(root, "file1.txt");
+        File file2 = new File(sub, "file2.txt");
+
+        Files.write(file1.toPath(), "content".getBytes());
+        Files.write(file2.toPath(), "content".getBytes());
+
+        Assertions.assertTrue(file1.exists());
+        Assertions.assertTrue(file2.exists());
+
+        FileUtils.delete(root);
+
+        Assertions.assertFalse(root.exists());
+        Assertions.assertFalse(file1.exists());
+        Assertions.assertFalse(sub.exists());
+        Assertions.assertFalse(file2.exists());
+    }
+
+    @Test
+    void test_delete_singleFile() throws IOException {
+        File file = tempDir.resolve("single.txt").toFile();
+        file.createNewFile();
+
+        FileUtils.delete(file);
+
+        Assertions.assertFalse(file.exists());
+    }
+
+    @Test
+    void test_delete_directory() {
+        File root = tempDir.resolve("root").toFile();
+        FileUtils.createDirectory(root);
+
+        Assertions.assertTrue(root.exists());
+
+        FileUtils.delete(root);
+
+        Assertions.assertFalse(root.exists());
+    }
+
+    @Test
+    void test_GetterSetter() {
+        String originalPrefix = FileUtils.getTempFilePrefix();
+        String newPrefix = tempDir.toString() + File.separator;
+
+        try {
+            FileUtils.setTempFilePrefix(newPrefix);
+            Assertions.assertEquals(newPrefix, FileUtils.getTempFilePrefix());
+
+            FileUtils.setCachePath(newPrefix + "cache");
+            Assertions.assertEquals(newPrefix + "cache", 
FileUtils.getCachePath());
+
+            FileUtils.setPoiFilesPath(newPrefix + "poi");
+            Assertions.assertEquals(newPrefix + "poi", 
FileUtils.getPoiFilesPath());
+        } finally {
+            FileUtils.setTempFilePrefix(originalPrefix);
+        }
+    }
+
+    @Test
+    void test_createPoiFilesDirectory() throws NoSuchFieldException, 
IllegalAccessException {
+        Field strategyField = TempFile.class.getDeclaredField("strategy");
+        strategyField.setAccessible(true);
+
+        TempFileCreationStrategy originalStrategy = (TempFileCreationStrategy) 
strategyField.get(null);
+
+        try {
+            FileUtils.createPoiFilesDirectory();
+
+            TempFileCreationStrategy newStrategy = (TempFileCreationStrategy) 
strategyField.get(null);
+
+            Assertions.assertNotNull(newStrategy);
+            Assertions.assertEquals(FesodTempFileCreationStrategy.class, 
newStrategy.getClass());
+            Assertions.assertNotSame(originalStrategy, newStrategy);
+        } finally {
+            TempFile.setTempFileCreationStrategy(originalStrategy);
+        }
+    }
+}
diff --git 
a/fesod-sheet/src/test/java/org/apache/fesod/sheet/util/NumberDataFormatterUtilsTest.java
 
b/fesod-sheet/src/test/java/org/apache/fesod/sheet/util/NumberDataFormatterUtilsTest.java
new file mode 100644
index 00000000..3c0133d0
--- /dev/null
+++ 
b/fesod-sheet/src/test/java/org/apache/fesod/sheet/util/NumberDataFormatterUtilsTest.java
@@ -0,0 +1,110 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.fesod.sheet.util;
+
+import java.lang.reflect.Field;
+import java.math.BigDecimal;
+import java.util.Locale;
+import org.apache.fesod.sheet.metadata.GlobalConfiguration;
+import org.apache.fesod.sheet.metadata.format.DataFormatter;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+/**
+ * Tests {@link NumberDataFormatterUtils}
+ */
+@ExtendWith(MockitoExtension.class)
+class NumberDataFormatterUtilsTest {
+
+    @Mock
+    private GlobalConfiguration globalConfiguration;
+
+    @AfterEach
+    void tearDown() {
+        NumberDataFormatterUtils.removeThreadLocalCache();
+    }
+
+    @Test
+    void test_format_withConfig_Locale() {
+        // Setup
+        
Mockito.when(globalConfiguration.getLocale()).thenReturn(Locale.GERMANY);
+        
Mockito.when(globalConfiguration.getUse1904windowing()).thenReturn(false);
+        
Mockito.when(globalConfiguration.getUseScientificFormat()).thenReturn(false);
+
+        BigDecimal data = new BigDecimal("1234.56");
+        String formatString = "0.00";
+
+        // Execute
+        String result = NumberDataFormatterUtils.format(data, null, 
formatString, globalConfiguration);
+
+        // Verify
+        Assertions.assertEquals("1234,56", result);
+    }
+
+    @Test
+    void test_format_nullConfig() {
+        BigDecimal data = new BigDecimal("1234.56");
+        String formatString = "0.00";
+
+        String result = NumberDataFormatterUtils.format(data, null, 
formatString, null);
+
+        Assertions.assertNotNull(result);
+        Assertions.assertTrue(result.contains("1234"));
+    }
+
+    @Test
+    void test_format_scientific() {
+        // 1.23E4 -> 12300
+        BigDecimal data = new BigDecimal("1.23E+4");
+        String formatString = "0";
+
+        String result = NumberDataFormatterUtils.format(data, null, 
formatString, false, Locale.US, false);
+
+        Assertions.assertEquals("12300", result);
+    }
+
+    @Test
+    void test_ThreadLocal_Cache_And_Remove() throws NoSuchFieldException, 
IllegalAccessException {
+        Field field = 
NumberDataFormatterUtils.class.getDeclaredField("DATA_FORMATTER_THREAD_LOCAL");
+        field.setAccessible(true);
+
+        @SuppressWarnings("unchecked")
+        ThreadLocal<DataFormatter> threadLocal = (ThreadLocal<DataFormatter>) 
field.get(null);
+
+        Assertions.assertNull(threadLocal.get());
+
+        NumberDataFormatterUtils.format(new BigDecimal("1"), null, "0", false, 
Locale.US, false);
+
+        DataFormatter cachedFormatter = threadLocal.get();
+        Assertions.assertNotNull(cachedFormatter);
+
+        NumberDataFormatterUtils.format(new BigDecimal("2"), null, "0", false, 
Locale.US, false);
+        Assertions.assertSame(cachedFormatter, threadLocal.get());
+
+        NumberDataFormatterUtils.removeThreadLocalCache();
+
+        Assertions.assertNull(threadLocal.get());
+    }
+}
diff --git 
a/fesod-sheet/src/test/java/org/apache/fesod/sheet/util/NumberUtilsTest.java 
b/fesod-sheet/src/test/java/org/apache/fesod/sheet/util/NumberUtilsTest.java
new file mode 100644
index 00000000..c4d6cb93
--- /dev/null
+++ b/fesod-sheet/src/test/java/org/apache/fesod/sheet/util/NumberUtilsTest.java
@@ -0,0 +1,270 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.fesod.sheet.util;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.text.ParseException;
+import org.apache.fesod.sheet.metadata.data.WriteCellData;
+import org.apache.fesod.sheet.metadata.property.ExcelContentProperty;
+import org.apache.fesod.sheet.metadata.property.NumberFormatProperty;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+/**
+ * Tests {@link NumberUtils}
+ */
+@ExtendWith(MockitoExtension.class)
+class NumberUtilsTest {
+
+    @Mock
+    private ExcelContentProperty contentProperty;
+
+    @Mock
+    private NumberFormatProperty numberFormatProperty;
+
+    @Test
+    void test_format_noFormat_BigDecimal() {
+        BigDecimal bigDecimal = new BigDecimal("0.0000001");
+
+        String result = NumberUtils.format(bigDecimal, null);
+
+        Assertions.assertEquals("0.0000001", result);
+    }
+
+    @Test
+    void test_format_noFormat_Integer() {
+        Integer num = 123;
+        String result = NumberUtils.format(num, null);
+
+        Assertions.assertEquals("123", result);
+    }
+
+    @Test
+    void test_format_withFormat() {
+        // Setup
+        
Mockito.when(contentProperty.getNumberFormatProperty()).thenReturn(numberFormatProperty);
+        Mockito.when(numberFormatProperty.getFormat()).thenReturn("#.00");
+        
Mockito.when(numberFormatProperty.getRoundingMode()).thenReturn(RoundingMode.UP);
+
+        // 123.451 -> UP -> 123.46
+        String result = NumberUtils.format(123.451, contentProperty);
+
+        Assertions.assertEquals("123.46", result);
+    }
+
+    @Test
+    void test_formatToCellDataString() {
+        Integer num = 100;
+        WriteCellData<?> data = NumberUtils.formatToCellDataString(num, null);
+
+        Assertions.assertEquals("100", data.getStringValue());
+    }
+
+    @Test
+    void test_formatToCellData_withFormat() {
+        // Setup
+        
Mockito.when(contentProperty.getNumberFormatProperty()).thenReturn(numberFormatProperty);
+        Mockito.when(numberFormatProperty.getFormat()).thenReturn("#,##0.00");
+
+        // Execute
+        WriteCellData<?> data = NumberUtils.formatToCellData(1234.56, 
contentProperty);
+
+        // Verify Value
+        Assertions.assertEquals(0, new 
BigDecimal("1234.56").compareTo(data.getNumberValue()));
+        Assertions.assertNotNull(data.getWriteCellStyle());
+        Assertions.assertNotNull(data.getWriteCellStyle().getDataFormatData());
+        Assertions.assertEquals(
+                "#,##0.00", 
data.getWriteCellStyle().getDataFormatData().getFormat());
+    }
+
+    @Test
+    void test_formatToCellData_noFormat() {
+        WriteCellData<?> data = NumberUtils.formatToCellData(99, null);
+
+        Assertions.assertEquals(0, new 
BigDecimal("99").compareTo(data.getNumberValue()));
+        Assertions.assertNull(data.getWriteCellStyle());
+    }
+
+    @Test
+    void test_parseInteger_noFormat() throws ParseException {
+        Integer result = NumberUtils.parseInteger("100", null);
+        Assertions.assertEquals(100, result);
+    }
+
+    @Test
+    void test_parseInteger_withFormat() throws ParseException {
+        // Setup
+        
Mockito.when(contentProperty.getNumberFormatProperty()).thenReturn(numberFormatProperty);
+        Mockito.when(numberFormatProperty.getFormat()).thenReturn("#");
+        
Mockito.when(numberFormatProperty.getRoundingMode()).thenReturn(RoundingMode.HALF_UP);
+
+        // Execute
+        Integer result = NumberUtils.parseInteger("99", contentProperty);
+
+        // Verify
+        Assertions.assertEquals(99, result);
+    }
+
+    @Test
+    void test_parseBigDecimal_noFormat() throws ParseException {
+        BigDecimal result = NumberUtils.parseBigDecimal("123.456789", null);
+        Assertions.assertEquals(new BigDecimal("123.456789"), result);
+    }
+
+    @Test
+    void test_parseBigDecimal_withFormat() throws ParseException {
+        // Setup
+        
Mockito.when(contentProperty.getNumberFormatProperty()).thenReturn(numberFormatProperty);
+        Mockito.when(numberFormatProperty.getFormat()).thenReturn("#,##0.00");
+        
Mockito.when(numberFormatProperty.getRoundingMode()).thenReturn(RoundingMode.HALF_UP);
+
+        // Execute: "1,234.56" -> 1234.56
+        BigDecimal result = NumberUtils.parseBigDecimal("1,234.56", 
contentProperty);
+
+        // Verify
+        Assertions.assertEquals(0, new 
BigDecimal("1234.56").compareTo(result));
+    }
+
+    @Test
+    void test_parseFloat_noFormat() throws ParseException {
+        Float result = NumberUtils.parseFloat("12.34", null);
+        Assertions.assertEquals(12.34f, result, 0.0001f);
+    }
+
+    @Test
+    void test_parseFloat_withFormat() throws ParseException {
+        // Setup
+        
Mockito.when(contentProperty.getNumberFormatProperty()).thenReturn(numberFormatProperty);
+        Mockito.when(numberFormatProperty.getFormat()).thenReturn("#.00");
+        
Mockito.when(numberFormatProperty.getRoundingMode()).thenReturn(RoundingMode.HALF_UP);
+
+        // Execute
+        Float result = NumberUtils.parseFloat("12.34", contentProperty);
+
+        // Verify
+        Assertions.assertEquals(12.34f, result, 0.0001f);
+    }
+
+    @Test
+    void test_parseDouble_noFormat() throws ParseException {
+        Double result = NumberUtils.parseDouble("123.456", null);
+        Assertions.assertEquals(123.456, result, 0.000001);
+    }
+
+    @Test
+    void test_parseDouble_withFormat() throws ParseException {
+        // Setup
+        
Mockito.when(contentProperty.getNumberFormatProperty()).thenReturn(numberFormatProperty);
+        // 12.34% -> 0.1234
+        Mockito.when(numberFormatProperty.getFormat()).thenReturn("0.00%");
+        
Mockito.when(numberFormatProperty.getRoundingMode()).thenReturn(RoundingMode.HALF_UP);
+
+        // Execute
+        Double result = NumberUtils.parseDouble("12.34%", contentProperty);
+
+        // Verify
+        Assertions.assertEquals(0.1234, result, 0.000001);
+    }
+
+    @Test
+    void test_parseLong_noFormat() throws ParseException {
+        Long result = NumberUtils.parseLong("123456789", null);
+
+        Assertions.assertEquals(123456789L, result);
+    }
+
+    @Test
+    void test_parseLong_withFormat() throws ParseException {
+        // Setup
+        
Mockito.when(contentProperty.getNumberFormatProperty()).thenReturn(numberFormatProperty);
+        Mockito.when(numberFormatProperty.getFormat()).thenReturn("#,###");
+        
Mockito.when(numberFormatProperty.getRoundingMode()).thenReturn(RoundingMode.HALF_UP);
+
+        // Execute: "1,234" -> 1234L
+        Long result = NumberUtils.parseLong("1,234", contentProperty);
+
+        // Verify
+        Assertions.assertEquals(1234L, result);
+    }
+
+    @Test
+    void test_parseByte_noFormat() throws ParseException {
+        Byte result = NumberUtils.parseByte("127", null);
+        Assertions.assertEquals((byte) 127, result);
+    }
+
+    @Test
+    void test_parseByte_withFormat() throws ParseException {
+        // Setup
+        
Mockito.when(contentProperty.getNumberFormatProperty()).thenReturn(numberFormatProperty);
+        Mockito.when(numberFormatProperty.getFormat()).thenReturn("#");
+        
Mockito.when(numberFormatProperty.getRoundingMode()).thenReturn(RoundingMode.HALF_UP);
+
+        // Execute
+        Byte result = NumberUtils.parseByte("100", contentProperty);
+
+        // Verify
+        Assertions.assertEquals((byte) 100, result);
+    }
+
+    @Test
+    void test_parseShort_withFormat() throws ParseException {
+        
Mockito.when(contentProperty.getNumberFormatProperty()).thenReturn(numberFormatProperty);
+        Mockito.when(numberFormatProperty.getFormat()).thenReturn("#");
+        
Mockito.when(numberFormatProperty.getRoundingMode()).thenReturn(RoundingMode.UP);
+
+        Short resultSimple = NumberUtils.parseShort("123", contentProperty);
+        Assertions.assertEquals((short) 123, resultSimple);
+    }
+
+    @Test
+    void test_parseShort_noFormat() throws ParseException {
+        Short resultNullProp = NumberUtils.parseShort("123", null);
+        Assertions.assertEquals((short) 123, resultNullProp);
+
+        Mockito.reset(contentProperty);
+        
Mockito.when(contentProperty.getNumberFormatProperty()).thenReturn(null);
+
+        Short resultNullFormat = NumberUtils.parseShort("456", 
contentProperty);
+        Assertions.assertEquals((short) 456, resultNullFormat);
+
+        
Mockito.when(contentProperty.getNumberFormatProperty()).thenReturn(numberFormatProperty);
+        Mockito.when(numberFormatProperty.getFormat()).thenReturn(null);
+
+        Short resultEmptyFormat = NumberUtils.parseShort("789", 
contentProperty);
+        Assertions.assertEquals((short) 789, resultEmptyFormat);
+    }
+
+    @Test
+    void test_parse_Error() {
+        
Mockito.when(contentProperty.getNumberFormatProperty()).thenReturn(numberFormatProperty);
+        Mockito.when(numberFormatProperty.getFormat()).thenReturn("#");
+        
Mockito.when(numberFormatProperty.getRoundingMode()).thenReturn(RoundingMode.UP);
+
+        Assertions.assertThrows(ParseException.class, () -> {
+            NumberUtils.parseInteger("not_a_number", contentProperty);
+        });
+    }
+}
diff --git 
a/fesod-sheet/src/test/java/org/apache/fesod/sheet/util/ParameterUtilTest.java 
b/fesod-sheet/src/test/java/org/apache/fesod/sheet/util/ParameterUtilTest.java
new file mode 100644
index 00000000..a182856b
--- /dev/null
+++ 
b/fesod-sheet/src/test/java/org/apache/fesod/sheet/util/ParameterUtilTest.java
@@ -0,0 +1,166 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.fesod.sheet.util;
+
+import org.apache.fesod.sheet.context.AnalysisContext;
+import org.apache.fesod.sheet.context.WriteContext;
+import org.apache.fesod.sheet.metadata.GlobalConfiguration;
+import org.apache.fesod.sheet.read.metadata.ReadSheet;
+import org.apache.fesod.sheet.read.metadata.holder.ReadWorkbookHolder;
+import org.apache.fesod.sheet.write.metadata.WriteSheet;
+import org.apache.fesod.sheet.write.metadata.holder.WriteWorkbookHolder;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+/**
+ * Tests {@link ParameterUtil}
+ */
+@ExtendWith(MockitoExtension.class)
+class ParameterUtilTest {
+
+    @Mock
+    private AnalysisContext analysisContext;
+
+    @Mock
+    private ReadWorkbookHolder readWorkbookHolder;
+
+    @Mock
+    private WriteContext writeContext;
+
+    @Mock
+    private WriteWorkbookHolder writeWorkbookHolder;
+
+    @Mock
+    private GlobalConfiguration globalConfiguration;
+
+    @Mock
+    private ReadSheet readSheet;
+
+    @Mock
+    private WriteSheet writeSheet;
+
+    @BeforeEach
+    void setUp() {
+        
Mockito.lenient().when(analysisContext.readWorkbookHolder()).thenReturn(readWorkbookHolder);
+        
Mockito.lenient().when(readWorkbookHolder.getGlobalConfiguration()).thenReturn(globalConfiguration);
+
+        
Mockito.lenient().when(writeContext.writeWorkbookHolder()).thenReturn(writeWorkbookHolder);
+        
Mockito.lenient().when(writeWorkbookHolder.getGlobalConfiguration()).thenReturn(globalConfiguration);
+    }
+
+    @Test
+    void test_AutoTrim_LocalTrue() {
+        Mockito.when(readSheet.getAutoTrim()).thenReturn(true);
+
+        Assertions.assertTrue(ParameterUtil.getAutoTrimFlag(readSheet, 
analysisContext));
+    }
+
+    @Test
+    void test_AutoTrim_LocalNull_GlobalTrue() {
+        Mockito.when(readSheet.getAutoTrim()).thenReturn(null);
+        Mockito.when(globalConfiguration.getAutoTrim()).thenReturn(true);
+
+        Assertions.assertTrue(ParameterUtil.getAutoTrimFlag(readSheet, 
analysisContext));
+    }
+
+    @Test
+    void test_AutoTrim_LocalNull_GlobalFalse() {
+        Mockito.when(readSheet.getAutoTrim()).thenReturn(null);
+        Mockito.when(globalConfiguration.getAutoTrim()).thenReturn(false);
+
+        Assertions.assertFalse(ParameterUtil.getAutoTrimFlag(readSheet, 
analysisContext));
+    }
+
+    @Test
+    void test_AutoTrim_LocalFalse_Override_Global() {
+        Mockito.when(readSheet.getAutoTrim()).thenReturn(false);
+        
Mockito.lenient().when(globalConfiguration.getAutoTrim()).thenReturn(true);
+
+        Assertions.assertFalse(ParameterUtil.getAutoTrimFlag(readSheet, 
analysisContext));
+    }
+
+    @Test
+    void test_AutoStrip_LocalTrue() {
+        Mockito.when(readSheet.getAutoStrip()).thenReturn(true);
+
+        Assertions.assertTrue(ParameterUtil.getAutoStripFlag(readSheet, 
analysisContext));
+    }
+
+    @Test
+    void test_AutoStrip_LocalNull_GlobalTrue() {
+        Mockito.when(readSheet.getAutoStrip()).thenReturn(null);
+        Mockito.when(globalConfiguration.getAutoStrip()).thenReturn(true);
+
+        Assertions.assertTrue(ParameterUtil.getAutoStripFlag(readSheet, 
analysisContext));
+    }
+
+    @Test
+    void test_AutoStrip_AllFalse() {
+        Mockito.when(readSheet.getAutoStrip()).thenReturn(false);
+        Mockito.when(globalConfiguration.getAutoStrip()).thenReturn(false);
+
+        Assertions.assertFalse(ParameterUtil.getAutoStripFlag(readSheet, 
analysisContext));
+    }
+
+    @Test
+    void test_AutoStrip_LocalFalse_Cannot_Override_Global() {
+        Mockito.when(readSheet.getAutoStrip()).thenReturn(false);
+        Mockito.when(globalConfiguration.getAutoStrip()).thenReturn(true);
+
+        Assertions.assertTrue(ParameterUtil.getAutoStripFlag(readSheet, 
analysisContext));
+    }
+
+    @Test
+    void test_AutoTrim() {
+        Mockito.when(writeSheet.getAutoTrim()).thenReturn(null);
+        Mockito.when(globalConfiguration.getAutoTrim()).thenReturn(true);
+
+        Assertions.assertTrue(ParameterUtil.getAutoTrimFlag(writeSheet, 
writeContext));
+    }
+
+    @Test
+    void test_AutoTrim_Override() {
+        Mockito.when(writeSheet.getAutoTrim()).thenReturn(false);
+        
Mockito.lenient().when(globalConfiguration.getAutoTrim()).thenReturn(true);
+
+        Assertions.assertFalse(ParameterUtil.getAutoTrimFlag(writeSheet, 
writeContext));
+    }
+
+    @Test
+    void test_AutoStrip_Fallback() {
+        Mockito.when(writeSheet.getAutoStrip()).thenReturn(null);
+        Mockito.when(globalConfiguration.getAutoStrip()).thenReturn(true);
+
+        Assertions.assertTrue(ParameterUtil.getAutoStripFlag(writeSheet, 
writeContext));
+    }
+
+    @Test
+    void test_AutoStrip_CannotOverride() {
+        Mockito.when(writeSheet.getAutoStrip()).thenReturn(false);
+        Mockito.when(globalConfiguration.getAutoStrip()).thenReturn(true);
+
+        Assertions.assertTrue(ParameterUtil.getAutoStripFlag(writeSheet, 
writeContext));
+    }
+}
diff --git 
a/fesod-sheet/src/test/java/org/apache/fesod/sheet/util/PoiUtilsTest.java 
b/fesod-sheet/src/test/java/org/apache/fesod/sheet/util/PoiUtilsTest.java
new file mode 100644
index 00000000..5a1f59f3
--- /dev/null
+++ b/fesod-sheet/src/test/java/org/apache/fesod/sheet/util/PoiUtilsTest.java
@@ -0,0 +1,81 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.fesod.sheet.util;
+
+import java.io.IOException;
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.ss.usermodel.Row;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.ss.usermodel.Workbook;
+import org.apache.poi.xssf.streaming.SXSSFWorkbook;
+import org.apache.poi.xssf.usermodel.XSSFRow;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests {@link PoiUtils}
+ */
+class PoiUtilsTest {
+
+    @Test
+    void test_customHeight_XSSF_true() throws IOException {
+        try (Workbook wb = new XSSFWorkbook()) {
+            Sheet sheet = wb.createSheet();
+            Row row = sheet.createRow(0);
+
+            if (row instanceof XSSFRow) {
+                ((XSSFRow) row).getCTRow().setCustomHeight(true);
+            }
+
+            Assertions.assertTrue(PoiUtils.customHeight(row));
+        }
+    }
+
+    @Test
+    void test_customHeight_XSSF_false() throws IOException {
+        try (Workbook wb = new XSSFWorkbook()) {
+            Sheet sheet = wb.createSheet();
+            Row row = sheet.createRow(0);
+
+            Assertions.assertFalse(PoiUtils.customHeight(row));
+        }
+    }
+
+    @Test
+    void test_customHeight_HSSF_false() throws IOException {
+        try (HSSFWorkbook wb = new HSSFWorkbook()) {
+            Sheet sheet = wb.createSheet();
+            Row row = sheet.createRow(0);
+
+            Assertions.assertFalse(PoiUtils.customHeight(row));
+        }
+    }
+
+    @Test
+    void test_customHeight_unsupportedType_false() throws IOException {
+        try (Workbook wb = new SXSSFWorkbook()) {
+            Sheet sheet = wb.createSheet();
+            Row row = sheet.createRow(0);
+
+            Assertions.assertFalse(PoiUtils.customHeight(row));
+        }
+    }
+}
diff --git 
a/fesod-sheet/src/test/java/org/apache/fesod/sheet/util/SheetUtilsTest.java 
b/fesod-sheet/src/test/java/org/apache/fesod/sheet/util/SheetUtilsTest.java
new file mode 100644
index 00000000..9ed8181c
--- /dev/null
+++ b/fesod-sheet/src/test/java/org/apache/fesod/sheet/util/SheetUtilsTest.java
@@ -0,0 +1,180 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.fesod.sheet.util;
+
+import java.util.Collections;
+import org.apache.fesod.sheet.context.AnalysisContext;
+import org.apache.fesod.sheet.metadata.GlobalConfiguration;
+import org.apache.fesod.sheet.read.metadata.ReadSheet;
+import org.apache.fesod.sheet.read.metadata.holder.ReadWorkbookHolder;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+/**
+ * Tests {@link SheetUtils}
+ */
+@ExtendWith(MockitoExtension.class)
+class SheetUtilsTest {
+
+    @Mock
+    private AnalysisContext analysisContext;
+
+    @Mock
+    private ReadWorkbookHolder readWorkbookHolder;
+
+    @Mock
+    private GlobalConfiguration globalConfiguration;
+
+    @Mock
+    private ReadSheet actualSheet;
+
+    @Mock
+    private ReadSheet paramSheet;
+
+    @BeforeEach
+    void setUp() {
+        
Mockito.lenient().when(analysisContext.readWorkbookHolder()).thenReturn(readWorkbookHolder);
+        
Mockito.lenient().when(readWorkbookHolder.getGlobalConfiguration()).thenReturn(globalConfiguration);
+    }
+
+    @Test
+    void test_match_withIgnoreHiddenSheet() {
+        // Setup
+        
Mockito.when(readWorkbookHolder.getIgnoreHiddenSheet()).thenReturn(true);
+        Mockito.when(actualSheet.isHidden()).thenReturn(true);
+
+        // Execute
+        ReadSheet result = SheetUtils.match(actualSheet, analysisContext);
+
+        Assertions.assertNull(result);
+    }
+
+    @Test
+    void test_match_withReadAll() {
+        // Setup
+        
Mockito.when(readWorkbookHolder.getIgnoreHiddenSheet()).thenReturn(false);
+        Mockito.when(readWorkbookHolder.getReadAll()).thenReturn(true);
+
+        // Execute
+        ReadSheet result = SheetUtils.match(actualSheet, analysisContext);
+
+        Assertions.assertEquals(actualSheet, result);
+    }
+
+    @Test
+    void test_match_withParameterSheetDataList_empty() {
+        // Setup
+        
Mockito.when(readWorkbookHolder.getIgnoreHiddenSheet()).thenReturn(false);
+        
Mockito.when(readWorkbookHolder.getParameterSheetDataList()).thenReturn(Collections.emptyList());
+
+        // Execute
+        ReadSheet result = SheetUtils.match(actualSheet, analysisContext);
+
+        Assertions.assertNull(result);
+    }
+
+    @Test
+    void test_match_ByName_FromSheet_RealObject() {
+        // Setup
+        
Mockito.when(readWorkbookHolder.getIgnoreHiddenSheet()).thenReturn(false);
+        ReadSheet realParamSheet = new ReadSheet();
+
+        Mockito.when(readWorkbookHolder.getParameterSheetDataList())
+                .thenReturn(Collections.singletonList(realParamSheet));
+
+        Mockito.when(actualSheet.getSheetNo()).thenReturn(0);
+
+        // Execute
+        ReadSheet result = SheetUtils.match(actualSheet, analysisContext);
+
+        // Verify
+        Assertions.assertEquals(actualSheet, result);
+        Assertions.assertEquals(0, realParamSheet.getSheetNo());
+        Mockito.verify(actualSheet).copyBasicParameter(realParamSheet);
+    }
+
+    @Test
+    void test_match_ByName_AutoStrip_FromSheet() {
+        // Setup
+        
Mockito.when(readWorkbookHolder.getIgnoreHiddenSheet()).thenReturn(false);
+        
Mockito.when(readWorkbookHolder.getParameterSheetDataList()).thenReturn(Collections.singletonList(paramSheet));
+        Mockito.when(paramSheet.getSheetNo()).thenReturn(99);
+        Mockito.when(actualSheet.getSheetNo()).thenReturn(100);
+        Mockito.when(paramSheet.getSheetName()).thenReturn("  SheetA  ");
+        Mockito.when(actualSheet.getSheetName()).thenReturn("SheetA");
+
+        Mockito.when(paramSheet.getAutoStrip()).thenReturn(true);
+
+        // Execute
+        ReadSheet result = SheetUtils.match(actualSheet, analysisContext);
+
+        // verify
+        Assertions.assertEquals(actualSheet, result);
+        Mockito.verify(actualSheet).copyBasicParameter(paramSheet);
+    }
+
+    @Test
+    void test_match_ByName_AutoTrim_FromSheet() {
+        // Setup
+        
Mockito.when(readWorkbookHolder.getIgnoreHiddenSheet()).thenReturn(false);
+        
Mockito.when(readWorkbookHolder.getParameterSheetDataList()).thenReturn(Collections.singletonList(paramSheet));
+
+        Mockito.when(paramSheet.getSheetNo()).thenReturn(99);
+        Mockito.when(actualSheet.getSheetNo()).thenReturn(100);
+        Mockito.when(paramSheet.getSheetName()).thenReturn("  SheetA  ");
+        Mockito.when(actualSheet.getSheetName()).thenReturn("SheetA");
+
+        Mockito.when(paramSheet.getAutoStrip()).thenReturn(false);
+        Mockito.when(globalConfiguration.getAutoStrip()).thenReturn(false);
+        Mockito.when(paramSheet.getAutoTrim()).thenReturn(true);
+
+        // Execute
+        ReadSheet result = SheetUtils.match(actualSheet, analysisContext);
+
+        // verify
+        Assertions.assertEquals(actualSheet, result);
+    }
+
+    @Test
+    void test_match_ByName_GlobalConfig() {
+        // Setup
+        
Mockito.when(readWorkbookHolder.getIgnoreHiddenSheet()).thenReturn(false);
+        
Mockito.when(readWorkbookHolder.getParameterSheetDataList()).thenReturn(Collections.singletonList(paramSheet));
+
+        Mockito.when(paramSheet.getSheetNo()).thenReturn(99);
+        Mockito.when(actualSheet.getSheetNo()).thenReturn(100);
+        Mockito.when(paramSheet.getSheetName()).thenReturn("  SheetA  ");
+        Mockito.when(actualSheet.getSheetName()).thenReturn("SheetA");
+
+        Mockito.when(paramSheet.getAutoStrip()).thenReturn(null);
+        Mockito.when(globalConfiguration.getAutoStrip()).thenReturn(true);
+
+        // Execute
+        ReadSheet result = SheetUtils.match(actualSheet, analysisContext);
+
+        // verify
+        Assertions.assertEquals(actualSheet, result);
+    }
+}
diff --git 
a/fesod-sheet/src/test/java/org/apache/fesod/sheet/util/WorkBookUtilTest.java 
b/fesod-sheet/src/test/java/org/apache/fesod/sheet/util/WorkBookUtilTest.java
new file mode 100644
index 00000000..f0d9cb23
--- /dev/null
+++ 
b/fesod-sheet/src/test/java/org/apache/fesod/sheet/util/WorkBookUtilTest.java
@@ -0,0 +1,381 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.fesod.sheet.util;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.Locale;
+import org.apache.commons.csv.CSVFormat;
+import org.apache.fesod.sheet.metadata.GlobalConfiguration;
+import org.apache.fesod.sheet.metadata.data.DataFormatData;
+import org.apache.fesod.sheet.metadata.data.WriteCellData;
+import org.apache.fesod.sheet.support.ExcelTypeEnum;
+import org.apache.fesod.sheet.write.metadata.WriteWorkbook;
+import org.apache.fesod.sheet.write.metadata.holder.WriteWorkbookHolder;
+import org.apache.fesod.sheet.write.metadata.style.WriteCellStyle;
+import org.apache.poi.hssf.record.crypto.Biff8EncryptionKey;
+import org.apache.poi.hssf.usermodel.HSSFWorkbook;
+import org.apache.poi.ss.usermodel.Cell;
+import org.apache.poi.ss.usermodel.CellStyle;
+import org.apache.poi.ss.usermodel.Row;
+import org.apache.poi.ss.usermodel.Sheet;
+import org.apache.poi.ss.usermodel.Workbook;
+import org.apache.poi.xssf.streaming.SXSSFWorkbook;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTWorkbook;
+
+/**
+ * Tests {@link WorkBookUtil}
+ */
+@ExtendWith(MockitoExtension.class)
+class WorkBookUtilTest {
+
+    @Mock
+    private WriteWorkbookHolder writeWorkbookHolder;
+
+    @Mock
+    private GlobalConfiguration globalConfiguration;
+
+    @Mock
+    private WriteWorkbook writeWorkbook;
+
+    @AfterEach
+    void tearDown() {
+        Biff8EncryptionKey.setCurrentUserPassword(null);
+    }
+
+    @Test
+    void test_createWorkBook_XLSX_SXSSF() throws IOException {
+        // Setup
+        
Mockito.when(writeWorkbookHolder.getExcelType()).thenReturn(ExcelTypeEnum.XLSX);
+        
Mockito.when(writeWorkbookHolder.getTempTemplateInputStream()).thenReturn(null);
+        Mockito.when(writeWorkbookHolder.getInMemory()).thenReturn(false);
+        
Mockito.when(writeWorkbookHolder.getGlobalConfiguration()).thenReturn(globalConfiguration);
+        
Mockito.when(globalConfiguration.getUse1904windowing()).thenReturn(null);
+
+        // Execute
+        WorkBookUtil.createWorkBook(writeWorkbookHolder);
+
+        // Verify
+        ArgumentCaptor<Workbook> workbookCaptor = 
ArgumentCaptor.forClass(Workbook.class);
+        
Mockito.verify(writeWorkbookHolder).setWorkbook(workbookCaptor.capture());
+        Assertions.assertInstanceOf(SXSSFWorkbook.class, 
workbookCaptor.getValue());
+        
Mockito.verify(writeWorkbookHolder).setCachedWorkbook(Mockito.any(Workbook.class));
+    }
+
+    @Test
+    void test_createWorkBook_XLSX_SXSSF_use1904windowing() throws IOException {
+        // Setup
+        
Mockito.when(writeWorkbookHolder.getExcelType()).thenReturn(ExcelTypeEnum.XLSX);
+        
Mockito.when(writeWorkbookHolder.getTempTemplateInputStream()).thenReturn(null);
+        Mockito.when(writeWorkbookHolder.getInMemory()).thenReturn(false);
+        
Mockito.when(writeWorkbookHolder.getGlobalConfiguration()).thenReturn(globalConfiguration);
+        
Mockito.when(globalConfiguration.getUse1904windowing()).thenReturn(true);
+
+        // Execute
+        WorkBookUtil.createWorkBook(writeWorkbookHolder);
+
+        // Verify
+        ArgumentCaptor<Workbook> workbookCaptor = 
ArgumentCaptor.forClass(Workbook.class);
+        
Mockito.verify(writeWorkbookHolder).setWorkbook(workbookCaptor.capture());
+
+        Assertions.assertInstanceOf(SXSSFWorkbook.class, 
workbookCaptor.getValue());
+
+        SXSSFWorkbook sxssfWorkbook = (SXSSFWorkbook) 
workbookCaptor.getValue();
+        CTWorkbook ctWorkbook = 
sxssfWorkbook.getXSSFWorkbook().getCTWorkbook();
+        Assertions.assertTrue(ctWorkbook.getWorkbookPr().getDate1904());
+        
Mockito.verify(writeWorkbookHolder).setCachedWorkbook(Mockito.any(Workbook.class));
+    }
+
+    @Test
+    void test_createWorkBook_XLSX_SXSSF_withTemplate() throws IOException {
+        // Setup
+        
Mockito.when(writeWorkbookHolder.getExcelType()).thenReturn(ExcelTypeEnum.XLSX);
+        
Mockito.when(writeWorkbookHolder.getTempTemplateInputStream()).thenReturn(createXLSX());
+        Mockito.when(writeWorkbookHolder.getInMemory()).thenReturn(false);
+
+        // Execute
+        WorkBookUtil.createWorkBook(writeWorkbookHolder);
+
+        // Verify
+        
Mockito.verify(writeWorkbookHolder).setCachedWorkbook(Mockito.any(XSSFWorkbook.class));
+        ArgumentCaptor<Workbook> workbookCaptor = 
ArgumentCaptor.forClass(Workbook.class);
+        
Mockito.verify(writeWorkbookHolder).setWorkbook(workbookCaptor.capture());
+        Assertions.assertInstanceOf(SXSSFWorkbook.class, 
workbookCaptor.getValue());
+    }
+
+    @Test
+    void test_createWorkBook_XLSX_SXSSF_withTemplate_inMemory() throws 
IOException {
+        // Setup
+        
Mockito.when(writeWorkbookHolder.getExcelType()).thenReturn(ExcelTypeEnum.XLSX);
+        
Mockito.when(writeWorkbookHolder.getTempTemplateInputStream()).thenReturn(createXLSX());
+        Mockito.when(writeWorkbookHolder.getInMemory()).thenReturn(true);
+
+        // Execute
+        WorkBookUtil.createWorkBook(writeWorkbookHolder);
+
+        // Verify
+        
Mockito.verify(writeWorkbookHolder).setCachedWorkbook(Mockito.any(XSSFWorkbook.class));
+        
Mockito.verify(writeWorkbookHolder).setWorkbook(Mockito.any(XSSFWorkbook.class));
+    }
+
+    private ByteArrayInputStream createXLSX() throws IOException {
+        try (XSSFWorkbook workbook = new XSSFWorkbook();
+                ByteArrayOutputStream out = new ByteArrayOutputStream()) {
+            workbook.createSheet("Template");
+            workbook.write(out);
+            return new ByteArrayInputStream(out.toByteArray());
+        }
+    }
+
+    @Test
+    void test_createWorkBook_XLSX_XSSF() throws IOException {
+        // Setup
+        
Mockito.when(writeWorkbookHolder.getExcelType()).thenReturn(ExcelTypeEnum.XLSX);
+        
Mockito.when(writeWorkbookHolder.getTempTemplateInputStream()).thenReturn(null);
+        Mockito.when(writeWorkbookHolder.getInMemory()).thenReturn(true);
+        
Mockito.when(writeWorkbookHolder.getGlobalConfiguration()).thenReturn(globalConfiguration);
+
+        // Execute
+        WorkBookUtil.createWorkBook(writeWorkbookHolder);
+
+        // Verify
+        ArgumentCaptor<Workbook> workbookCaptor = 
ArgumentCaptor.forClass(Workbook.class);
+        
Mockito.verify(writeWorkbookHolder).setWorkbook(workbookCaptor.capture());
+
+        Assertions.assertInstanceOf(XSSFWorkbook.class, 
workbookCaptor.getValue());
+    }
+
+    @Test
+    void test_createWorkBook_XLS() throws IOException {
+        // Setup
+        
Mockito.when(writeWorkbookHolder.getExcelType()).thenReturn(ExcelTypeEnum.XLS);
+        
Mockito.when(writeWorkbookHolder.getTempTemplateInputStream()).thenReturn(null);
+
+        // Execute
+        WorkBookUtil.createWorkBook(writeWorkbookHolder);
+
+        // Verify
+        
Mockito.verify(writeWorkbookHolder).setCachedWorkbook(Mockito.any(HSSFWorkbook.class));
+        ArgumentCaptor<Workbook> workbookCaptor = 
ArgumentCaptor.forClass(Workbook.class);
+        
Mockito.verify(writeWorkbookHolder).setWorkbook(workbookCaptor.capture());
+
+        Assertions.assertInstanceOf(HSSFWorkbook.class, 
workbookCaptor.getValue());
+    }
+
+    @Test
+    void test_createWorkBook_XLS_withTemplate() throws IOException {
+        // Setup
+        
Mockito.when(writeWorkbookHolder.getExcelType()).thenReturn(ExcelTypeEnum.XLS);
+        
Mockito.when(writeWorkbookHolder.getTempTemplateInputStream()).thenReturn(createXLS());
+
+        // Execute
+        WorkBookUtil.createWorkBook(writeWorkbookHolder);
+
+        // Verify
+        
Mockito.verify(writeWorkbookHolder).setCachedWorkbook(Mockito.any(HSSFWorkbook.class));
+        ArgumentCaptor<Workbook> workbookCaptor = 
ArgumentCaptor.forClass(Workbook.class);
+        
Mockito.verify(writeWorkbookHolder).setWorkbook(workbookCaptor.capture());
+
+        Assertions.assertInstanceOf(HSSFWorkbook.class, 
workbookCaptor.getValue());
+    }
+
+    private ByteArrayInputStream createXLS() throws IOException {
+        try (HSSFWorkbook workbook = new HSSFWorkbook();
+                ByteArrayOutputStream out = new ByteArrayOutputStream()) {
+            workbook.createSheet("Template");
+            workbook.write(out);
+            return new ByteArrayInputStream(out.toByteArray());
+        }
+    }
+
+    @Test
+    void test_createWorkBook_XLS_Password() throws IOException {
+        // Setup
+        
Mockito.when(writeWorkbookHolder.getExcelType()).thenReturn(ExcelTypeEnum.XLS);
+        
Mockito.when(writeWorkbookHolder.getTempTemplateInputStream()).thenReturn(null);
+        Mockito.when(writeWorkbookHolder.getPassword()).thenReturn("123456");
+
+        // Execute
+        WorkBookUtil.createWorkBook(writeWorkbookHolder);
+
+        // Verify
+        
Mockito.verify(writeWorkbookHolder).setWorkbook(Mockito.any(HSSFWorkbook.class));
+    }
+
+    @Test
+    void test_createWorkBook_CSV() throws IOException {
+        // Setup
+        
Mockito.when(writeWorkbookHolder.getExcelType()).thenReturn(ExcelTypeEnum.CSV);
+        Mockito.when(writeWorkbookHolder.getOutputStream()).thenReturn(new 
ByteArrayOutputStream());
+        
Mockito.when(writeWorkbookHolder.getCharset()).thenReturn(StandardCharsets.UTF_8);
+        
Mockito.when(writeWorkbookHolder.getWriteWorkbook()).thenReturn(writeWorkbook);
+
+        
Mockito.when(writeWorkbookHolder.getGlobalConfiguration()).thenReturn(globalConfiguration);
+        
Mockito.when(globalConfiguration.getLocale()).thenReturn(Locale.SIMPLIFIED_CHINESE);
+
+        // Execute
+        WorkBookUtil.createWorkBook(writeWorkbookHolder);
+
+        // Verify
+        
Mockito.verify(writeWorkbookHolder).setWorkbook(Mockito.any(Workbook.class));
+    }
+
+    @Test
+    void test_createWorkBook_CSV_withFormat() throws IOException {
+        // Setup
+        
Mockito.when(writeWorkbookHolder.getExcelType()).thenReturn(ExcelTypeEnum.CSV);
+        Mockito.when(writeWorkbookHolder.getOutputStream()).thenReturn(new 
ByteArrayOutputStream());
+        
Mockito.when(writeWorkbookHolder.getCharset()).thenReturn(StandardCharsets.UTF_8);
+        
Mockito.when(writeWorkbookHolder.getWriteWorkbook()).thenReturn(writeWorkbook);
+        
Mockito.when(writeWorkbook.getCsvFormat()).thenReturn(CSVFormat.DEFAULT);
+
+        
Mockito.when(writeWorkbookHolder.getGlobalConfiguration()).thenReturn(globalConfiguration);
+        
Mockito.when(globalConfiguration.getLocale()).thenReturn(Locale.SIMPLIFIED_CHINESE);
+
+        // Execute
+        WorkBookUtil.createWorkBook(writeWorkbookHolder);
+
+        
Mockito.verify(writeWorkbookHolder).setWorkbook(Mockito.any(Workbook.class));
+    }
+
+    @Test
+    void test_createWorkBook_UnknownType() {
+        Mockito.when(writeWorkbookHolder.getExcelType()).thenReturn(null);
+
+        Assertions.assertThrows(NullPointerException.class, () -> 
WorkBookUtil.createWorkBook(writeWorkbookHolder));
+    }
+
+    @Test
+    void test_createSheet() {
+        Workbook workbook = Mockito.mock(Workbook.class);
+        String sheetName = "TestSheet";
+
+        WorkBookUtil.createSheet(workbook, sheetName);
+
+        Mockito.verify(workbook).createSheet(sheetName);
+    }
+
+    @Test
+    void test_createRow() {
+        Sheet sheet = Mockito.mock(Sheet.class);
+
+        WorkBookUtil.createRow(sheet, 1);
+
+        Mockito.verify(sheet).createRow(1);
+    }
+
+    @Test
+    void test_createCell_basic() {
+        Row row = Mockito.mock(Row.class);
+
+        WorkBookUtil.createCell(row, 2);
+
+        Mockito.verify(row).createCell(2);
+    }
+
+    @Test
+    void test_createCell_withStyle() {
+        Row row = Mockito.mock(Row.class);
+        Cell cell = Mockito.mock(Cell.class);
+        CellStyle style = Mockito.mock(CellStyle.class);
+        Mockito.when(row.createCell(Mockito.anyInt())).thenReturn(cell);
+
+        WorkBookUtil.createCell(row, 2, style);
+
+        Mockito.verify(row).createCell(2);
+        Mockito.verify(cell).setCellStyle(style);
+    }
+
+    @Test
+    void test_createCell_withValue() {
+        Row row = Mockito.mock(Row.class);
+        Cell cell = Mockito.mock(Cell.class);
+        CellStyle style = Mockito.mock(CellStyle.class);
+        Mockito.when(row.createCell(Mockito.anyInt())).thenReturn(cell);
+
+        WorkBookUtil.createCell(row, 2, style, "Hello");
+
+        Mockito.verify(cell).setCellStyle(style);
+        Mockito.verify(cell).setCellValue("Hello");
+    }
+
+    @Test
+    void test_createCell_stringOnly() {
+        Row row = Mockito.mock(Row.class);
+        Cell cell = Mockito.mock(Cell.class);
+        Mockito.when(row.createCell(Mockito.anyInt())).thenReturn(cell);
+
+        WorkBookUtil.createCell(row, 2, "World");
+
+        Mockito.verify(cell).setCellValue("World");
+    }
+
+    @Test
+    void test_fillDataFormat_allNull() {
+        WriteCellData<?> cellData = new WriteCellData<>();
+        String format = null;
+        String defaultFormat = "yyyy-MM-dd";
+
+        WorkBookUtil.fillDataFormat(cellData, format, defaultFormat);
+
+        Assertions.assertNotNull(cellData.getWriteCellStyle());
+        
Assertions.assertNotNull(cellData.getWriteCellStyle().getDataFormatData());
+        Assertions.assertEquals(
+                defaultFormat, 
cellData.getWriteCellStyle().getDataFormatData().getFormat());
+    }
+
+    @Test
+    void test_fillDataFormat_withFormat() {
+        WriteCellData<?> cellData = new WriteCellData<>();
+        String format = "#.00";
+        String defaultFormat = "General";
+
+        WorkBookUtil.fillDataFormat(cellData, format, defaultFormat);
+
+        Assertions.assertEquals(
+                format, 
cellData.getWriteCellStyle().getDataFormatData().getFormat());
+    }
+
+    @Test
+    void test_fillDataFormat_existingFormat() {
+        WriteCellData<?> cellData = new WriteCellData<>();
+        WriteCellStyle writeCellStyle = new WriteCellStyle();
+        DataFormatData dataFormatData = new DataFormatData();
+        dataFormatData.setFormat("Existing");
+        writeCellStyle.setDataFormatData(dataFormatData);
+        cellData.setWriteCellStyle(writeCellStyle);
+
+        WorkBookUtil.fillDataFormat(cellData, "NewFormat", "Default");
+
+        Assertions.assertEquals(
+                "Existing", 
cellData.getWriteCellStyle().getDataFormatData().getFormat());
+    }
+}
diff --git 
a/fesod-sheet/src/test/java/org/apache/fesod/sheet/util/WriteHandlerUtilsTest.java
 
b/fesod-sheet/src/test/java/org/apache/fesod/sheet/util/WriteHandlerUtilsTest.java
new file mode 100644
index 00000000..737c5f16
--- /dev/null
+++ 
b/fesod-sheet/src/test/java/org/apache/fesod/sheet/util/WriteHandlerUtilsTest.java
@@ -0,0 +1,424 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.fesod.sheet.util;
+
+import org.apache.fesod.sheet.context.WriteContext;
+import org.apache.fesod.sheet.metadata.Head;
+import org.apache.fesod.sheet.metadata.property.ExcelContentProperty;
+import org.apache.fesod.sheet.write.handler.chain.CellHandlerExecutionChain;
+import org.apache.fesod.sheet.write.handler.chain.RowHandlerExecutionChain;
+import org.apache.fesod.sheet.write.handler.chain.SheetHandlerExecutionChain;
+import 
org.apache.fesod.sheet.write.handler.chain.WorkbookHandlerExecutionChain;
+import org.apache.fesod.sheet.write.handler.context.CellWriteHandlerContext;
+import org.apache.fesod.sheet.write.handler.context.RowWriteHandlerContext;
+import org.apache.fesod.sheet.write.handler.context.SheetWriteHandlerContext;
+import 
org.apache.fesod.sheet.write.handler.context.WorkbookWriteHandlerContext;
+import org.apache.fesod.sheet.write.metadata.holder.AbstractWriteHolder;
+import org.apache.fesod.sheet.write.metadata.holder.WriteSheetHolder;
+import org.apache.fesod.sheet.write.metadata.holder.WriteTableHolder;
+import org.apache.fesod.sheet.write.metadata.holder.WriteWorkbookHolder;
+import org.apache.poi.ss.usermodel.Row;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.ArgumentMatchers;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+/**
+ * Tests {@link WriteHandlerUtilsTest}
+ */
+@ExtendWith(MockitoExtension.class)
+class WriteHandlerUtilsTest {
+
+    @Mock
+    private WriteContext writeContext;
+
+    @Mock
+    private WriteWorkbookHolder writeWorkbookHolder;
+
+    @Mock
+    private WriteSheetHolder writeSheetHolder;
+
+    @Mock
+    private WriteTableHolder writeTableHolder;
+
+    @Mock
+    private AbstractWriteHolder abstractWriteHolder;
+
+    @Mock
+    private WorkbookHandlerExecutionChain workbookChain;
+
+    @Mock
+    private SheetHandlerExecutionChain sheetChain;
+
+    @Mock
+    private RowHandlerExecutionChain rowChain;
+
+    @Mock
+    private CellHandlerExecutionChain cellChain;
+
+    @BeforeEach
+    void setUp() {
+        
Mockito.lenient().when(writeContext.writeWorkbookHolder()).thenReturn(writeWorkbookHolder);
+        
Mockito.lenient().when(writeContext.writeSheetHolder()).thenReturn(writeSheetHolder);
+        
Mockito.lenient().when(writeContext.writeTableHolder()).thenReturn(writeTableHolder);
+        
Mockito.lenient().when(writeContext.currentWriteHolder()).thenReturn(abstractWriteHolder);
+    }
+
+    @Test
+    void test_createWorkbookWriteHandlerContext_success() {
+        Assertions.assertDoesNotThrow(() -> {
+            WorkbookWriteHandlerContext context = 
WriteHandlerUtils.createWorkbookWriteHandlerContext(writeContext);
+            Assertions.assertNotNull(context);
+            Assertions.assertEquals(writeContext, context.getWriteContext());
+
+            
Mockito.verify(writeWorkbookHolder).setWorkbookWriteHandlerContext(context);
+        });
+    }
+
+    @Test
+    void test_beforeWorkbookCreate_runOwn_false() {
+        WorkbookWriteHandlerContext context = 
Mockito.mock(WorkbookWriteHandlerContext.class);
+        Mockito.when(context.getWriteContext()).thenReturn(writeContext);
+        
Mockito.when(abstractWriteHolder.getWorkbookHandlerExecutionChain()).thenReturn(workbookChain);
+
+        Assertions.assertDoesNotThrow(() -> 
WriteHandlerUtils.beforeWorkbookCreate(context));
+        Mockito.verify(workbookChain).beforeWorkbookCreate(context);
+    }
+
+    @Test
+    void test_beforeWorkbookCreate_runOwn_true() {
+        WorkbookWriteHandlerContext context = 
Mockito.mock(WorkbookWriteHandlerContext.class);
+        Mockito.when(context.getWriteContext()).thenReturn(writeContext);
+        
Mockito.when(abstractWriteHolder.getOwnWorkbookHandlerExecutionChain()).thenReturn(workbookChain);
+
+        Assertions.assertDoesNotThrow(() -> 
WriteHandlerUtils.beforeWorkbookCreate(context, true));
+
+        Mockito.verify(workbookChain).beforeWorkbookCreate(context);
+        Mockito.verify(abstractWriteHolder, 
Mockito.never()).getWorkbookHandlerExecutionChain();
+    }
+
+    @Test
+    void test_beforeWorkbookCreate_chain_null() {
+        WorkbookWriteHandlerContext context = 
Mockito.mock(WorkbookWriteHandlerContext.class);
+        Mockito.when(context.getWriteContext()).thenReturn(writeContext);
+        
Mockito.when(abstractWriteHolder.getWorkbookHandlerExecutionChain()).thenReturn(null);
+
+        Assertions.assertDoesNotThrow(() -> 
WriteHandlerUtils.beforeWorkbookCreate(context));
+    }
+
+    @Test
+    void test_afterWorkbookCreate_runOwn_false() {
+        WorkbookWriteHandlerContext context = 
Mockito.mock(WorkbookWriteHandlerContext.class);
+        Mockito.when(context.getWriteContext()).thenReturn(writeContext);
+        
Mockito.when(abstractWriteHolder.getWorkbookHandlerExecutionChain()).thenReturn(workbookChain);
+
+        Assertions.assertDoesNotThrow(() -> 
WriteHandlerUtils.afterWorkbookCreate(context));
+        Mockito.verify(workbookChain).afterWorkbookCreate(context);
+    }
+
+    @Test
+    void test_afterWorkbookCreate_runOwn_true() {
+        WorkbookWriteHandlerContext context = 
Mockito.mock(WorkbookWriteHandlerContext.class);
+        Mockito.when(context.getWriteContext()).thenReturn(writeContext);
+        
Mockito.when(abstractWriteHolder.getOwnWorkbookHandlerExecutionChain()).thenReturn(workbookChain);
+
+        Assertions.assertDoesNotThrow(() -> 
WriteHandlerUtils.afterWorkbookCreate(context, true));
+        Mockito.verify(workbookChain).afterWorkbookCreate(context);
+        Mockito.verify(abstractWriteHolder, 
Mockito.never()).getWorkbookHandlerExecutionChain();
+    }
+
+    @Test
+    void test_afterWorkbookCreate_chain_null() {
+        WorkbookWriteHandlerContext context = 
Mockito.mock(WorkbookWriteHandlerContext.class);
+        Mockito.when(context.getWriteContext()).thenReturn(writeContext);
+        
Mockito.when(abstractWriteHolder.getWorkbookHandlerExecutionChain()).thenReturn(null);
+
+        Assertions.assertDoesNotThrow(() -> 
WriteHandlerUtils.afterWorkbookCreate(context));
+    }
+
+    @Test
+    void test_afterWorkbookDispose_execution() {
+        WorkbookWriteHandlerContext context = 
Mockito.mock(WorkbookWriteHandlerContext.class);
+        Mockito.when(context.getWriteContext()).thenReturn(writeContext);
+        
Mockito.when(abstractWriteHolder.getWorkbookHandlerExecutionChain()).thenReturn(workbookChain);
+
+        Assertions.assertDoesNotThrow(() -> 
WriteHandlerUtils.afterWorkbookDispose(context));
+        Mockito.verify(workbookChain).afterWorkbookDispose(context);
+    }
+
+    @Test
+    void test_afterWorkbookDispose_chain_null() {
+        WorkbookWriteHandlerContext context = 
Mockito.mock(WorkbookWriteHandlerContext.class);
+        Mockito.when(context.getWriteContext()).thenReturn(writeContext);
+        
Mockito.when(abstractWriteHolder.getWorkbookHandlerExecutionChain()).thenReturn(null);
+
+        Assertions.assertDoesNotThrow(() -> 
WriteHandlerUtils.afterWorkbookDispose(context));
+    }
+
+    @Test
+    void test_createSheetWriteHandlerContext_fields_populated() {
+        Assertions.assertDoesNotThrow(() -> {
+            SheetWriteHandlerContext context = 
WriteHandlerUtils.createSheetWriteHandlerContext(writeContext);
+            Assertions.assertNotNull(context);
+            Assertions.assertEquals(writeSheetHolder, 
context.getWriteSheetHolder());
+        });
+    }
+
+    @Test
+    void test_beforeSheetCreate_runOwn_false() {
+        SheetWriteHandlerContext context = 
Mockito.mock(SheetWriteHandlerContext.class);
+        Mockito.when(context.getWriteContext()).thenReturn(writeContext);
+        
Mockito.when(abstractWriteHolder.getSheetHandlerExecutionChain()).thenReturn(sheetChain);
+
+        Assertions.assertDoesNotThrow(() -> 
WriteHandlerUtils.beforeSheetCreate(context));
+
+        Mockito.verify(sheetChain).beforeSheetCreate(context);
+    }
+
+    @Test
+    void test_beforeSheetCreate_runOwn_true() {
+        SheetWriteHandlerContext context = 
Mockito.mock(SheetWriteHandlerContext.class);
+        Mockito.when(context.getWriteContext()).thenReturn(writeContext);
+        
Mockito.when(abstractWriteHolder.getOwnSheetHandlerExecutionChain()).thenReturn(sheetChain);
+
+        Assertions.assertDoesNotThrow(() -> 
WriteHandlerUtils.beforeSheetCreate(context, true));
+        Mockito.verify(sheetChain).beforeSheetCreate(context);
+        Mockito.verify(abstractWriteHolder, 
Mockito.never()).getSheetHandlerExecutionChain();
+    }
+
+    @Test
+    void test_beforeSheetCreate_chain_null() {
+        SheetWriteHandlerContext context = 
Mockito.mock(SheetWriteHandlerContext.class);
+        Mockito.when(context.getWriteContext()).thenReturn(writeContext);
+        
Mockito.when(abstractWriteHolder.getSheetHandlerExecutionChain()).thenReturn(null);
+
+        Assertions.assertDoesNotThrow(() -> 
WriteHandlerUtils.beforeSheetCreate(context));
+    }
+
+    @Test
+    void test_afterSheetCreate_runOwn_false() {
+        SheetWriteHandlerContext context = 
Mockito.mock(SheetWriteHandlerContext.class);
+        Mockito.when(context.getWriteContext()).thenReturn(writeContext);
+        
Mockito.when(abstractWriteHolder.getSheetHandlerExecutionChain()).thenReturn(sheetChain);
+
+        Assertions.assertDoesNotThrow(() -> 
WriteHandlerUtils.afterSheetCreate(context));
+        Mockito.verify(sheetChain).afterSheetCreate(context);
+    }
+
+    @Test
+    void test_afterSheetCreate_runOwn_true() {
+        SheetWriteHandlerContext context = 
Mockito.mock(SheetWriteHandlerContext.class);
+        Mockito.when(context.getWriteContext()).thenReturn(writeContext);
+        
Mockito.when(abstractWriteHolder.getOwnSheetHandlerExecutionChain()).thenReturn(sheetChain);
+
+        Assertions.assertDoesNotThrow(() -> 
WriteHandlerUtils.afterSheetCreate(context, true));
+        Mockito.verify(sheetChain).afterSheetCreate(context);
+        Mockito.verify(abstractWriteHolder, 
Mockito.never()).getSheetHandlerExecutionChain();
+    }
+
+    @Test
+    void test_afterSheetCreate_chain_null() {
+        SheetWriteHandlerContext context = 
Mockito.mock(SheetWriteHandlerContext.class);
+        Mockito.when(context.getWriteContext()).thenReturn(writeContext);
+        
Mockito.when(abstractWriteHolder.getSheetHandlerExecutionChain()).thenReturn(null);
+
+        Assertions.assertDoesNotThrow(() -> 
WriteHandlerUtils.afterSheetCreate(context));
+    }
+
+    @Test
+    void test_afterSheetDispose_execution() {
+        
Mockito.when(abstractWriteHolder.getSheetHandlerExecutionChain()).thenReturn(sheetChain);
+
+        Assertions.assertDoesNotThrow(() -> 
WriteHandlerUtils.afterSheetDispose(writeContext));
+        
Mockito.verify(sheetChain).afterSheetDispose(ArgumentMatchers.any(SheetWriteHandlerContext.class));
+    }
+
+    @Test
+    void test_afterSheetDispose_chain_null() {
+        
Mockito.when(abstractWriteHolder.getSheetHandlerExecutionChain()).thenReturn(null);
+
+        Assertions.assertDoesNotThrow(() -> 
WriteHandlerUtils.afterSheetDispose(writeContext));
+    }
+
+    @Test
+    void test_createRowWriteHandlerContext_args_mapping() {
+        Assertions.assertDoesNotThrow(() -> {
+            RowWriteHandlerContext context = 
WriteHandlerUtils.createRowWriteHandlerContext(writeContext, 10, 5, true);
+            Assertions.assertEquals(10, context.getRowIndex());
+            Assertions.assertEquals(5, context.getRelativeRowIndex());
+            Assertions.assertTrue(context.getHead());
+        });
+    }
+
+    @Test
+    void test_beforeRowCreate_execution() {
+        RowWriteHandlerContext context = 
Mockito.mock(RowWriteHandlerContext.class);
+        Mockito.when(context.getWriteContext()).thenReturn(writeContext);
+        
Mockito.when(abstractWriteHolder.getRowHandlerExecutionChain()).thenReturn(rowChain);
+
+        Assertions.assertDoesNotThrow(() -> 
WriteHandlerUtils.beforeRowCreate(context));
+        Mockito.verify(rowChain).beforeRowCreate(context);
+    }
+
+    @Test
+    void test_beforeRowCreate_chain_null() {
+        RowWriteHandlerContext context = 
Mockito.mock(RowWriteHandlerContext.class);
+        Mockito.when(context.getWriteContext()).thenReturn(writeContext);
+        
Mockito.when(abstractWriteHolder.getRowHandlerExecutionChain()).thenReturn(null);
+
+        Assertions.assertDoesNotThrow(() -> 
WriteHandlerUtils.beforeRowCreate(context));
+    }
+
+    @Test
+    void test_afterRowCreate_execution() {
+        RowWriteHandlerContext context = 
Mockito.mock(RowWriteHandlerContext.class);
+        Mockito.when(context.getWriteContext()).thenReturn(writeContext);
+        
Mockito.when(abstractWriteHolder.getRowHandlerExecutionChain()).thenReturn(rowChain);
+
+        Assertions.assertDoesNotThrow(() -> 
WriteHandlerUtils.afterRowCreate(context));
+        Mockito.verify(rowChain).afterRowCreate(context);
+    }
+
+    @Test
+    void test_afterRowCreate_chain_null() {
+        RowWriteHandlerContext context = 
Mockito.mock(RowWriteHandlerContext.class);
+        Mockito.when(context.getWriteContext()).thenReturn(writeContext);
+        
Mockito.when(abstractWriteHolder.getRowHandlerExecutionChain()).thenReturn(null);
+
+        Assertions.assertDoesNotThrow(() -> 
WriteHandlerUtils.afterRowCreate(context));
+    }
+
+    @Test
+    void test_afterRowDispose_execution() {
+        RowWriteHandlerContext context = 
Mockito.mock(RowWriteHandlerContext.class);
+        Mockito.when(context.getWriteContext()).thenReturn(writeContext);
+        
Mockito.when(abstractWriteHolder.getRowHandlerExecutionChain()).thenReturn(rowChain);
+
+        Assertions.assertDoesNotThrow(() -> 
WriteHandlerUtils.afterRowDispose(context));
+        Mockito.verify(rowChain).afterRowDispose(context);
+    }
+
+    @Test
+    void test_afterRowDispose_chain_null() {
+        RowWriteHandlerContext context = 
Mockito.mock(RowWriteHandlerContext.class);
+        Mockito.when(context.getWriteContext()).thenReturn(writeContext);
+        
Mockito.when(abstractWriteHolder.getRowHandlerExecutionChain()).thenReturn(null);
+
+        Assertions.assertDoesNotThrow(() -> 
WriteHandlerUtils.afterRowDispose(context));
+    }
+
+    @Test
+    void test_createCellWriteHandlerContext_full_args() {
+        Row row = Mockito.mock(Row.class);
+        Head head = Mockito.mock(Head.class);
+        ExcelContentProperty property = 
Mockito.mock(ExcelContentProperty.class);
+
+        Assertions.assertDoesNotThrow(() -> {
+            CellWriteHandlerContext context =
+                    
WriteHandlerUtils.createCellWriteHandlerContext(writeContext, row, 1, head, 2, 
0, false, property);
+
+            Assertions.assertEquals(row, context.getRow());
+            Assertions.assertEquals(1, context.getRowIndex());
+            Assertions.assertEquals(head, context.getHeadData());
+            Assertions.assertEquals(2, context.getColumnIndex());
+            Assertions.assertEquals(property, 
context.getExcelContentProperty());
+        });
+    }
+
+    @Test
+    void test_beforeCellCreate_execution() {
+        CellWriteHandlerContext context = 
Mockito.mock(CellWriteHandlerContext.class);
+        Mockito.when(context.getWriteContext()).thenReturn(writeContext);
+        
Mockito.when(abstractWriteHolder.getCellHandlerExecutionChain()).thenReturn(cellChain);
+
+        Assertions.assertDoesNotThrow(() -> 
WriteHandlerUtils.beforeCellCreate(context));
+        Mockito.verify(cellChain).beforeCellCreate(context);
+    }
+
+    @Test
+    void test_beforeCellCreate_chain_null() {
+        CellWriteHandlerContext context = 
Mockito.mock(CellWriteHandlerContext.class);
+        Mockito.when(context.getWriteContext()).thenReturn(writeContext);
+        
Mockito.when(abstractWriteHolder.getCellHandlerExecutionChain()).thenReturn(null);
+
+        Assertions.assertDoesNotThrow(() -> 
WriteHandlerUtils.beforeCellCreate(context));
+    }
+
+    @Test
+    void test_afterCellCreate_execution() {
+        CellWriteHandlerContext context = 
Mockito.mock(CellWriteHandlerContext.class);
+        Mockito.when(context.getWriteContext()).thenReturn(writeContext);
+        
Mockito.when(abstractWriteHolder.getCellHandlerExecutionChain()).thenReturn(cellChain);
+
+        Assertions.assertDoesNotThrow(() -> 
WriteHandlerUtils.afterCellCreate(context));
+        Mockito.verify(cellChain).afterCellCreate(context);
+    }
+
+    @Test
+    void test_afterCellCreate_chain_null() {
+        CellWriteHandlerContext context = 
Mockito.mock(CellWriteHandlerContext.class);
+        Mockito.when(context.getWriteContext()).thenReturn(writeContext);
+        
Mockito.when(abstractWriteHolder.getCellHandlerExecutionChain()).thenReturn(null);
+
+        Assertions.assertDoesNotThrow(() -> 
WriteHandlerUtils.afterCellCreate(context));
+    }
+
+    @Test
+    void test_afterCellDataConverted_execution() {
+        CellWriteHandlerContext context = 
Mockito.mock(CellWriteHandlerContext.class);
+        Mockito.when(context.getWriteContext()).thenReturn(writeContext);
+        
Mockito.when(abstractWriteHolder.getCellHandlerExecutionChain()).thenReturn(cellChain);
+
+        Assertions.assertDoesNotThrow(() -> 
WriteHandlerUtils.afterCellDataConverted(context));
+        Mockito.verify(cellChain).afterCellDataConverted(context);
+    }
+
+    @Test
+    void test_afterCellDataConverted_chain_null() {
+        CellWriteHandlerContext context = 
Mockito.mock(CellWriteHandlerContext.class);
+        Mockito.when(context.getWriteContext()).thenReturn(writeContext);
+        
Mockito.when(abstractWriteHolder.getCellHandlerExecutionChain()).thenReturn(null);
+
+        Assertions.assertDoesNotThrow(() -> 
WriteHandlerUtils.afterCellDataConverted(context));
+    }
+
+    @Test
+    void test_afterCellDispose_execution() {
+        CellWriteHandlerContext context = 
Mockito.mock(CellWriteHandlerContext.class);
+        Mockito.when(context.getWriteContext()).thenReturn(writeContext);
+        
Mockito.when(abstractWriteHolder.getCellHandlerExecutionChain()).thenReturn(cellChain);
+
+        Assertions.assertDoesNotThrow(() -> 
WriteHandlerUtils.afterCellDispose(context));
+        Mockito.verify(cellChain).afterCellDispose(context);
+    }
+
+    @Test
+    void test_afterCellDispose_chain_null() {
+        CellWriteHandlerContext context = 
Mockito.mock(CellWriteHandlerContext.class);
+        Mockito.when(context.getWriteContext()).thenReturn(writeContext);
+        
Mockito.when(abstractWriteHolder.getCellHandlerExecutionChain()).thenReturn(null);
+
+        Assertions.assertDoesNotThrow(() -> 
WriteHandlerUtils.afterCellDispose(context));
+    }
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to