Simplify SpEL ExpressionWithConversionTests

This commit is contained in:
Sam Brannen 2025-03-28 15:13:04 +01:00
parent ac7c7ff5b2
commit b8c2780bfe
1 changed files with 32 additions and 65 deletions

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2024 the original author or authors. * Copyright 2002-2025 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -16,27 +16,24 @@
package org.springframework.expression.spel; package org.springframework.expression.spel;
import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Set;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.core.MethodParameter; import org.springframework.core.MethodParameter;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor; import org.springframework.core.convert.TypeDescriptor;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.Expression; import org.springframework.expression.Expression;
import org.springframework.expression.TypeConverter; import org.springframework.expression.TypeConverter;
import org.springframework.expression.spel.support.StandardEvaluationContext; import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.expression.spel.support.StandardTypeConverter;
import org.springframework.util.ReflectionUtils;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
/** /**
* Expression evaluation where the TypeConverter plugged in is the * Expression evaluation where the TypeConverter plugged in uses the
* {@link org.springframework.core.convert.support.GenericConversionService}. * {@link org.springframework.core.convert.support.GenericConversionService}.
* *
* @author Andy Clement * @author Andy Clement
@ -44,54 +41,43 @@ import static org.assertj.core.api.Assertions.assertThat;
*/ */
class ExpressionWithConversionTests extends AbstractExpressionTests { class ExpressionWithConversionTests extends AbstractExpressionTests {
private static List<String> listOfString = new ArrayList<>(); private static final List<String> listOfString = List.of("1", "2", "3");
private static TypeDescriptor typeDescriptorForListOfString = null; private static final List<Integer> listOfInteger = List.of(4, 5, 6);
private static List<Integer> listOfInteger = new ArrayList<>();
private static TypeDescriptor typeDescriptorForListOfInteger = null;
static { private static final TypeDescriptor typeDescriptorForListOfString =
listOfString.add("1"); new TypeDescriptor(ReflectionUtils.findField(ExpressionWithConversionTests.class, "listOfString"));
listOfString.add("2"); private static TypeDescriptor typeDescriptorForListOfInteger =
listOfString.add("3"); new TypeDescriptor(ReflectionUtils.findField(ExpressionWithConversionTests.class, "listOfInteger"));
listOfInteger.add(4);
listOfInteger.add(5);
listOfInteger.add(6);
}
@BeforeEach
void setUp() throws Exception {
ExpressionWithConversionTests.typeDescriptorForListOfString = new TypeDescriptor(ExpressionWithConversionTests.class.getDeclaredField("listOfString"));
ExpressionWithConversionTests.typeDescriptorForListOfInteger = new TypeDescriptor(ExpressionWithConversionTests.class.getDeclaredField("listOfInteger"));
}
/** /**
* Test the service can convert what we are about to use in the expression evaluation tests. * Test the service can convert what we are about to use in the expression evaluation tests.
*/ */
@Test @Test
void testConversionsAvailable() { void conversionsAreSupportedByStandardTypeConverter() {
TypeConvertorUsingConversionService tcs = new TypeConvertorUsingConversionService(); StandardTypeConverter typeConverter = new StandardTypeConverter();
// ArrayList containing List<Integer> to List<String> // List<Integer> to List<String>
Class<?> clazz = typeDescriptorForListOfString.getElementTypeDescriptor().getType(); Class<?> clazz = typeDescriptorForListOfString.getElementTypeDescriptor().getType();
assertThat(clazz).isEqualTo(String.class); assertThat(clazz).isEqualTo(String.class);
List<?> l = (List<?>) tcs.convertValue(listOfInteger, TypeDescriptor.forObject(listOfInteger), typeDescriptorForListOfString); List<?> l = (List<?>) typeConverter.convertValue(listOfInteger, TypeDescriptor.forObject(listOfInteger), typeDescriptorForListOfString);
assertThat(l).isNotNull(); assertThat(l).isNotNull();
// ArrayList containing List<String> to List<Integer> // List<String> to List<Integer>
clazz = typeDescriptorForListOfInteger.getElementTypeDescriptor().getType(); clazz = typeDescriptorForListOfInteger.getElementTypeDescriptor().getType();
assertThat(clazz).isEqualTo(Integer.class); assertThat(clazz).isEqualTo(Integer.class);
l = (List<?>) tcs.convertValue(listOfString, TypeDescriptor.forObject(listOfString), typeDescriptorForListOfString); l = (List<?>) typeConverter.convertValue(listOfString, TypeDescriptor.forObject(listOfString), typeDescriptorForListOfString);
assertThat(l).isNotNull(); assertThat(l).isNotNull();
} }
@Test @Test
void testSetParameterizedList() { void setParameterizedList() {
StandardEvaluationContext context = TestScenarioCreator.getTestEvaluationContext(); StandardEvaluationContext context = TestScenarioCreator.getTestEvaluationContext();
Expression e = parser.parseExpression("listOfInteger.size()"); Expression e = parser.parseExpression("listOfInteger.size()");
assertThat(e.getValue(context, Integer.class)).isZero(); assertThat(e.getValue(context, Integer.class)).isZero();
context.setTypeConverter(new TypeConvertorUsingConversionService());
// Assign a List<String> to the List<Integer> field - the component elements should be converted // Assign a List<String> to the List<Integer> field - the component elements should be converted
parser.parseExpression("listOfInteger").setValue(context,listOfString); parser.parseExpression("listOfInteger").setValue(context,listOfString);
// size now 3 // size now 3
@ -101,7 +87,7 @@ class ExpressionWithConversionTests extends AbstractExpressionTests {
} }
@Test @Test
void testCoercionToCollectionOfPrimitive() throws Exception { void coercionToCollectionOfPrimitive() throws Exception {
class TestTarget { class TestTarget {
@SuppressWarnings("unused") @SuppressWarnings("unused")
@ -115,28 +101,28 @@ class ExpressionWithConversionTests extends AbstractExpressionTests {
} }
StandardEvaluationContext evaluationContext = new StandardEvaluationContext(); StandardEvaluationContext evaluationContext = new StandardEvaluationContext();
TypeConverter typeConverter = evaluationContext.getTypeConverter();
TypeDescriptor collectionType = new TypeDescriptor(new MethodParameter(TestTarget.class.getDeclaredMethod( TypeDescriptor collectionType = new TypeDescriptor(new MethodParameter(TestTarget.class.getDeclaredMethod(
"sum", Collection.class), 0)); "sum", Collection.class), 0));
// The type conversion is possible // The type conversion is possible
assertThat(evaluationContext.getTypeConverter() assertThat(typeConverter.canConvert(TypeDescriptor.valueOf(String.class), collectionType)).isTrue();
.canConvert(TypeDescriptor.valueOf(String.class), collectionType)).isTrue();
// ... and it can be done successfully // ... and it can be done successfully
assertThat(evaluationContext.getTypeConverter().convertValue("1,2,3,4", TypeDescriptor.valueOf(String.class), collectionType).toString()).isEqualTo("[1, 2, 3, 4]"); assertThat(typeConverter.convertValue("1,2,3,4", TypeDescriptor.valueOf(String.class), collectionType))
.hasToString("[1, 2, 3, 4]");
evaluationContext.setVariable("target", new TestTarget()); evaluationContext.setVariable("target", new TestTarget());
// OK up to here, so the evaluation should be fine... // OK up to here, so the evaluation should be fine...
// ... but this fails // ... but this fails
int result = (Integer) parser.parseExpression("#target.sum(#root)").getValue(evaluationContext, "1,2,3,4"); int result = parser.parseExpression("#target.sum(#root)").getValue(evaluationContext, "1,2,3,4", int.class);
assertThat(result).as("Wrong result: " + result).isEqualTo(10); assertThat(result).isEqualTo(10);
} }
@Test @Test
void testConvert() { void convert() {
Foo root = new Foo("bar"); Foo root = new Foo("bar");
Collection<String> foos = Collections.singletonList("baz"); Collection<String> foos = Set.of("baz");
StandardEvaluationContext context = new StandardEvaluationContext(root); StandardEvaluationContext context = new StandardEvaluationContext(root);
@ -163,26 +149,7 @@ class ExpressionWithConversionTests extends AbstractExpressionTests {
expression = parser.parseExpression("setFoos(getFoosAsObjects())"); expression = parser.parseExpression("setFoos(getFoosAsObjects())");
expression.getValue(context); expression.getValue(context);
baz = root.getFoos().iterator().next(); baz = root.getFoos().iterator().next();
assertThat(baz.value).isEqualTo("baz"); assertThat(baz.value).isEqualTo("quux");
}
/**
* Type converter that uses the core conversion service.
*/
private static class TypeConvertorUsingConversionService implements TypeConverter {
private final ConversionService service = new DefaultConversionService();
@Override
public boolean canConvert(TypeDescriptor sourceType, TypeDescriptor targetType) {
return this.service.canConvert(sourceType, targetType);
}
@Override
public Object convertValue(Object value, TypeDescriptor sourceType, TypeDescriptor targetType) throws EvaluationException {
return this.service.convert(value, sourceType, targetType);
}
} }
@ -205,11 +172,11 @@ class ExpressionWithConversionTests extends AbstractExpressionTests {
} }
public Collection<String> getFoosAsStrings() { public Collection<String> getFoosAsStrings() {
return Collections.singletonList("baz"); return Set.of("baz");
} }
public Collection<?> getFoosAsObjects() { public Collection<?> getFoosAsObjects() {
return Collections.singletonList("baz"); return Set.of("quux");
} }
} }