001/* 002 * Copyright 2002-2019 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.test.context.junit.jupiter; 018 019import java.lang.reflect.Constructor; 020import java.lang.reflect.Executable; 021import java.lang.reflect.Method; 022import java.lang.reflect.Parameter; 023 024import org.junit.jupiter.api.extension.AfterAllCallback; 025import org.junit.jupiter.api.extension.AfterEachCallback; 026import org.junit.jupiter.api.extension.AfterTestExecutionCallback; 027import org.junit.jupiter.api.extension.BeforeAllCallback; 028import org.junit.jupiter.api.extension.BeforeEachCallback; 029import org.junit.jupiter.api.extension.BeforeTestExecutionCallback; 030import org.junit.jupiter.api.extension.ExtensionContext; 031import org.junit.jupiter.api.extension.ExtensionContext.Namespace; 032import org.junit.jupiter.api.extension.ExtensionContext.Store; 033import org.junit.jupiter.api.extension.ParameterContext; 034import org.junit.jupiter.api.extension.ParameterResolver; 035import org.junit.jupiter.api.extension.TestInstancePostProcessor; 036 037import org.springframework.beans.factory.annotation.ParameterResolutionDelegate; 038import org.springframework.context.ApplicationContext; 039import org.springframework.lang.Nullable; 040import org.springframework.test.context.TestConstructor; 041import org.springframework.test.context.TestContextManager; 042import org.springframework.test.context.support.TestConstructorUtils; 043import org.springframework.util.Assert; 044 045/** 046 * {@code SpringExtension} integrates the <em>Spring TestContext Framework</em> 047 * into JUnit 5's <em>Jupiter</em> programming model. 048 * 049 * <p>To use this extension, simply annotate a JUnit Jupiter based test class with 050 * {@code @ExtendWith(SpringExtension.class)}, {@code @SpringJUnitConfig}, or 051 * {@code @SpringJUnitWebConfig}. 052 * 053 * @author Sam Brannen 054 * @since 5.0 055 * @see org.springframework.test.context.junit.jupiter.EnabledIf 056 * @see org.springframework.test.context.junit.jupiter.DisabledIf 057 * @see org.springframework.test.context.junit.jupiter.SpringJUnitConfig 058 * @see org.springframework.test.context.junit.jupiter.web.SpringJUnitWebConfig 059 * @see org.springframework.test.context.TestContextManager 060 */ 061public class SpringExtension implements BeforeAllCallback, AfterAllCallback, TestInstancePostProcessor, 062 BeforeEachCallback, AfterEachCallback, BeforeTestExecutionCallback, AfterTestExecutionCallback, 063 ParameterResolver { 064 065 /** 066 * {@link Namespace} in which {@code TestContextManagers} are stored, 067 * keyed by test class. 068 */ 069 private static final Namespace NAMESPACE = Namespace.create(SpringExtension.class); 070 071 072 /** 073 * Delegates to {@link TestContextManager#beforeTestClass}. 074 */ 075 @Override 076 public void beforeAll(ExtensionContext context) throws Exception { 077 getTestContextManager(context).beforeTestClass(); 078 } 079 080 /** 081 * Delegates to {@link TestContextManager#afterTestClass}. 082 */ 083 @Override 084 public void afterAll(ExtensionContext context) throws Exception { 085 try { 086 getTestContextManager(context).afterTestClass(); 087 } 088 finally { 089 getStore(context).remove(context.getRequiredTestClass()); 090 } 091 } 092 093 /** 094 * Delegates to {@link TestContextManager#prepareTestInstance}. 095 */ 096 @Override 097 public void postProcessTestInstance(Object testInstance, ExtensionContext context) throws Exception { 098 getTestContextManager(context).prepareTestInstance(testInstance); 099 } 100 101 /** 102 * Delegates to {@link TestContextManager#beforeTestMethod}. 103 */ 104 @Override 105 public void beforeEach(ExtensionContext context) throws Exception { 106 Object testInstance = context.getRequiredTestInstance(); 107 Method testMethod = context.getRequiredTestMethod(); 108 getTestContextManager(context).beforeTestMethod(testInstance, testMethod); 109 } 110 111 /** 112 * Delegates to {@link TestContextManager#beforeTestExecution}. 113 */ 114 @Override 115 public void beforeTestExecution(ExtensionContext context) throws Exception { 116 Object testInstance = context.getRequiredTestInstance(); 117 Method testMethod = context.getRequiredTestMethod(); 118 getTestContextManager(context).beforeTestExecution(testInstance, testMethod); 119 } 120 121 /** 122 * Delegates to {@link TestContextManager#afterTestExecution}. 123 */ 124 @Override 125 public void afterTestExecution(ExtensionContext context) throws Exception { 126 Object testInstance = context.getRequiredTestInstance(); 127 Method testMethod = context.getRequiredTestMethod(); 128 Throwable testException = context.getExecutionException().orElse(null); 129 getTestContextManager(context).afterTestExecution(testInstance, testMethod, testException); 130 } 131 132 /** 133 * Delegates to {@link TestContextManager#afterTestMethod}. 134 */ 135 @Override 136 public void afterEach(ExtensionContext context) throws Exception { 137 Object testInstance = context.getRequiredTestInstance(); 138 Method testMethod = context.getRequiredTestMethod(); 139 Throwable testException = context.getExecutionException().orElse(null); 140 getTestContextManager(context).afterTestMethod(testInstance, testMethod, testException); 141 } 142 143 /** 144 * Determine if the value for the {@link Parameter} in the supplied {@link ParameterContext} 145 * should be autowired from the test's {@link ApplicationContext}. 146 * <p>A parameter is considered to be autowirable if one of the following 147 * conditions is {@code true}. 148 * <ol> 149 * <li>The {@linkplain ParameterContext#getDeclaringExecutable() declaring 150 * executable} is a {@link Constructor} and 151 * {@link TestConstructorUtils#isAutowirableConstructor(Constructor, Class)} 152 * returns {@code true}.</li> 153 * <li>The parameter is of type {@link ApplicationContext} or a sub-type thereof.</li> 154 * <li>{@link ParameterResolutionDelegate#isAutowirable} returns {@code true}.</li> 155 * </ol> 156 * <p><strong>WARNING</strong>: If a test class {@code Constructor} is annotated 157 * with {@code @Autowired} or automatically autowirable (see {@link TestConstructor}), 158 * Spring will assume the responsibility for resolving all parameters in the 159 * constructor. Consequently, no other registered {@link ParameterResolver} 160 * will be able to resolve parameters. 161 * @see #resolveParameter 162 * @see TestConstructorUtils#isAutowirableConstructor(Constructor, Class) 163 * @see ParameterResolutionDelegate#isAutowirable 164 */ 165 @Override 166 public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) { 167 Parameter parameter = parameterContext.getParameter(); 168 Executable executable = parameter.getDeclaringExecutable(); 169 Class<?> testClass = extensionContext.getRequiredTestClass(); 170 return (TestConstructorUtils.isAutowirableConstructor(executable, testClass) || 171 ApplicationContext.class.isAssignableFrom(parameter.getType()) || 172 ParameterResolutionDelegate.isAutowirable(parameter, parameterContext.getIndex())); 173 } 174 175 /** 176 * Resolve a value for the {@link Parameter} in the supplied {@link ParameterContext} by 177 * retrieving the corresponding dependency from the test's {@link ApplicationContext}. 178 * <p>Delegates to {@link ParameterResolutionDelegate#resolveDependency}. 179 * @see #supportsParameter 180 * @see ParameterResolutionDelegate#resolveDependency 181 */ 182 @Override 183 @Nullable 184 public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) { 185 Parameter parameter = parameterContext.getParameter(); 186 int index = parameterContext.getIndex(); 187 Class<?> testClass = extensionContext.getRequiredTestClass(); 188 ApplicationContext applicationContext = getApplicationContext(extensionContext); 189 return ParameterResolutionDelegate.resolveDependency(parameter, index, testClass, 190 applicationContext.getAutowireCapableBeanFactory()); 191 } 192 193 194 /** 195 * Get the {@link ApplicationContext} associated with the supplied {@code ExtensionContext}. 196 * @param context the current {@code ExtensionContext} (never {@code null}) 197 * @return the application context 198 * @throws IllegalStateException if an error occurs while retrieving the application context 199 * @see org.springframework.test.context.TestContext#getApplicationContext() 200 */ 201 public static ApplicationContext getApplicationContext(ExtensionContext context) { 202 return getTestContextManager(context).getTestContext().getApplicationContext(); 203 } 204 205 /** 206 * Get the {@link TestContextManager} associated with the supplied {@code ExtensionContext}. 207 * @return the {@code TestContextManager} (never {@code null}) 208 */ 209 private static TestContextManager getTestContextManager(ExtensionContext context) { 210 Assert.notNull(context, "ExtensionContext must not be null"); 211 Class<?> testClass = context.getRequiredTestClass(); 212 Store store = getStore(context); 213 return store.getOrComputeIfAbsent(testClass, TestContextManager::new, TestContextManager.class); 214 } 215 216 private static Store getStore(ExtensionContext context) { 217 return context.getRoot().getStore(NAMESPACE); 218 } 219 220}