-
Notifications
You must be signed in to change notification settings - Fork 3.3k
Description
It would be great if I did not have to specifically enumerate all the test classes in a test suite in order to run a specific category. It seems counter intuitive. The only use that @category really has at that point is at the method level, not at the class level.
I came up with a spring based solution (doesn't really require much of spring, but it uses some spring utility classes, in particular ClassPathScanningCandidateComponentProvider). Perhaps something similar could be implemented in JUnit. The downside to this of course is that the @category annotation (or some other annotation) has to be at the class level in order for the class to be found. That being said, we might be able to leave out the filter on the scanning provider, though you run the risk of pulling in too much if you do that.
@RunWith(DiscoverCategories.class)
@IncludeCategory(Foo.class)
@ScanPackage
public class MyTestClass { ... }
And in DiscoverCategories class it's relatively easy to find all the classes in the classpath with the @category annotation:
/***
* Scan for classes for a given test suite. This will use the {@link ScanPackage}
* annotation to look for classes with the {@link Category} annotation.
*
* @param suiteClass
* @return
* @throws InitializationError
*/
private static Class<?>[] scanClassesWithCategoryAnnotation(Class<?> suiteClass)
throws InitializationError {
String packages[] = null;
if (suiteClass.isAnnotationPresent(ScanPackage.class)) {
packages = suiteClass.getAnnotation(ScanPackage.class).value();
}
return scanClassesWithCategoryAnnotation(packages);
}
/***
* Scan for classes for a given set of packages. This will look for classes
* with the {@link Category} annotation.
*
* @param suiteClass
* @return
* @throws InitializationError
*/
private static Class<?>[] scanClassesWithCategoryAnnotation(String packages[])
throws InitializationError {
ClassPathScanningCandidateComponentProvider provider =
new ClassPathScanningCandidateComponentProvider(false);
provider.addIncludeFilter(new AnnotationTypeFilter(Category.class));
Set<BeanDefinition> allDefs = new HashSet<BeanDefinition>();
if (packages != null) {
for (String pack : packages) {
Set<BeanDefinition> defs = provider.findCandidateComponents(pack);
allDefs.addAll(defs);
}
}
Set<Class<?>> klasses = new HashSet<Class<?>>();
for (BeanDefinition def : allDefs) {
String beanClassName = def.getBeanClassName();
try {
Class<?> klass = Thread.currentThread().getContextClassLoader()
.loadClass(beanClassName);
klasses.add(klass);
}
catch (ClassNotFoundException e) {
throw new InitializationError(e);
}
}
return klasses.toArray(new Class<?>[klasses.size()]);
}
/***
* Public constructor for a test suite class.
*
* @param klass
* @param builder
* @throws InitializationError
*/
public DiscoverCategories(Class<?> klass, RunnerBuilder builder)
throws InitializationError {
super(builder, klass, scanClassesWithCategoryAnnotation(klass));
try {
filter(getCategoryFilter(klass));
}
catch (NoTestsRemainException e) {
throw new InitializationError(e);
}
}