• 聊聊SpringBootTest的webEnvironment


    本文主要研究一下SpringBootTest的webEnvironment

    SpringBootTest

    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    @BootstrapWith(SpringBootTestContextBootstrapper.class)
    @ExtendWith({SpringExtension.class})
    public @interface SpringBootTest {
        @AliasFor("properties")
        String[] value() default {};
    
        @AliasFor("value")
        String[] properties() default {};
    
        String[] args() default {};
    
        Class[] classes() default {};
    
        WebEnvironment webEnvironment() default SpringBootTest.WebEnvironment.MOCK;
    }    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    SpringBootTest的webEnvironment默认为SpringBootTest.WebEnvironment.MOCK

    WebEnvironment

    	/**
    	 * An enumeration web environment modes.
    	 */
    	enum WebEnvironment {
    
    		/**
    		 * Creates a {@link WebApplicationContext} with a mock servlet environment if
    		 * servlet APIs are on the classpath, a {@link ReactiveWebApplicationContext} if
    		 * Spring WebFlux is on the classpath or a regular {@link ApplicationContext}
    		 * otherwise.
    		 */
    		MOCK(false),
    
    		/**
    		 * Creates a web application context (reactive or servlet based) and sets a
    		 * {@code server.port=0} {@link Environment} property (which usually triggers
    		 * listening on a random port). Often used in conjunction with a
    		 * {@link LocalServerPort @LocalServerPort} injected field on the test.
    		 */
    		RANDOM_PORT(true),
    
    		/**
    		 * Creates a (reactive) web application context without defining any
    		 * {@code server.port=0} {@link Environment} property.
    		 */
    		DEFINED_PORT(true),
    
    		/**
    		 * Creates an {@link ApplicationContext} and sets
    		 * {@link SpringApplication#setWebApplicationType(WebApplicationType)} to
    		 * {@link WebApplicationType#NONE}.
    		 */
    		NONE(false);
    
    		private final boolean embedded;
    
    		WebEnvironment(boolean embedded) {
    			this.embedded = embedded;
    		}
    
    		/**
    		 * Return if the environment uses an {@link ServletWebServerApplicationContext}.
    		 * @return if an {@link ServletWebServerApplicationContext} is used.
    		 */
    		public boolean isEmbedded() {
    			return this.embedded;
    		}
    
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49

    WebEnvironment有四个枚举,分别是MOCK、RANDOM_PORT、DEFINED_PORT、NONE

    SpringBootTestContextBootstrapper

    spring-boot-project/spring-boot-test/src/main/java/org/springframework/boot/test/context/SpringBootTestContextBootstrapper.java

    public class SpringBootTestContextBootstrapper extends DefaultTestContextBootstrapper {
    
    	private static final String[] WEB_ENVIRONMENT_CLASSES = { "javax.servlet.Servlet",
    			"org.springframework.web.context.ConfigurableWebApplicationContext" };
    
    	private static final String REACTIVE_WEB_ENVIRONMENT_CLASS = "org.springframework."
    			+ "web.reactive.DispatcherHandler";
    
    	private static final String MVC_WEB_ENVIRONMENT_CLASS = "org.springframework.web.servlet.DispatcherServlet";
    
    	private static final String JERSEY_WEB_ENVIRONMENT_CLASS = "org.glassfish.jersey.server.ResourceConfig";
    
    	private static final String ACTIVATE_SERVLET_LISTENER = "org.springframework.test."
    			+ "context.web.ServletTestExecutionListener.activateListener";
    
    	private static final Log logger = LogFactory.getLog(SpringBootTestContextBootstrapper.class);
    
    	@Override
    	public TestContext buildTestContext() {
    		TestContext context = super.buildTestContext();
    		verifyConfiguration(context.getTestClass());
    		WebEnvironment webEnvironment = getWebEnvironment(context.getTestClass());
    		if (webEnvironment == WebEnvironment.MOCK && deduceWebApplicationType() == WebApplicationType.SERVLET) {
    			context.setAttribute(ACTIVATE_SERVLET_LISTENER, true);
    		}
    		else if (webEnvironment != null && webEnvironment.isEmbedded()) {
    			context.setAttribute(ACTIVATE_SERVLET_LISTENER, false);
    		}
    		return context;
    	}
    
    	//......
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    SpringBootTestContextBootstrapper继承了DefaultTestContextBootstrapper,其buildTestContext方法会判断webEnvironment,然后决定ACTIVATE_SERVLET_LISTENER是设置为true还是false,在为MOCK的时候该值为true

    ServletTestExecutionListener

    spring-test/src/main/java/org/springframework/test/context/web/ServletTestExecutionListener.java

        private boolean isActivated(TestContext testContext) {
            return Boolean.TRUE.equals(testContext.getAttribute(ACTIVATE_LISTENER)) || AnnotatedElementUtils.hasAnnotation(testContext.getTestClass(), WebAppConfiguration.class);
        }
    
        private void setUpRequestContextIfNecessary(TestContext testContext) {
    		if (!isActivated(testContext) || alreadyPopulatedRequestContextHolder(testContext)) {
    			return;
    		}
    
    		ApplicationContext context = testContext.getApplicationContext();
    
    		if (context instanceof WebApplicationContext) {
    			WebApplicationContext wac = (WebApplicationContext) context;
    			ServletContext servletContext = wac.getServletContext();
    			Assert.state(servletContext instanceof MockServletContext, () -> String.format(
    						"The WebApplicationContext for test context %s must be configured with a MockServletContext.",
    						testContext));
    
    			if (logger.isDebugEnabled()) {
    				logger.debug(String.format(
    						"Setting up MockHttpServletRequest, MockHttpServletResponse, ServletWebRequest, and RequestContextHolder for test context %s.",
    						testContext));
    			}
    
    			MockServletContext mockServletContext = (MockServletContext) servletContext;
    			MockHttpServletRequest request = new MockHttpServletRequest(mockServletContext);
    			request.setAttribute(CREATED_BY_THE_TESTCONTEXT_FRAMEWORK, Boolean.TRUE);
    			MockHttpServletResponse response = new MockHttpServletResponse();
    			ServletWebRequest servletWebRequest = new ServletWebRequest(request, response);
    
    			RequestContextHolder.setRequestAttributes(servletWebRequest);
    			testContext.setAttribute(POPULATED_REQUEST_CONTEXT_HOLDER_ATTRIBUTE, Boolean.TRUE);
    			testContext.setAttribute(RESET_REQUEST_CONTEXT_HOLDER_ATTRIBUTE, Boolean.TRUE);
    
    			if (wac instanceof ConfigurableApplicationContext) {
    				@SuppressWarnings("resource")
    				ConfigurableApplicationContext configurableApplicationContext = (ConfigurableApplicationContext) wac;
    				ConfigurableListableBeanFactory bf = configurableApplicationContext.getBeanFactory();
    				bf.registerResolvableDependency(MockHttpServletResponse.class, response);
    				bf.registerResolvableDependency(ServletWebRequest.class, servletWebRequest);
    			}
    		}
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    ServletTestExecutionListener的isActivated会判断ACTIVATE_SERVLET_LISTENER是不是设置为true,或者testClass有标注@WebAppConfiguration; setUpRequestContextIfNecessary方法会调用isActivated来决定是否初始化MockHttpServletRequest等设置

    小结

    SpringBootTest的webEnvironment默认为SpringBootTest.WebEnvironment.MOCK,它会设置ACTIVATE_SERVLET_LISTENER是设置为true,即在ServletTestExecutionListener的isActivated为true,在setUpRequestContextIfNecessary方法会初始化MockHttpServletRequest、MockHttpServletResponse等。

  • 相关阅读:
    EDA实验-----3-8译码器设计(QuartusII)
    高性能MySQL第四版
    dns域名解析服务和bond网卡
    信息学奥赛一本通2062:【例1.3】电影票
    ModStartCMS 主题入门开发教程
    生产业务环境下部署 Kubernetes 高可用集群的步骤
    leetcode每天5题-Day04
    酒店报修管理系统哪家好?设备巡检系统对酒店运营有什么帮助?
    MATLAB2016笔记(九):概率统计( 概率密度、统计作图、统计特征、累积概率分布、随机变量产生)
    [云原生2.] Kurbernetes资源管理 ---- (陈述式资源管理方式)
  • 原文地址:https://blog.csdn.net/hello_ejb3/article/details/132925570