DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

The software you build is only as secure as the code that powers it. Learn how malicious code creeps into your software supply chain.

Apache Cassandra combines the benefits of major NoSQL databases to support data management needs not covered by traditional RDBMS vendors.

Generative AI has transformed nearly every industry. How can you leverage GenAI to improve your productivity and efficiency?

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Related

  • Mastering Unit Testing and Test-Driven Development in Java
  • Hints for Unit Testing With AssertJ
  • How to Migrate From JUnit 4 to JUnit 5 Step by Step
  • Testing Asynchronous Operations in Spring With JUnit 5 and Byteman

Trending

  • How to Create a Successful API Ecosystem
  • A Complete Guide to Modern AI Developer Tools
  • Intro to RAG: Foundations of Retrieval Augmented Generation, Part 2
  • Integrating Security as Code: A Necessity for DevSecOps
  1. DZone
  2. Testing, Deployment, and Maintenance
  3. Testing, Tools, and Frameworks
  4. Introducing a Parameterized Test Suite for JUnit 4

Introducing a Parameterized Test Suite for JUnit 4

As JUnit 4 fans will know, you aren't able to parameterize test suites. But now there's an extension, via the Runner class, that will help you out.

By 
Peter Wippermann user avatar
Peter Wippermann
·
May. 29, 17 · Tutorial
Likes (2)
Comment
Save
Tweet
Share
19.8K Views

Join the DZone community and get the full member experience.

Join For Free

Parameterization of test cases is a well-known feature of JUnit 4. While some use cases also promote the parameterization of test suites, this feature is not available in the test framework by default. This article introduces an extension for JUnit 4 that provides a new Runner class for this purpose. Applicability in the context of automated GUI tests is shown.

Why Would You Parameterize a Test Suite?

Most of us Java developers are familiar with JUnit and its popular features. We typically define test classes and use test suites to aggregate these tests and give them a hierarchy. Test rules like @BeforeClass or @After are also widely used to set up a test's context. For my part, I knew that tests could also be parameterized and I had seen the Fibonacci example. But I think parameterization is a feature one doesn't have to use often.

Parameterized tests became valuable for me when I started automating GUI tests for web applications with Selenium and WebDriver: Although GUI tests are no unit tests for sure the JUnit framework is very handy for test definition, execution and result aggregation. Integration with CI is also available off the shelf. Once I had reached a good coverage of use cases in a single browser, I wanted to port these test cases to other browsers as well. Thus, I parameterized my tests with the browser being the parameter and ran these tests on Chrome, Firefox and Internet Explorer. Since all my tests should be executed on all browsers, the next logical step was to define these parameters at the test suite once instead of defining them repeatedly for every test class.

Then I stumbled upon a severe limitation of JUnit 4: You can parameterize your tests, but you can't parameterize the test suite that combines all your tests!?

Introducing ParameterizedSuite Runner

In JUnit 4 the execution of a test is done by the Runner and configured by the corresponding annotation  @RunWith. BlockJUnit4ClassRunner is the standard Runnerfor tests and chosen if you don't define any. For defining a test suite you configure @RunWith(Suite.class), similar to a parameterized test: @RunWith(Parameterized.class). So that's mutually exclusive.

But how can you parameterize a test suite if there's no @RunWith(ParameterizedSuite.class)? Well, now you can.

Wanting to define parameters on suite level and having the use case of test automation in mind, I implemented and published an open source library as an extension of JUnit 4. Integration is easy and uses the same patterns as in parameterization of single test classes.

Exemplary Integration

Start by defining another dependency in addition to JUnit 4.
For Maven:

<dependency>
    <!-- https://github.com/PeterWippermann/parameterized-suite -->
    <groupId>com.github.peterwippermann.junit4</groupId>
    <artifactId>parameterized-suite</artifactId>
    <version>1.1.0</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
    <scope>test</scope>
</dependency>


Make your test suite a ParameterizedSuite and declare a method to produce parameters - just as in a parameterized test class.

import com.github.peterwippermann.junit4.parameterizedsuite.ParameterizedSuite;

@RunWith(ParameterizedSuite.class)
@SuiteClasses({
    OneTest.class,
    TwoTest.class
})
public class MyParameterizedTestSuite {

    @Parameters(name = "Parameters are {0} and {1}")
    public static Object[] params() {
        return new Object[][] {
            {
                'A',
                1
            }, {
                'B',
                2
            }, {
                'C',
                3
            }
        };
    }

    /**
     * Always provide a target for the defined parameters - even if you only want to access them in the suite's child classes.
     * Instead of fields you can also use a constructor with parameters.
     */
    @Parameter(0)
    public char myCharParameter;

    @Parameter(1)
    public int myIntParameter;
}


In JUnit 4 a test suite has no access to the instances of the test classes. This is by the framework’s design. Thus, the parameters have to be transferred via a static context, which is the ParameterContext singleton. Instead of instantiating their own parameters, parameterized test classes refer to the ParameterContext.

@RunWith(Parameterized.class)
public class MyParameterizedTestCase {

    @Parameters(name = "Parameters are {0} and {1}")
    public static Iterable < Object[] > params() {
        if (ParameterContext.isParameterSet()) {
            return Collections.singletonList(ParameterContext.getParameter(Object[].class));
        } else {
            // if the test case is not executed as part of a ParameterizedSuite, you can define fallback parameters
        }
    }

    private char myCharParameter;

    private int myIntParameter;

    public MyParameterizedTestCase(char c, int i) {
        super();
        this.myCharParameter = c;
        this.myIntParameter = i;
    }

    @Test
    public void testWithParameters() {
        // ...
    }
}

Execution Order

The set of test classes in a test suite and the set of parameters the suite defines lead to two alternative execution strategies:

  1. Execute all tests per parameter

  2. Execute each test with all parameters in a row

The current implementation of ParameterizedSuite implements the first strategy, i.e. for each parameter all tests are run before going over to the next parameter. That way instances of parameter objects are handed over from test to test and allow to share state between tests.

Sharing state between tests became especially important in my case for GUI tests: A browser could be set up once and be reused in all tests. That way the session, cookies, current URL and history persisted.

Due to the execution order, only a single parameter instance may be accessed from the ParameterContext at once. During the test suite's execution the test classes will be instantiated multiple times and, consequently, the method annotated with @Parameters will also be called as often.

Enabling Test Rules for Test Suites

 TestRules  encapsulate the execution of a test case and may take additional actions before or after a test is run. Popular examples are @Before, @AfterClass, and @ExternalResource. These test rules are not applicable to default test suites since a test suite is never instantiated. It rather acts as a declaration of the grouped test classes. Opposed to this, a parameterized test suite will also be instantiated and respects any definitions of test rules.

Notes to State-Sharing and Thread-Safety

Parameters of a test suite are only instantiated once and reused in all test classes, which implies these objects are shared state between the test classes! This behavior is actually identical to the parameterisation of single classes using @Parameterized. Nevertheless, state sharing has to be used with care and considered for race conditions. In the context of GUI testing, the sharing of the browser instances is explicitly wanted to preserve the web application’s state.

Regarding race conditions: The current implementation of the ParameterContextis not thread-safe. A concurrent implementation could be based on ThreadLocal. Feature requests or even pull requests are welcome at the project’s site.

Conclusion

The parameterization of test suites is required by few JUnit 4 users, but many of those seem to come from a GUI test automation background. Supporting this feature in the JUnit 4 framework has been discussed but not been carried out. The library parameterized-suite that has been introduced in this article is intended to be an extension of the framework and fills the described feature gap.

unit test Test suite JUnit

Opinions expressed by DZone contributors are their own.

Related

  • Mastering Unit Testing and Test-Driven Development in Java
  • Hints for Unit Testing With AssertJ
  • How to Migrate From JUnit 4 to JUnit 5 Step by Step
  • Testing Asynchronous Operations in Spring With JUnit 5 and Byteman

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!