Create JUnit Test Suite with Example: @RunWith @SuiteClasses

⚡ Smart Summary

JUnit test suites aggregate test cases from several classes so they run together in a single pass, driven by the @RunWith(Suite.class) and @SuiteClasses annotations and launched through an ordinary test runner class.

  • 🔘 Purpose: Group tests from many classes into one runnable unit instead of launching every test class separately.
  • ☑️ Annotations: @RunWith(Suite.class) delegates execution, while @SuiteClasses lists every class the suite must run.
  • Steps: Write the test classes, add a holder class carrying both annotations, then run it from a JUnitCore runner.
  • 🧪 Example: JunitTest.java groups SuiteTest1 and SuiteTest2, and the console reports three passing tests.
  • 🛠️ JUnit 5: Jupiter replaces the runner with @Suite plus @SelectClasses or @SelectPackages on the platform suite engine.
  • 📌 Pitfalls: Empty suites, missing imports and a suite class holding its own @Test methods cause most failures.

Creating a JUnit test suite with @RunWith and @SuiteClasses annotations

In JUnit, a test suite allows us to aggregate all test cases from multiple classes in one place and run them together.

To run the suite test, you need to annotate a class using the below-mentioned annotations:

  1. @RunWith(Suite.class)
  2. @SuiteClasses(test1.class, test2.class…) or @Suite.SuiteClasses({test1.class, test2.class…})

With the above annotations, all the test classes in the suite will start executing one by one. The suite class itself stays empty: it is only a holder for the annotations, and the runner reads those annotations to decide what to execute.

Steps to Create Test Suite and Test Runner

The four steps below build the smallest suite that actually runs: two ordinary test classes, a suite class that names them, and a runner that starts the suite from a main method.

Step 1) Create a simple test class (e.g. MyFirstClassTest) and add a method annotated with @Test.

The first class holds a single empty test method, which is enough to prove that the suite picks it up:

MyFirstClassTest class in Eclipse declaring a @Test annotated myFirstMethod

Step 2) Create another test class to add (e.g. MySecondClassTest) and create a method annotated with @Test.

The second class follows exactly the same shape, so the suite has two separate classes to aggregate:

MySecondClassTest class in Eclipse declaring a @Test annotated mySecondMethod

Step 3) To create a testSuite you need to first annotate the class with @RunWith(Suite.class) and @SuiteClasses(class1.class, class2.class…).

Notice that the suite class TestSuiteExample declares no test method of its own — the two annotations carry all of the information:

TestSuiteExample annotated with @RunWith(Suite.class) and @SuiteClasses naming MyFirstClassTest and MySecondClassTest

Step 4) Create a Test Runner class to run our test suite as given below:

The runner is a plain Java class with a main method that hands the suite class to JUnitCore:

Test runner class calling JUnitCore.runClasses on TestSuiteExample and printing the failure list

Code Explanation:

  • Code Line 8: Declaring the main method of the class Test which will run our JUnit test.
  • Code Line 9: Executing test cases using JUnitCore.runClasses which takes the test class name as a parameter (In the example above, you are using TestSuiteExample.class shown in step 3).
  • Code Line 11: Processing the result using a for loop and printing out the failed result.
  • Code Line 13: Printing out the successful result.

Output: Here is the output which shows a successful test with no failure trace as given below:

Eclipse JUnit view reporting Runs 2/2 with zero errors and zero failures for TestSuiteExample

The green bar confirms what a suite is for: one launch, one result view, and both classes reported underneath the suite node rather than in two separate runs.

JUnit Test Suite Example

Consider a more complex example, in which the classes inside the suite actually assert something instead of holding an empty method.

JunitTest.java

JunitTest.java is a simple class annotated with the @RunWith and @Suite annotations. You can list any number of classes in the suite as parameters, as given below:

package guru99.junit;		
import org.junit.runner.RunWith;		
import org.junit.runners.Suite;		

@RunWith(Suite.class)				
@Suite.SuiteClasses({				
  SuiteTest1.class,
  SuiteTest2.class,  			
})		

public class JunitTest {				
			// This class remains empty, it is used only as a holder for the above annotations		
}

SuiteTest1.java

SuiteTest1.java is a test class having test methods that print out a message, as given below. You will use this class as a suite member in the class mentioned above. It relies on a helper class named JUnitMessage, which is defined elsewhere in the same package and is not reproduced here.

package guru99.junit;		

import static org.junit.Assert.assertEquals;				

import org.junit.Test;		

public class SuiteTest1 {				

    public String message = "Saurabh";							

    JUnitMessage junitMessage = new JUnitMessage(message);							

    @Test(expected = ArithmeticException.class)					
    public void testJUnitMessage() {					

        System.out.println("Junit Message is printing ");					
        junitMessage.printMessage();			

    }		

    @Test		
    public void testJUnitHiMessage() {					
        message = "Hi!" + message;							
        System.out.println("Junit Hi Message is printing ");					
        assertEquals(message, junitMessage.printHiMessage());					
        System.out.println("Suite Test 2 is successful " + message);							
    }		
}		

SuiteTest2.java

SuiteTest2.java is another test class, similar to SuiteTest1.java, having a test method to print out a message as given below. You will use this class as a suite member in JunitTest.java.

package guru99.junit;		

import org.junit.Assert;		
import org.junit.Test;		

public class SuiteTest2 {				
   	

    @Test		
    public void createAndSetName() {					
        		

        String expected = "Y";					
        String actual = "Y";					

        Assert.assertEquals(expected, actual);					
        System.out.println("Suite Test 1 is successful " + actual);							
    }		

}		

Output

After executing JunitTest.java, which contains a suite holding SuiteTest1.java and SuiteTest2.java, you will get the output below. The console prints the messages produced by both classes:

Eclipse Console showing the printed messages from both suite classes after JunitTest finishes

The JUnit view records the same run as three passing test methods grouped under the suite class:

Eclipse JUnit view reporting Runs 3/3 green for JunitTest with SuiteTest1 and SuiteTest2 expanded

Note: the console labels in this example are crossed over — the string printed by SuiteTest1 reads “Suite Test 2” and the one printed by SuiteTest2 reads “Suite Test 1”. The original listings are reproduced unchanged, so the labels stay as the author wrote them; read the class name in the JUnit view rather than the printed label when you match output to source.

How to Create a Test Suite in JUnit 5

JUnit 5 removes runners altogether, so @RunWith(Suite.class) does not exist in the org.junit.jupiter packages. Suites moved to the JUnit Platform, which ships them in a separate artifact called junit-platform-suite-engine. Add that dependency, then annotate a class with @Suite and one of the selector annotations.

import org.junit.platform.suite.api.SelectClasses;
import org.junit.platform.suite.api.Suite;
import org.junit.platform.suite.api.SuiteDisplayName;

@Suite
@SuiteDisplayName("Guru99 suite")
@SelectClasses({ SuiteTest1.class, SuiteTest2.class })
public class JunitTest {
    // Empty holder, exactly as in JUnit 4
}

The selector annotations replace the single @SuiteClasses list with a small family of options:

Annotation What it selects
@SelectClasses Individual test classes named one by one, the direct replacement for @SuiteClasses.
@SelectPackages Every test class in the named package and in all of its sub-packages.
@IncludeClassNamePatterns A regular expression filter applied on top of the selection.
@SuiteDisplayName A readable name shown in the report instead of the class name.

Two further differences matter in practice. A JUnit 5 suite class may declare @BeforeSuite and @AfterSuite methods, which run once around the whole suite, and the JUnit 4 example above still executes on the JUnit Platform through the vintage engine, so the original code on this page does not have to be rewritten to keep working.

Advantages and Limitations of JUnit Test Suites

A suite is a grouping mechanism, not a test framework of its own, and that shapes both what it does well and where it stops being useful.

Advantages

  • One launch runs related classes together, so a smoke set or a regression set is started with a single command.
  • The result view groups every class under one suite node, which makes a shared failure easier to spot.
  • The membership list lives in code, so it is reviewed and versioned like any other source file.
  • The same suite class can be launched from an IDE, from a runner such as JUnitCore, or from a build tool.

Limitations

  • @SuiteClasses is a hard-coded list, so a new test class is silently left out until somebody edits the suite.
  • The suite gives no ordering guarantee beyond the order in which the classes are listed.
  • Nothing is shared between the member classes, so a suite is not a substitute for a fixture or a base class.
  • Build tools already discover test classes by naming convention, which makes a suite redundant on many projects.

Common Errors When Creating a JUnit Test Suite

Most suite problems produce a short, unhelpful message. The table maps the messages you are likely to meet to their cause and fix.

Message or symptom Cause Fix
initializationError: No runnable methods The class is run as an ordinary test class, but it holds only annotations. Add @RunWith(Suite.class) so the suite runner takes over instead of the default runner.
cannot find symbol: class Suite The Suite import is missing. Import org.junit.runners.Suite alongside org.junit.runner.RunWith.
Suite runs, but a class is skipped The class was never added to the @SuiteClasses list. Add the class explicitly, or move to @SelectPackages in JUnit 5.
Class name is not accepted by the compiler @Runwith or @suiteClasses was typed with the wrong casing. Java annotations are case-sensitive: write @RunWith and @SuiteClasses exactly.
Tests in the suite class itself never run A @Test method was added to the suite holder. Keep the holder empty and move the test method into a member class.

Writing the suite once and keeping it accurate is the real work. If the list drifts away from the classes on disk, the suite reports green while part of the unit test set never executes at all, one of the quietest failure modes in software testing.

FAQs

The suite executes the classes in the order they are listed, but JUnit gives no guarantee about the order of methods inside each class. Tests that depend on a previous test having run are fragile and should be rewritten to stand alone.

Yes. A suite class is an ordinary class as far as the runner is concerned, so naming it inside another @SuiteClasses list nests the suites. Nesting is useful for a top-level regression suite built from smaller module suites.

AI assistants read the test classes on disk, compare them with the @SuiteClasses list and flag classes that were never added. They also cluster repeated failure traces by root cause, which shortens triage after a suite reports many reds at once.

Copilot writes the annotation pair quickly, but it frequently mixes JUnit 4 and JUnit 5 imports in the same file. Check that org.junit.runners.Suite and org.junit.platform.suite.api.Suite never appear together, because only one of them matches your runner.

Add junit-platform-suite-engine to the test scope. The @Suite and @SelectClasses annotations live in junit-platform-suite-api, which that engine artifact pulls in transitively, so a single dependency is normally enough.

Usually not. Surefire and the Gradle test task already discover test classes by naming pattern and run them all. A suite is worth writing when you want a named subset, such as a smoke set, that is smaller than the full run.

The runner reports an initialization error for that member, stating that no runnable methods were found, and the whole suite is marked failed. Remove the entry or add a @Test method to the class.

Not inside one selector list. Keep the JUnit 4 classes on the vintage engine and select them from a JUnit 5 suite by package, so both engines run under the same JUnit Platform launch without mixing annotation families.

Summarize this post with: