How can I run all JUnit unit tests except those ending in "IntegrationTest" in my IntelliJ IDEA project using the integrated test runner?

10,374

Solution 1

I've written some code to do most of the work. It works only if your files are on the local disk instead of in a JAR. All you need is one class in the package. You could, for this purpose, create a Locator.java class, just to be able to find the package.

public class ClassEnumerator {
    public static void main(String[] args) throws ClassNotFoundException {
        List<Class<?>> list = listClassesInSamePackage(Locator.class, true);

        System.out.println(list);
    }

    private static List<Class<?>> listClassesInSamePackage(Class<?> locator, boolean includeLocator) 
                                                                      throws ClassNotFoundException {

        File packageFile = getPackageFile(locator);

        String ignore = includeLocator ? null : locator.getSimpleName() + ".class";

        return toClassList(locator.getPackage().getName(), listClassNames(packageFile, ignore));
    }

    private static File getPackageFile(Class<?> locator) {
        URL url = locator.getClassLoader().getResource(locator.getName().replace(".", "/") + ".class");
        if (url == null) {
            throw new RuntimeException("Cannot locate " + Locator.class.getName());
        }

        try {
        return new File(url.toURI()).getParentFile();
        }
        catch (URISyntaxException e) {
            throw new RuntimeException(e);
        }
    }

    private static String[] listClassNames(File packageFile, final String ignore) {
        return packageFile.list(new FilenameFilter(){
            @Override
            public boolean accept(File dir, String name) {
                if (name.equals(ignore)) {
                    return false;
                }
                return name.endsWith(".class");
            }
        });
    }

    private static List<Class<?>> toClassList(String packageName, String[] classNames)
                                                             throws ClassNotFoundException {

        List<Class<?>> result = new ArrayList<Class<?>>(classNames.length);
        for (String className : classNames) {
            // Strip the .class
            String simpleName = className.substring(0, className.length() - 6);

            result.add(Class.forName(packageName + "." + simpleName));
        }
        return result;
    }
}

Solution 2

How about putting each major group of junit tests into their own root package. I use this package structure in my project:

test.
  quick.
    com.acme
  slow.
    com.acme

Without any coding, you can set up IntelliJ to run all tests, just the quick ones or just the slow ones.

Solution 3

What about using JUnit4 and the Suite-Runner?

Example:

@RunWith(Suite.class)
@Suite.SuiteClasses({
UserUnitTest.class,
AnotherUnitTest.class
})
public class UnitTestSuite {}

I made a small Shell-Script to find all Unit-Tests and another one to find my Integration-Tests. Have a look at my blog entry: http://blog.timomeinen.de/2010/02/find-all-junit-tests-in-a-project/

If you use Spring TestContext you can use the @IfProfile Annotation to declare different tests.

Kind regards, Timo Meinen

Share:
10,374
thvo
Author by

thvo

Updated on June 15, 2022

Comments

  • thvo
    thvo almost 2 years

    I basically want to run all JUnit unit tests in my IntelliJ IDEA project (excluding JUnit integration tests), using the static suite() method of JUnit. Why use the static suite() method? Because I can then use IntelliJ IDEA's JUnit test runner to run all unit tests in my application (and easily exclude all integration tests by naming convention). The code so far looks like this:

    package com.acme;
    
    import junit.framework.Test;
    import junit.framework.TestCase;
    import junit.framework.TestSuite;
    
    import java.util.ArrayList;
    import java.util.Iterator;
    import java.util.List;
    
    public class AllUnitTests extends TestCase {
    
        public static Test suite() {
            List classes = getUnitTestClasses();
            return createTestSuite(classes);
        }
    
        private static List getUnitTestClasses() {
            List classes = new ArrayList();
            classes.add(CalculatorTest.class);
            return classes;
        }
    
        private static TestSuite createTestSuite(List allClasses) {
            TestSuite suite = new TestSuite("All Unit Tests");
            for (Iterator i = allClasses.iterator(); i.hasNext();) {
                suite.addTestSuite((Class<? extends TestCase>) i.next());
            }
            return suite;
        }
    
    }
    

    The method getUnitTestClasses() should be rewritten to add all project classes extending TestCase, except if the class name ends in "IntegrationTest".

    I know I can do this easily in Maven for example, but I need to do it in IntelliJ IDEA so I can use the integrated test runner - I like the green bar :)

  • thvo
    thvo over 15 years
    hanks. Most of the tests need to be in the same package than the class they test since they access non-public members. Whether or not that's a good practice, it can't be changed right now. So I need to keep the test and the class in the same package.
  • thvo
    thvo over 15 years
    With some modifications, mainly to make it recursive, this has worked well for me. Thanks!
  • case nelson
    case nelson over 14 years
    This answer would still work. test/quick and test/slow are test source roots and tests within either would still be in the com.acme package.
  • Casey Watson
    Casey Watson over 13 years
    This is a great option. It's much easier than scraping the filesystem. It also seems to be much faster than running the tests through ant since IntelliJ only rebuilds the files that have changed.
  • Arne Evertsson
    Arne Evertsson over 13 years
    Reply to CNelson: I created more test source roots but the trouble then is that IntelliJ only let's you group and run tests specified by package or class.