Skip to content

Instantly share code, notes, and snippets.

@josefbetancourt
Last active August 29, 2015 14:25
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save josefbetancourt/98756136cc30d15c1b67 to your computer and use it in GitHub Desktop.
Save josefbetancourt/98756136cc30d15c1b67 to your computer and use it in GitHub Desktop.
Scan classpath for classes with @RequiresTest annotated methods
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
}
/**
*
*/
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