简体   繁体   English

Spring 启动测试 Thymeleaf org.thymeleaf.exceptions.TemplateProcessingException

[英]Spring Boot Testing Thymeleaf org.thymeleaf.exceptions.TemplateProcessingException

I am trying to test a controller but it always gives me the following Thymeleaf TemplateProcessingException:我正在尝试测试 controller 但它总是给我以下 Thymeleaf TemplateProcessingException:

org.springframework.web.util.NestedServletException: Request processing failed; org.springframework.web.util.NestedServletException:请求处理失败; nested exception is org.thymeleaf.exceptions.TemplateProcessingException: Exception evaluating SpringEL expression: "car.name" (template: "checkout" - line 56, col 35)嵌套异常是 org.thymeleaf.exceptions.TemplateProcessingException:评估 SpringEL 表达式的异常:“car.name”(模板:“checkout” - 第 56 行,第 35 列)

This is my controller:这是我的 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:这是我的测试 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", "test@test.de");
        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()使用 when().thenReturn() 设置 car.name 和 car.getname() 的期望
  • Loading the whole ApplicationContext with @SpringBootTest and instead of @WebMvcTest使用 @SpringBootTest 而不是 @WebMvcTest 加载整个 ApplicationContext
  • Creating a new Car instance and returning it with thenReturn() instead of the mocked Car instance创建一个新的 Car 实例并使用 thenReturn() 而不是模拟的 Car 实例返回它
  • Searching the internet for 2 days for a possible solution在互联网上搜索 2 天寻找可能的解决方案

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.需要 session 属性才能正确返回 Car,我可以通过将此属性添加到模拟的 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");
}

Caused by: org.springframework.expression.spel.SpelEvaluationException: EL1007E: Property or field 'name' cannot be found on null原因:org.springframework.expression.spel.SpelEvaluationException:EL1007E:在 null 上找不到属性或字段“名称”

Meaning your car object is returning as null mostly as a part of this call in BookingController :这意味着您的car object 以 null 的形式返回,主要是作为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.您能否重新检查一下您的carService是否在此时返回任何汽车 object。

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".如果您设法将正确的非空car属性添加到 model,则“car.name”和“car.basePrice”的问题将得到解决。

If in some cases, car used to return null, then you might need to use conditions something like this -如果在某些情况下, car曾经返回 null,那么您可能需要使用类似这样的条件 -

 <div th:if= "${car!=null}">
  //then access cars properties

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 org.thymeleaf.exceptions.TemplateProcessingException:串联 href - org.thymeleaf.exceptions.TemplateProcessingException: Concatenation href org.thymeleaf.exceptions.TemplateProcessingException:评估 SpringEL 表达式的异常:“workout.exerciseName” - org.thymeleaf.exceptions.TemplateProcessingException: Exception evaluating SpringEL expression: “workout.exerciseName” Thymeleaf 模板 th:data=http://www.somesite.com || org.thymeleaf.exceptions.TemplateProcessingException:无法解析为分配序列 - Thymeleaf Templating th:data=http://www.somesite.com || org.thymeleaf.exceptions.TemplateProcessingException: Could not parse as assignation sequence org.thymeleaf.exceptions.TemplateInputException:Spring Boot - org.thymeleaf.exceptions.TemplateInputException: Spring Boot Thymeleaf spring 引导解析模板异常 - Thymeleaf exceptions for resolving template in spring boot 我的spring-boot应用程序给出以下错误“ org.thymeleaf.exceptions.TemplateInputException:” - My spring-boot app gives the following error “org.thymeleaf.exceptions.TemplateInputException:” 测试spring-boot安全性,Thymeleaf错误 - Testing spring-boot security, Thymeleaf error Thymeleaf:org.thymeleaf.exceptions.TemplateInputException - Thymeleaf : org.thymeleaf.exceptions.TemplateInputException Thymeleaf失败的Spring Boot - Spring Boot with Thymeleaf Failing Spring Boot和Thymeleaf的问题 - Issues with Spring Boot and Thymeleaf
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM