Browse Source

SEC-3074: Add Test Meta Annotation Support

Rob Winch 10 years ago
parent
commit
567c51e109

+ 27 - 0
docs/manual/src/docs/asciidoc/_includes/test.adoc

@@ -249,6 +249,33 @@ final class WithUserDetailsSecurityContextFactory
 }
 ----
 
+[[test-method-meta-annotations]]
+=== Test Meta Annotations
+
+If you reuse the same user within your tests often, it is not ideal to have to repeatedly specify the attributes.
+For example, if there are many tests related to an administrative user with the username "admin" and the roles `ROLE_USER` and `ROLE_ADMIN` you would have to write:
+
+[source,java]
+----
+@WithMockUser(username="admin",roles={"USER","ADMIN"})
+----
+
+Rather than repeating this everywhere, we can use a meta annotation.
+For example, we could create a meta annotation named `WithMockAdmin`:
+
+[source,java]
+----
+@Retention(RetentionPolicy.RUNTIME)
+@WithMockUser(value="rob",roles="ADMIN")
+public @interface WithMockAdmin { }
+----
+
+Now we can use `@WithMockAdmin` in the same way as the more verbose `@WithMockUser`.
+
+Meta annotations work with any of the testing annotations described above.
+For example, this means we could create a meta annotation for `@WithUserDetails("admin")` as well.
+
+
 [[test-mockmvc]]
 == Spring MVC Test Integration
 

+ 5 - 1
docs/manual/src/docs/asciidoc/index.adoc

@@ -367,7 +367,11 @@ git clone https://github.com/spring-projects/spring-security.git
 This will give you access to the entire project history (including all releases and branches) on your local machine.
 
 [[new]]
-== What's new in Spring Security 4.0
+== What's new in Spring Security 4.1
+
+* <<test-method-meta-annotations>>
+
+=== What's new in Spring Security 4.0
 
 There are http://goo.gl/ui9GCl[175+ tickets resolved] with the Spring Security 4.0 release.
 

+ 18 - 18
test/src/main/java/org/springframework/security/test/context/support/WithSecurityContextTestExecutionListener.java

@@ -16,10 +16,13 @@
 package org.springframework.security.test.context.support;
 
 import java.lang.annotation.Annotation;
+import java.lang.reflect.AnnotatedElement;
+import java.lang.reflect.TypeVariable;
 
 import org.springframework.beans.BeanUtils;
 import org.springframework.context.ApplicationContext;
 import org.springframework.context.support.StaticApplicationContext;
+import org.springframework.core.GenericTypeResolver;
 import org.springframework.core.annotation.AnnotationUtils;
 import org.springframework.core.annotation.Order;
 import org.springframework.security.core.context.SecurityContext;
@@ -59,13 +62,10 @@ public class WithSecurityContextTestExecutionListener extends
 	 */
 	@Override
 	public void beforeTestMethod(TestContext testContext) throws Exception {
-		Annotation[] methodAnnotations = AnnotationUtils.getAnnotations(testContext
-				.getTestMethod());
-		SecurityContext securityContext = createSecurityContext(methodAnnotations,
+		SecurityContext securityContext = createSecurityContext(testContext.getTestMethod(),
 				testContext);
 		if (securityContext == null) {
-			Annotation[] classAnnotations = testContext.getTestClass().getAnnotations();
-			securityContext = createSecurityContext(classAnnotations, testContext);
+			securityContext = createSecurityContext(testContext.getTestClass(), testContext);
 		}
 		if (securityContext != null) {
 			TestSecurityContextHolder.setContext(securityContext);
@@ -73,20 +73,20 @@ public class WithSecurityContextTestExecutionListener extends
 	}
 
 	@SuppressWarnings({ "rawtypes", "unchecked" })
-	private SecurityContext createSecurityContext(Annotation[] annotations,
+	private SecurityContext createSecurityContext(AnnotatedElement annotated,
 			TestContext context) {
-		for (Annotation a : annotations) {
-			WithSecurityContext withUser = AnnotationUtils.findAnnotation(
-					a.annotationType(), WithSecurityContext.class);
-			if (withUser != null) {
-				WithSecurityContextFactory factory = createFactory(withUser, context);
-				try {
-					return factory.createSecurityContext(a);
-				}
-				catch (RuntimeException e) {
-					throw new IllegalStateException(
-							"Unable to create SecurityContext using " + a, e);
-				}
+		WithSecurityContext withUser = AnnotationUtils.findAnnotation(
+				annotated, WithSecurityContext.class);
+		if (withUser != null) {
+			WithSecurityContextFactory factory = createFactory(withUser, context);
+			Class<? extends Annotation> type = (Class<? extends Annotation>) GenericTypeResolver.resolveTypeArgument(factory.getClass(), WithSecurityContextFactory.class);
+			Annotation annotation  = AnnotationUtils.findAnnotation(annotated, type);
+			try {
+				return factory.createSecurityContext(annotation);
+			}
+			catch (RuntimeException e) {
+				throw new IllegalStateException(
+						"Unable to create SecurityContext using " + annotation, e);
 			}
 		}
 		return null;

+ 33 - 0
test/src/test/java/org/springframework/security/test/web/servlet/showcase/secured/WithAdminRob.java

@@ -0,0 +1,33 @@
+/*
+ * Copyright 2002-2015 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.security.test.web.servlet.showcase.secured;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.springframework.security.test.context.support.WithMockUser;
+
+@Target({ ElementType.METHOD, ElementType.TYPE })
+@Retention(RetentionPolicy.RUNTIME)
+@Inherited
+@Documented
+@WithMockUser(value="rob",roles="ADMIN")
+public @interface WithAdminRob {
+}

+ 10 - 0
test/src/test/java/org/springframework/security/test/web/servlet/showcase/secured/WithUserAuthenticationTests.java

@@ -64,6 +64,16 @@ public class WithUserAuthenticationTests {
 				.andExpect(authenticated().withUsername("user"));
 	}
 
+	@Test
+	@WithAdminRob
+	public void requestProtectedUrlWithAdminRob() throws Exception {
+		mvc.perform(get("/"))
+		// Ensure we got past Security
+				.andExpect(status().isNotFound())
+				// Ensure it appears we are authenticated with user
+				.andExpect(authenticated().withUsername("rob").withRoles("ADMIN"));
+	}
+
 	@Test
 	@WithMockUser(roles = "ADMIN")
 	public void requestProtectedUrlWithAdmin() throws Exception {