Home > other >  Spring Boot Testing Thymeleaf org.thymeleaf.exceptions.TemplateProcessingException
Spring Boot Testing Thymeleaf org.thymeleaf.exceptions.TemplateProcessingException

Time:07-28

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 ></div>
    <div >
        <div  style="width: 800px">
            <h3>Zusammenfassung</h3>
            <br>

            <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 >Gesamt:</td>
                        <td  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 >
                    <button type="submit" >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");
}

CodePudding user response:

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
  • Related