Issue
I am trying to test a controller but it always gives me the following Thymeleaf TemplateProcessingException:
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.thymeleaf.exceptions.TemplateProcessingException: Exception evaluating SpringEL expression: "car.name" (template: "checkout" - line 56, col 35)
This is my controller:
@Controller
public class BookingController {
@Autowired
private BookingService bookingService;
@Autowired
private UserService userService;
@Autowired
private BookingExtraService bookingExtraService;
@Autowired
private CarService carService;
@PostMapping("/checkout")
public String checkout(@RequestParam(required = false) List<Long> selectedExtras,
@RequestParam String userName,
@RequestParam String startAddress,
@RequestParam String goalAddress,
@RequestParam String dateOfTrip,
@RequestParam String timeOfTrip,
@RequestParam Integer numberOfPeople,
Model model,
HttpSession session){
session.setAttribute("userName", userName);
session.setAttribute("startAddress", startAddress);
session.setAttribute("goalAddress", goalAddress);
session.setAttribute("dateOfTrip", dateOfTrip);
session.setAttribute("timeOfTrip", timeOfTrip);
session.setAttribute("numberOfPeople", numberOfPeople);
List<BookingExtra> extras = new ArrayList<>();
if(selectedExtras != null) {
session.setAttribute("selectedExtras", selectedExtras);
for (Long extraId : selectedExtras) {
extras.add(bookingExtraService.findById(extraId));
}
}
model.addAttribute("extras", extras);
Car car = carService.findById((Long) session.getAttribute("selectedCar"));
model.addAttribute("car", car);
DecimalFormat df = new DecimalFormat("#0.00");
String priceTotalString = df.format(bookingService.calculatePrice(extras, car));
model.addAttribute("priceTotal", priceTotalString);
return "checkout";
}
This is my view "checkout":
<body>
<navbar th:insert="fragments.html :: navbar"></navbar>
<div class="div-gap"></div>
<div class="container">
<div class="container" style="width: 800px">
<h3>Zusammenfassung</h3>
<br>
<table class="table">
<thead>
<tr>
<th scope="col">Extra:</th>
<th scope="col">Preis:</th>
</tr>
</thead>
<tbody>
<tr th:if="${extras.empty}">
<td>keine Extras ausgewählt</td>
</tr>
<tr th:each="extra : ${extras}">
<td th:text="${extra.name}"></td>
<td th:text="${extra.price} + ' €'"></td>
</tr>
<tr>
<td>+ Grundpreis Auto
<span th:text="' (' +${car.name} + ')'"></span>
</td>
<td th:text="${car.basePrice} + ' €'"></td>
</tr>
<tr>
<td class="fs-4 fw-bold">Gesamt:</td>
<td class="fs-4 fw-bold" th:text="${priceTotal} + '€'"></td>
</tr>
</tbody>
</table>
<form th:action="@{/savebooking}" method="post">
<input type="hidden" name="dateOfTrip" th:value="${session.dateOfTrip}">
<input type="hidden" name="timeOfTrip" th:value="${session.timeOfTrip}">
<div class="d-grid gap-2">
<button type="submit" class="btn btn-primary fs-5 fw-bold">Buchen</button>
</div>
</form>
</div>
</div>
</body>
</html>
And this is my test class:
@WebMvcTest(BookingController.class)
@AutoConfigureMockMvc(addFilters = false)
class BookingControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private BookingService bookingServiceMock;
@MockBean
private UserService userServiceMock;
@MockBean
private BookingExtraService bookingExtraServiceMock;
@MockBean
private CarService carServiceMock;
private static MockHttpServletRequest request;
@MockBean
private Car car;
@MockBean
private CarImage carImage;
@BeforeAll
public static void setup(){
request = new MockHttpServletRequest();
request.setParameter("userName", "[email protected]");
request.setParameter("startAddress", "Testaddress");
request.setParameter("goalAddress", "Testaddress");
request.setParameter("dateOfTrip", "2022/08/01");
request.setParameter("timeOfTrip", "08:00");
request.setParameter("numberOfPeople", "8");
}
@Test
public void shouldReturnCheckout() throws Exception{
when(carServiceMock.findById(1L)).thenReturn(car);
MvcResult mvcResult = this.mockMvc.perform(post("/checkout")
.contentType(MediaType.APPLICATION_JSON)
.param("userName", request.getParameter("userName"))
.param("startAddress", request.getParameter("startAddress"))
.param("goalAddress", request.getParameter("goalAddress"))
.param("dateOfTrip", request.getParameter("dateOfTrip"))
.param("timeOfTrip", request.getParameter("timeOfTrip"))
.param("numberOfPeople", request.getParameter("numberOfPeople")))
.andExpect(status().isOk()).andReturn();
ModelAndViewAssert.assertViewName(mvcResult.getModelAndView(), "checkout");
}
What I've already tried:
- Setting Expectations for car.name and car.getname() with when().thenReturn()
- Loading the whole ApplicationContext with @SpringBootTest and instead of @WebMvcTest
- Creating a new Car instance and returning it with thenReturn() instead of the mocked Car instance
- Searching the internet for 2 days for a possible solution
Can anyone please point me in the right direction what I am missing here?
EDIT: Complete Stacktrace:
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.thymeleaf.exceptions.TemplateProcessingException: Exception evaluating SpringEL expression: "car.name" (template: "checkout" - line 56, col 35)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1014)
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:909)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:681)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)
at org.springframework.test.web.servlet.TestDispatcherServlet.service(TestDispatcherServlet.java:72)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:764)
at org.springframework.mock.web.MockFilterChain$ServletFilterProxy.doFilter(MockFilterChain.java:167)
at org.springframework.mock.web.MockFilterChain.doFilter(MockFilterChain.java:134)
at org.springframework.test.web.servlet.MockMvc.perform(MockMvc.java:199)
at de.akad.web43.Controller.BookingControllerTest.shouldReturnCheckout(BookingControllerTest.java:124)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:568)
at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:725)
at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131)
at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:149)
at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:140)
at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:84)
at org.junit.jupiter.engine.execution.ExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(ExecutableInvoker.java:115)
at org.junit.jupiter.engine.execution.ExecutableInvoker.lambda$invoke$0(ExecutableInvoker.java:105)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37)
at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:104)
at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:98)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$7(TestMethodTestDescriptor.java:214)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:210)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:135)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:66)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:151)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:35)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:54)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:107)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:88)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:54)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:67)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:52)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:114)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:86)
at org.junit.platform.launcher.core.DefaultLauncherSession$DelegatingLauncher.execute(DefaultLauncherSession.java:86)
at org.junit.platform.launcher.core.SessionPerRequestLauncher.execute(SessionPerRequestLauncher.java:53)
at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:71)
at com.intellij.rt.junit.IdeaTestRunner$Repeater$1.execute(IdeaTestRunner.java:38)
at com.intellij.rt.execution.junit.TestsRepeater.repeat(TestsRepeater.java:11)
at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:35)
at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:235)
at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:54)
Caused by: org.thymeleaf.exceptions.TemplateProcessingException: Exception evaluating SpringEL expression: "car.name" (template: "checkout" - line 56, col 35)
at org.thymeleaf.spring5.expression.SPELVariableExpressionEvaluator.evaluate(SPELVariableExpressionEvaluator.java:292)
at org.thymeleaf.standard.expression.VariableExpression.executeVariableExpression(VariableExpression.java:166)
at org.thymeleaf.standard.expression.SimpleExpression.executeSimple(SimpleExpression.java:66)
at org.thymeleaf.standard.expression.Expression.execute(Expression.java:109)
at org.thymeleaf.standard.expression.AdditionExpression.executeAddition(AdditionExpression.java:96)
at org.thymeleaf.standard.expression.ComplexExpression.executeComplex(ComplexExpression.java:62)
at org.thymeleaf.standard.expression.Expression.execute(Expression.java:112)
at org.thymeleaf.standard.expression.AdditionExpression.executeAddition(AdditionExpression.java:89)
at org.thymeleaf.standard.expression.ComplexExpression.executeComplex(ComplexExpression.java:62)
at org.thymeleaf.standard.expression.Expression.execute(Expression.java:112)
at org.thymeleaf.standard.expression.Expression.execute(Expression.java:138)
at org.thymeleaf.standard.processor.AbstractStandardExpressionAttributeTagProcessor.doProcess(AbstractStandardExpressionAttributeTagProcessor.java:144)
at org.thymeleaf.processor.element.AbstractAttributeTagProcessor.doProcess(AbstractAttributeTagProcessor.java:74)
at org.thymeleaf.processor.element.AbstractElementTagProcessor.process(AbstractElementTagProcessor.java:95)
at org.thymeleaf.util.ProcessorConfigurationUtils$ElementTagProcessorWrapper.process(ProcessorConfigurationUtils.java:633)
at org.thymeleaf.engine.ProcessorTemplateHandler.handleOpenElement(ProcessorTemplateHandler.java:1314)
at org.thymeleaf.engine.OpenElementTag.beHandled(OpenElementTag.java:205)
at org.thymeleaf.engine.TemplateModel.process(TemplateModel.java:136)
at org.thymeleaf.engine.TemplateManager.parseAndProcess(TemplateManager.java:661)
at org.thymeleaf.TemplateEngine.process(TemplateEngine.java:1098)
at org.thymeleaf.TemplateEngine.process(TemplateEngine.java:1072)
at org.thymeleaf.spring5.view.ThymeleafView.renderFragment(ThymeleafView.java:366)
at org.thymeleaf.spring5.view.ThymeleafView.render(ThymeleafView.java:190)
at org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java:1401)
at org.springframework.test.web.servlet.TestDispatcherServlet.render(TestDispatcherServlet.java:137)
at org.springframework.web.servlet.DispatcherServlet.processDispatchResult(DispatcherServlet.java:1145)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1084)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:963)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)
... 78 more
Caused by: org.springframework.expression.spel.SpelEvaluationException: EL1007E: Property or field 'name' cannot be found on null
at org.springframework.expression.spel.ast.PropertyOrFieldReference.readProperty(PropertyOrFieldReference.java:213)
at org.springframework.expression.spel.ast.PropertyOrFieldReference.getValueInternal(PropertyOrFieldReference.java:104)
at org.springframework.expression.spel.ast.PropertyOrFieldReference.access$000(PropertyOrFieldReference.java:51)
at org.springframework.expression.spel.ast.PropertyOrFieldReference$AccessorLValue.getValue(PropertyOrFieldReference.java:406)
at org.springframework.expression.spel.ast.CompoundExpression.getValueInternal(CompoundExpression.java:92)
at org.springframework.expression.spel.ast.SpelNodeImpl.getValue(SpelNodeImpl.java:112)
at org.springframework.expression.spel.standard.SpelExpression.getValue(SpelExpression.java:338)
at org.thymeleaf.spring5.expression.SPELVariableExpressionEvaluator.evaluate(SPELVariableExpressionEvaluator.java:265)
... 106 more
SOLUTION
Since
carServices.findId((Long) session.getAttribute("selectedCar")
needs the session attribute to return Car properly, I could resolve the issue by adding this attribute to the mocked session. My adjusted test is as follows:
@Test
public void shouldReturnCheckout() throws Exception{
when(carServiceMock.findById(1L)).thenReturn(car);
MvcResult mvcResult = this.mockMvc.perform(post("/checkout")
.contentType(MediaType.APPLICATION_JSON)
.param("userName", request.getParameter("userName"))
.param("startAddress", request.getParameter("startAddress"))
.param("goalAddress", request.getParameter("goalAddress"))
.param("dateOfTrip", request.getParameter("dateOfTrip"))
.param("timeOfTrip", request.getParameter("timeOfTrip"))
.param("numberOfPeople", request.getParameter("numberOfPeople"))
.sessionAttr("selectedCar", 1L))
.andExpect(status().isOk()).andReturn();
ModelAndViewAssert.assertViewName(mvcResult.getModelAndView(), "checkout");
}
Solution
Caused by: org.springframework.expression.spel.SpelEvaluationException: EL1007E: Property or field 'name' cannot be found on null
Meaning your car
object is returning as null mostly as a part of this call in BookingController
:
Car car = carService.findById((Long) session.getAttribute("selectedCar"));
Can you recheck if your carService
is returning any car object at all at this point.
If you managed to add proper non-null car
attribute to model, your issue will get resolved for "car.name" as well as for "car.basePrice".
If in some cases, car
used to return null, then you might need to use conditions something like this -
<div th:if= "${car!=null}">
//then access cars properties
Answered By - Ashish Patil
Answer Checked By - David Marino (JavaFixing Volunteer)