Last active
August 29, 2015 14:25
-
-
Save josefbetancourt/98756136cc30d15c1b67 to your computer and use it in GitHub Desktop.
Scan classpath for classes with @RequiresTest annotated methods
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.octodecillion.test; | |
import java.lang.reflect.Method; | |
import java.util.List; | |
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider; | |
import org.springframework.core.type.ClassMetadata; | |
import org.springframework.core.type.classreading.MetadataReader; | |
import org.springframework.core.type.classreading.MetadataReaderFactory; | |
import org.springframework.core.type.filter.TypeFilter; | |
import org.springframework.util.ClassUtils; | |
import com.google.common.base.Preconditions; | |
import com.google.common.collect.ArrayListMultimap; | |
import com.google.common.collect.Lists; | |
import com.google.common.collect.Multimap; | |
/** | |
* | |
* Get all methods that require a test. | |
* <p> | |
* Example use: | |
* <pre> | |
* Multimap<Class<?>, Method> map = new ShouldTestAnnotationScanner().doScan("com.anywhere"); | |
* </pre> | |
* | |
* @author josef betancourt | |
* | |
*/ | |
public class RequiresTestAnnotationScanner { | |
/** | |
* | |
* @param basePackage where search will start | |
* @return multi map of classes and methods requiring tests | |
*/ | |
public Multimap<Class<?>, Method> doScan(String basePackage) { | |
final Multimap<Class<?>, Method> multiMap = ArrayListMultimap.create(); | |
ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(false); | |
provider.addIncludeFilter(new ShouldTestAnnotationFilter(multiMap)); | |
provider.findCandidateComponents(basePackage); | |
return multiMap; | |
} | |
/** Filters methods with RequiresTest annotation. */ | |
private static class ShouldTestAnnotationFilter implements TypeFilter { | |
private Multimap<Class<?>, Method> classMethodsMap; | |
/** | |
* Create a filter. | |
* <p> | |
* Updates the provided map with found classes and methods. | |
* | |
* @param map | |
* Multimap, not null | |
*/ | |
public ShouldTestAnnotationFilter(final Multimap<Class<?>, Method> map) { | |
Preconditions.checkNotNull(map, "param 'map' is null"); | |
this.classMethodsMap = map; | |
} | |
/** | |
* Does the class match? | |
* <p> | |
* Since we don't want to revisit each class later (via the Set returned | |
* from | |
* {@link ClassPathScanningCandidateComponentProvider#findCandidateComponents(String)} | |
* we just do the method lookups and add to map. | |
* | |
* @see TypeFilter#match(MetadataReader, MetadataReaderFactory) | |
*/ | |
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataFactory) { | |
boolean accepted = false; | |
if (!accept(metadataReader.getClassMetadata())) { | |
return accepted; | |
} | |
List<Method> foundMethods = Lists.newArrayList(); | |
try { | |
Class<?> clazz; | |
clazz = Class.forName(metadataReader.getClassMetadata().getClassName(), false, | |
ClassUtils.getDefaultClassLoader()); | |
accepted = scanMethods(clazz, foundMethods); | |
if (accepted) { | |
classMethodsMap.putAll(clazz, foundMethods); | |
} | |
} catch (ClassNotFoundException e) { | |
throw new RuntimeException(e.getMessage(), e); | |
} | |
return accepted; | |
} | |
/** | |
* Test each method in class. | |
* | |
* @param clazz | |
* class being scanned | |
* @param foundMethods | |
* list of methods found | |
* @return | |
* @throws ClassNotFoundException | |
*/ | |
private boolean scanMethods(final Class<?> clazz, final List<Method> foundMethods) { | |
boolean accepted = false; | |
for (Method method : clazz.getMethods()) { | |
if (accept(method)) { | |
accepted = true; | |
foundMethods.add(method); | |
} | |
} | |
return accepted; | |
} | |
/** | |
* Does method have RequiresTest annotation. | |
* | |
* @param method | |
* @return | |
*/ | |
private boolean accept(Method method) { | |
return method.getAnnotation(RequiresTest.class) != null; | |
} | |
/** | |
* Accept if class is non-nested and non-abstract. | |
* | |
* @param meta | |
* {@link ClassMetadata} | |
* @return true if acceptable | |
*/ | |
private boolean accept(ClassMetadata meta) { | |
return (!meta.hasEnclosingClass() && meta.isConcrete()); | |
} | |
} // end ShouldTestAnnotationFilter | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* | |
*/ | |
package com.octodecillion.test; | |
import java.lang.reflect.Method; | |
import org.junit.Assert; | |
import org.junit.Test; | |
import com.google.common.collect.Multimap; | |
/** | |
* @author jbetancourt | |
* | |
*/ | |
public class RequiresTestAnnotationScannerTest { | |
private static boolean debug = true; | |
/** | |
* Test method for {@link com.octodecillion.test.RequiresTestAnnotationScanner#getShouldTestMethods(java.lang.String, boolean)}. | |
* @throws Exception | |
*/ | |
@Test | |
public void shouldFindRequiredList() throws Exception { | |
RequiresTestAnnotationScanner scanner = new RequiresTestAnnotationScanner(); | |
Multimap<Class<?>, Method> list = scanner.doScan("com.anywhere"); | |
if(debug ){ | |
System.out.println(list); | |
} | |
Assert.assertTrue("Did not find any required tests!", list.size()>0); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment