将Spring @WebMvcTest与Junit5结合使用时,如何将模型属性传递给Thymeleaf

问题描述

我对Spring和测试还很陌生。我有一个简单的应用程序,其中使用Thymeleaf并通过控制器中Model的.addAttribute方法传递TITLE文本属性

尝试使用带有WebMvcTest批注的junit5集成测试http响应,它会在到达布局片段中的Thymeleaf TITLE属性时引发异常。当我绕过HEAD片段交换时,它工作正常,因此对我来说很明显,我需要以某种方式将属性传递给模拟的Web上下文,但在任何地方都找不到简单的答案。

@Controller
public class IndexController {

    @RequestMapping({"","/","index","index.html"})
    public String getIndexPage(Model model){

        model.addAttribute("title_text","an index title");
        return "index";
    }
}

在index.html页面中只是一个替换命令,用于从布局片段中添加HEAD;

<head th:replace="fragments/layout :: head_tag">
    <title>Bringbackdada.com</title>
</head>

在layout.html中是引发异常的片段的TITLE标记

<head th:fragment="head_tag">
    <Meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
    <Meta charset="utf-8">
    <Meta name="viewport" content="width=device-width,initial-scale=1">

    <title th:text="${title_text}">title text</title>

    <link rel="stylesheet" th:href="@{/resources/css/main.css}"/>
</head>

还有我要执行的集成测试;

@WebMvcTest(controllers = IndexController.class)
class IndexControllerIntegrationTest {

    @Autowired
    private mockmvc mockmvc;

    @Test
    void getIndexPageResponse() throws Exception {
        mockmvc.perform(get("/"))
                .andExpect(status().isOk());
    }
}

还有堆栈跟踪;

org.springframework.web.util.nestedservletexception: Request processing Failed; nested exception is org.thymeleaf.exceptions.TemplateProcessingException: Error during execution of processor 'org.thymeleaf.Spring5.processor.SpringHrefTagProcessor' (template: "fragments/layout" - line 14,col 28)

    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1014)
    at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:626)
    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:733)
    at org.springframework.mock.web.MockFilterChain$ServletFilterProxy.doFilter(MockFilterChain.java:167)
    at org.springframework.mock.web.MockFilterChain.doFilter(MockFilterChain.java:134)
    at org.springframework.web.servlet.resource.ResourceUrlEncodingFilter.doFilter(ResourceUrlEncodingFilter.java:64)
    at org.springframework.mock.web.MockFilterChain.doFilter(MockFilterChain.java:134)
    at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
    at org.springframework.mock.web.MockFilterChain.doFilter(MockFilterChain.java:134)
    at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
    at org.springframework.mock.web.MockFilterChain.doFilter(MockFilterChain.java:134)
    at org.springframework.test.web.servlet.mockmvc.perform(mockmvc.java:183)
    at com.bringbackdada.site.controllers.IndexControllerIntegrationTest.getIndexPageResponse(IndexControllerIntegrationTest.java:22)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:566)
    at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:686)
    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$6(TestMethodTestDescriptor.java:212)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:208)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:137)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:71)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:135)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125)
    at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1540)
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:139)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125)
    at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1540)
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:139)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125)
    at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80)
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:32)
    at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
    at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:51)
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:248)
    at org.junit.platform.launcher.core.DefaultLauncher.lambda$execute$5(DefaultLauncher.java:211)
    at org.junit.platform.launcher.core.DefaultLauncher.withInterceptedStreams(DefaultLauncher.java:226)
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:199)
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:132)
    at com.intellij.junit5.junit5IdeaTestRunner.startRunnerWithArgs(junit5IdeaTestRunner.java:71)
    at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
    at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:220)
    at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:53)
Caused by: org.thymeleaf.exceptions.TemplateProcessingException: Error during execution of processor 'org.thymeleaf.Spring5.processor.SpringHrefTagProcessor' (template: "fragments/layout" - line 14,col 28)
    at org.thymeleaf.processor.element.AbstractAttributeTagProcessor.doProcess(AbstractAttributeTagProcessor.java:117)
    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.handleStandaloneElement(ProcessorTemplateHandler.java:918)
    at org.thymeleaf.engine.StandaloneElementTag.beHandled(StandaloneElementTag.java:228)
    at org.thymeleaf.engine.Model.process(Model.java:282)
    at org.thymeleaf.engine.ProcessorTemplateHandler.handleOpenElement(ProcessorTemplateHandler.java:1587)
    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:362)
    at org.thymeleaf.Spring5.view.ThymeleafView.render(ThymeleafView.java:189)
    at org.springframework.web.servlet.dispatcherServlet.render(dispatcherServlet.java:1373)
    at org.springframework.test.web.servlet.TestdispatcherServlet.render(TestdispatcherServlet.java:136)
    at org.springframework.web.servlet.dispatcherServlet.processdispatchResult(dispatcherServlet.java:1118)
    at org.springframework.web.servlet.dispatcherServlet.dodispatch(dispatcherServlet.java:1057)
    at org.springframework.web.servlet.dispatcherServlet.doService(dispatcherServlet.java:943)
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)
    ... 80 more
Caused by: java.lang.NullPointerException
    at org.webjars.WebJarassetLocator.getFullPathExact(WebJarassetLocator.java:262)
    at org.springframework.web.servlet.resource.WebJarsResourceResolver.findWebJarResourcePath(WebJarsResourceResolver.java:109)
    at org.springframework.web.servlet.resource.WebJarsResourceResolver.resolveUrlPathInternal(WebJarsResourceResolver.java:94)
    at org.springframework.web.servlet.resource.AbstractResourceResolver.resolveUrlPath(AbstractResourceResolver.java:54)
    at org.springframework.web.servlet.resource.DefaultResourceResolverChain.resolveUrlPath(DefaultResourceResolverChain.java:82)
    at org.springframework.web.servlet.resource.CachingResourceResolver.resolveUrlPathInternal(CachingResourceResolver.java:170)
    at org.springframework.web.servlet.resource.AbstractResourceResolver.resolveUrlPath(AbstractResourceResolver.java:54)
    at org.springframework.web.servlet.resource.DefaultResourceResolverChain.resolveUrlPath(DefaultResourceResolverChain.java:82)
    at org.springframework.web.servlet.resource.ResourceUrlProvider.getForLookupPath(ResourceUrlProvider.java:239)
    at org.springframework.web.servlet.resource.ResourceUrlEncodingFilter$ResourceUrlEncodingRequestWrapper.resolveUrlPath(ResourceUrlEncodingFilter.java:124)
    at org.springframework.web.servlet.resource.ResourceUrlEncodingFilter$ResourceUrlEncodingResponseWrapper.encodeURL(ResourceUrlEncodingFilter.java:157)
    at org.thymeleaf.linkbuilder.StandardLinkBuilder.processLink(StandardLinkBuilder.java:528)
    at org.thymeleaf.linkbuilder.StandardLinkBuilder.buildLink(StandardLinkBuilder.java:138)
    at org.thymeleaf.context.AbstractEngineContext.buildLink(AbstractEngineContext.java:167)
    at org.thymeleaf.standard.expression.LinkExpression.executeLinkExpression(LinkExpression.java:290)
    at org.thymeleaf.standard.expression.SimpleExpression.executeSimple(SimpleExpression.java:85)
    at org.thymeleaf.standard.expression.Expression.execute(Expression.java:109)
    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)
    ... 99 more

编辑:事实证明,自从我上个小时才使用代码以来,Maven打包中的一个问题已以某种方式损坏。

解决方法

好吧,所以我终于把它追溯到了Maven的一个小问题。适当清洁并重新包装即可。直到我尝试运行该应用程序时,它才变得显而易见,并在启动时出现了相同的错误。刚刚了解到,注意堆栈跟踪中的细节会带来很多好处。仍然不确定在Maven构建中资源如何损坏,因为我没有进行任何更改。使用Spring开发工具猜测“构建项目”在途中起到了作用……