Java SpringMVC:使用TestWebApplicationContext和嵌入式Jetty进行UI测试

Java SpringMVC:使用TestWebApplicationContext和嵌入式Jetty进行UI测试,java,spring,spring-mvc,selenium-webdriver,embedded-jetty,Java,Spring,Spring Mvc,Selenium Webdriver,Embedded Jetty,tldr如何从嵌入式jetty中的WebApplicationContext部署一个简单的自动测试用例 我所拥有的: 一个spring应用程序(不是SpringBoot),它使用注释配置(没有web.xml)并为生产构建(使用gradle)到ear并部署在wildfly上 我最终想要做的是: 具有自包含(在应用程序测试包中)的ui测试,可以像其他单元或集成测试一样轻松执行(无需构建和部署) 到目前为止我所做的: 简单的部分:使用SeleniumWebDriver设置实际测试 困难的部分:使用测

tldr如何从嵌入式jetty中的WebApplicationContext部署一个简单的自动测试用例

我所拥有的: 一个spring应用程序(不是SpringBoot),它使用注释配置(没有web.xml)并为生产构建(使用gradle)到ear并部署在wildfly上

我最终想要做的是: 具有自包含(在应用程序测试包中)的ui测试,可以像其他单元或集成测试一样轻松执行(无需构建和部署)

到目前为止我所做的:
  • 简单的部分:使用SeleniumWebDriver设置实际测试
  • 困难的部分:使用测试WebApplicationContext启动嵌入式Jetty
我为嵌入式Jetty编写的代码(是的,我会在它运行时进行一些重构):

资源库的位置:

所有静态资源(如css、js和图像)都位于主题中

什么是有效的:
  • 控制器在请求时被命中
  • Spring Security处于活动状态
  • 找到并呈现JSP(感谢)
什么不起作用:
  • 缺少所有其他静态资源(css、js、图像等)。基本上从主题到主题的所有内容
那么,我的问题是什么:
  • 如何使缺少的静态资源可用
  • 是否有完全不同/更好的方法来实现我的最终目标?到目前为止,我所面临的问题相当棘手,我不确定丢失的静态内容是否是我最后的问题
  • 我很乐意提供更多信息/代码。但是,由于这是一个实际的产品,我有一些限制

    编辑:
  • 请注意application.war只是一个文件夹,实际上不是一个war

  • 资源配置


  • ResourceHandler使用哪种情况并不重要。两者都不起作用。

    主题文件在哪里?(希望在
    src/website/application.war
    )被引用。您可能希望在问题中描述如何配置mvc资源。-请看:我已编辑了我的问题。希望这是你感兴趣的。谢谢你看。谢谢,这是一个信息丰富得多的问题。可悲的是,我不能帮助春季方面,因为我的专业知识是在码头。但希望春天那边的人能帮上忙。(我将调整标签,使其成为更受欢迎的标签)
    import java.io.File;
    import java.io.FileNotFoundException;
    import java.io.IOException;
    import java.net.MalformedURLException;
    import java.net.URI;
    import java.net.URISyntaxException;
    import java.net.URL;
    import java.net.URLClassLoader;
    import java.util.EnumSet;
    
    import javax.servlet.DispatcherType;
    
    import org.apache.tomcat.util.scan.StandardJarScanner;
    import org.eclipse.jetty.apache.jsp.JettyJasperInitializer;
    import org.eclipse.jetty.jsp.JettyJspServlet;
    import org.eclipse.jetty.server.Server;
    import org.eclipse.jetty.server.session.HashSessionIdManager;
    import org.eclipse.jetty.server.session.HashSessionManager;
    import org.eclipse.jetty.server.session.SessionHandler;
    import org.eclipse.jetty.servlet.FilterHolder;
    import org.eclipse.jetty.servlet.ServletContextHandler;
    import org.eclipse.jetty.servlet.ServletHolder;
    import org.eclipse.jetty.util.component.AbstractLifeCycle;
    import org.eclipse.jetty.util.resource.Resource;
    import org.springframework.security.web.FilterChainProxy;
    import org.springframework.web.context.WebApplicationContext;
    import org.springframework.web.servlet.DispatcherServlet;
    
    public class JettyServerFactory {
    
        private Server server = null;
    
        public Server start(WebApplicationContext context) throws Exception {
            if (server == null) {
                this.server = new Server(9090);
                setupServer(context);
    
                server.start();
                //server.join();
            }
            return server;
        }
    
        public void stop() throws Exception {
            if (server != null) {
                server.stop();
            }
        }
    
        private void setupServer(WebApplicationContext context) throws Exception {
    
            final ServletContextHandler contextHandler = new ServletContextHandler();
    
            // Since this is a ServletContextHandler we must manually configure JSP support.
            enableEmbeddedJspSupport(contextHandler);
    
            // Spring Security
            enableSpringSecurity(contextHandler, context);
    
            /*
            // TODO: Does not seem to have any effect
            // Set reference to actual Spring ApplicationContext
            ServletContext servletContext = contextHandler.getServletContext();
            servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, context);
            */
    
            // Set resource base folder
            contextHandler.setBaseResource(getResourceBase());
    
            // Add DispatcherServlet to context
            final ServletHolder servletHolder = new ServletHolder("default", new DispatcherServlet(context));
            servletHolder.setInitParameter("dirAllowed", "true");
            contextHandler.setContextPath("/oauth");
            contextHandler.addServlet(servletHolder, "/");
    
            // Add context to server
            server.setHandler(contextHandler);
        }
    
        private Resource getResourceBase() {
            File file = new File("src/website/application.war");
            //servletHolder.setInitParameter("resourceBase", getWebRootResourceUri().toASCIIString());
            final Resource base = Resource.newResource(file);
            return base;
        }
    
        /**
         * Apply existing securityFilterChain to context
         *
         * @param contextHandler
         * @param context
         */
        private void enableSpringSecurity(ServletContextHandler contextHandler, WebApplicationContext context) {
    
            FilterChainProxy filter = (FilterChainProxy) context.getBean("springSecurityFilterChain");
            FilterHolder filterHolder = new FilterHolder(filter);
            contextHandler.addFilter(filterHolder, "/*", EnumSet.allOf(DispatcherType.class));
    
            server.setSessionIdManager(new HashSessionIdManager());
    
            HashSessionManager manager = new HashSessionManager();
            SessionHandler sessions = new SessionHandler(manager);
            contextHandler.setHandler(sessions);
        }
    
        /**
         * Setup JSP Support for ServletContextHandlers.
         * <p>
         * NOTE: This is not required or appropriate if using a WebAppContext.
         * </p>
         *
         * @param servletContextHandler the ServletContextHandler to configure
         * @throws IOException if unable to configure
         */
        private void enableEmbeddedJspSupport(ServletContextHandler servletContextHandler) throws IOException {
            // Establish Scratch directory for the servlet context (used by JSP compilation)
            File tempDir = new File(System.getProperty("java.io.tmpdir"));
            File scratchDir = new File(tempDir.toString(), "embedded-jetty-jsp");
    
            if (!scratchDir.exists()) {
                if (!scratchDir.mkdirs()) {
                    throw new IOException("Unable to create scratch directory: " + scratchDir);
                }
            }
            servletContextHandler.setAttribute("javax.servlet.context.tempdir", scratchDir);
    
            // Set Classloader of Context to be sane (needed for JSTL)
            // JSP requires a non-System classloader, this simply wraps the
            // embedded System classloader in a way that makes it suitable
            // for JSP to use
            ClassLoader jspClassLoader = new URLClassLoader(new URL[0], this.getClass().getClassLoader());
            servletContextHandler.setClassLoader(jspClassLoader);
    
            // Manually call JettyJasperInitializer on context startup
            servletContextHandler.addBean(new JspStarter(servletContextHandler));
    
            // Create / Register JSP Servlet (must be named "jsp" per spec)
            ServletHolder holderJsp = new ServletHolder("jsp", JettyJspServlet.class);
            holderJsp.setInitOrder(0);
            holderJsp.setInitParameter("logVerbosityLevel", "DEBUG");
            holderJsp.setInitParameter("fork", "false");
            holderJsp.setInitParameter("xpoweredBy", "false");
            holderJsp.setInitParameter("compilerTargetVM", "1.8");
            holderJsp.setInitParameter("compilerSourceVM", "1.8");
            holderJsp.setInitParameter("keepgenerated", "true");
            servletContextHandler.addServlet(holderJsp, "*.jsp");
        }
    
        /**
         * JspStarter for embedded ServletContextHandlers
         *
         * This is added as a bean that is a jetty LifeCycle on the ServletContextHandler. This bean's doStart method will
         * be called as the ServletContextHandler starts, and will call the ServletContainerInitializer for the jsp engine.
         *
         */
        public static class JspStarter extends AbstractLifeCycle
                implements ServletContextHandler.ServletContainerInitializerCaller {
            JettyJasperInitializer sci;
            ServletContextHandler context;
    
            public JspStarter(ServletContextHandler context) {
                this.sci = new JettyJasperInitializer();
                this.context = context;
                this.context.setAttribute("org.apache.tomcat.JarScanner", new StandardJarScanner());
            }
    
            @Override
            protected void doStart() throws Exception {
                ClassLoader old = Thread.currentThread().getContextClassLoader();
                Thread.currentThread().setContextClassLoader(context.getClassLoader());
                try {
                    sci.onStartup(null, context.getServletContext());
                    super.doStart();
                } finally {
                    Thread.currentThread().setContextClassLoader(old);
                }
            }
        }
    
        private URI getWebRootResourceUri() throws FileNotFoundException, URISyntaxException, MalformedURLException {
            String WEBROOT_INDEX = "/application.war/";
    
            URL indexUri = this.getClass().getResource(WEBROOT_INDEX);
    
            if (indexUri == null) {
                throw new FileNotFoundException("Unable to find resource " + WEBROOT_INDEX);
            }
    
            File file = new File("src/website/application.war");
            System.out.println(indexUri.toURI());
            System.out.println(file.toURI());
            System.out.println(file.getAbsolutePath());
    
            return file.toURI();
        }
    }
    
    
    ...
    @WebAppConfiguration
    public class ATestClass {
    
        @Autowired
        WebApplicationContext context;
    
        ...
        @Before
        public void setupTest() throws Exception {
            new JettyServerFactory().start(context);
            ...
        }
        ...
    ...
    
    @EnableWebMvc
    @Configuration
    public class AppWebMvcConfiguration implements WebMvcConfigurer {
        ...
    
        @Override
        public void configureViewResolvers(ViewResolverRegistry registry) {
            registry.jsp("/WEB-INF/jsp/", ".jsp");
        }
    
        @Override
        public void addResourceHandlers(ResourceHandlerRegistry registry) {
            boolean developmentMode = configuration.getBooleanFromNewconfig(false, "oauth2.developmentMode");
            if (USE_VERSIONED_RESOURCES) {
                registry.addResourceHandler("/themes/**")
                        .addResourceLocations("/themes/")
                        .setCacheControl(CacheControl.maxAge(365, TimeUnit.DAYS))
                        .resourceChain(developmentMode ? false : true)
                        .addResolver(new VersionResourceResolver().addContentVersionStrategy("/**"))
                        .addTransformer(new CssLinkResourceTransformer());
            } else {
                registry.addResourceHandler("/themes/**")
                        .addResourceLocations("/themes/")
                        .setCacheControl(CacheControl.noCache());
            }
        }
    
        ...
    }