Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/main/java/com/github/jsonzou/jmockdata/MockConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,14 @@ public Object getcacheBean(String beanClassName) {
return beanCache.get(beanClassName);
}

public void cacheTypeVariable(String name, Type type) {
typeVariableCache.put(name, type);
}

public void removeTypeVariable(String name) {
typeVariableCache.remove(name);
}

public void cacheEnum(String name, Enum[] enums) {
enumCache.put(name, enums);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
Expand All @@ -17,9 +20,15 @@
public class BeanMocker implements Mocker<Object> {
private Map<Class<?>, List<Field>> classFieldCache = new ConcurrentHashMap<>((int)((float)32 / 0.75F + 1.0F));
private final Class clazz;
private final Type[] genericTypes;

BeanMocker(Class clazz) {
this(clazz, null);
}

BeanMocker(Class clazz, Type[] genericTypes) {
this.clazz = clazz;
this.genericTypes = genericTypes;
}

@Override
Expand All @@ -41,7 +50,41 @@ public Object mock(DataConfig mockConfig) {
if(mockConfig.globalConfig().isConfigExcludeMock(clazz)){
return result;
}
setFieldValueByFieldAccessible(mockConfig, result);
// Initialize type variable cache if generic types are provided
Map<String, Type> savedTypeVariables = null;
if (genericTypes != null && genericTypes.length > 0) {
TypeVariable[] typeVariables = clazz.getTypeParameters();
if (typeVariables != null && typeVariables.length > 0) {
// Save current type variable mappings
savedTypeVariables = new HashMap<>();
for (int index = 0; index < Math.min(typeVariables.length, genericTypes.length); index++) {
String varName = typeVariables[index].getName();
Type oldValue = mockConfig.globalConfig().getVariableType(varName);
if (oldValue != null) {
savedTypeVariables.put(varName, oldValue);
}
mockConfig.globalConfig().cacheTypeVariable(varName, genericTypes[index]);
}
}
}
try {
setFieldValueByFieldAccessible(mockConfig, result);
} finally {
// Restore previous type variable mappings
if (savedTypeVariables != null) {
TypeVariable[] typeVariables = clazz.getTypeParameters();
if (typeVariables != null && typeVariables.length > 0) {
for (int index = 0; index < Math.min(typeVariables.length, genericTypes.length); index++) {
String varName = typeVariables[index].getName();
if (savedTypeVariables.containsKey(varName)) {
mockConfig.globalConfig().cacheTypeVariable(varName, savedTypeVariables.get(varName));
} else {
mockConfig.globalConfig().removeTypeVariable(varName);
}
}
}
}
}
return result;
} catch (Exception e) {
throw new MockException(e);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public Object mock(DataConfig mockConfig) {
} else {
mocker = mockConfig.globalConfig().getMocker(clazz);
if (mocker == null) {
mocker = new BeanMocker(clazz);
mocker = new BeanMocker(clazz, genericTypes);
}
}
return mocker.mock(mockConfig);
Expand Down
22 changes: 22 additions & 0 deletions src/test/java/com/github/jsonzou/jmockdata/JMockDataTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,28 @@ public void testGenericData() {
assertNotNull(genericData);
}

/**
* Test for issue: Mocking fails when regular objects contain collections with generic fields
* (普通对象包含含有泛型字段的集合时mock失败)
* Test mocking a bean with a field containing a parameterized generic type
*/
@Test
public void testGenericFieldInCollection() {
GeneralBean entity = JMockData.mock(GeneralBean.class);
assertNotNull(entity);
assertNotNull(entity.getRows());
assertFalse(entity.getRows().isEmpty());

// Verify that the generic field in GenericFieldEntity is properly mocked
GenericFieldEntity<String> firstItem = entity.getRows().get(0);
assertNotNull(firstItem);
assertNotNull(firstItem.getKey());
assertTrue(firstItem.getKey() instanceof String);

System.out.println("Generic field test passed! Key type: " + firstItem.getKey().getClass().getName());
System.out.println("Key value: " + firstItem.getKey());
}

@Test
public void testMockConfig() {
MockConfig mockConfig = new MockConfig()
Expand Down
19 changes: 19 additions & 0 deletions src/test/java/com/github/jsonzou/jmockdata/bean/GeneralBean.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.github.jsonzou.jmockdata.bean;

import java.util.List;

/**
* Bean containing a list of generic entities
* Used to test the fix for issue: Mocking fails when regular objects contain collections with generic fields
*/
public class GeneralBean {
private List<GenericFieldEntity<String>> rows;

public List<GenericFieldEntity<String>> getRows() {
return rows;
}

public void setRows(List<GenericFieldEntity<String>> rows) {
this.rows = rows;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.github.jsonzou.jmockdata.bean;

/**
* Generic entity with a parameterized type field
*/
public class GenericFieldEntity<T> {
private T key;

public T getKey() {
return key;
}

public void setKey(T key) {
this.key = key;
}
}