How to run JUnit 4 test cases in JUnit 5
By Pratik Barjatiya, Community Contributor - May 23, 2022
JUnit is an effective framework for Selenium that enables test automation of unit testing. This helps the smallest individual chunks of code as a part of Unit Testing using Selenium Framework.
This helps developers check their code and locate bugs or defective functionality right at the early stages if any occur. Although developers largely use the JUnit frameworks, it is also used by the QAs likewise due to its Assertions feature.
This article discusses what JUnit 5 is and how JUnit 4 test cases can be run in JUnit 5.
What Is JUnit 5? How does it work?
JUnit 5 is the latest version of JUnit that consists of 3 special sub-additives:
- JUnit Platform,
- JUnit Jupiter, and
- JUnit Vintage.
Before discussing the components, it is noteworthy that runtime JUnit 5 at least Java 8 as the runtime environment. However, the unit assessments can nonetheless be compiled with the preceding Java variations and examined with JUnit 5.
JUnit platform is the primary basis of the testing framework, which facilitates framework development. JUnit Jupiter is used for writing the assessments and the JUnit Vintage is used for strolling in advance variations of JUnit tests, so that the tests on older versions like JUnit 3 and JUnit 4 can be run along with the newer tests on JUnit 5.
JUnit 5 vs JUnit 4
Here are the core differences between JUnit 5 and JUnit 4:
- In JUnit 4, the whole lot consists and is wrapped collectively in contrast to JUnit 5. JUnit 5 consists of 3 additives specifically JUnit Platform, JUnit Jupiter, and JUnit Vintage.
- JUnit 4 calls for a Java 5 (or above), while JUnit 5 calls for Java 8 (or maybe better).
- JUnit 4 doesn`t aid any third-celebration integration plugins and IDEs. In JUnit 5, the JUnit Platform provides aid for build tools and integrated development platforms and famous IDEs like Eclipse, Visual Studio, and IntelliJ.
- JUnit Asserts are an essential part of the JUnit framework. In JUnit 4, assertions (or asserts) are grouped below org.junit.Assert package deal which includes all of the announcement techniques. In JUnit 5, assertions techniques are grouped and can be imported from org.junit.jupiter.Assertions
- Like assertions, Assumptions techniques also are imported from special programs in JUnit 4 and JUnit 5. In JUnit 4, they’re imported from org.junit.Assume and in JUnit 5 they’re imported from org.junit.jupiter.api.Assumptions
- When returning errors/blunders messages in assertions, the order of the parameters differs as seen below
In JUnit 4,
public static void assertEquals(String message, long expected, long actual)
In JUnit 5,
public static void assertEquals(long expected, long actual, String message)
Here are some of the annotations that vary in JUnit 4 and JUnit 5:
JUNIT 4 | JUNIT 5 |
@Before | @BeforeEach |
@After | @AfterEach |
@BeforeClass | @BeforeAll |
@AfterClass | @AfterAll |
@Ignore | @Disabled |
@Category | @Tag |
@Rule and @ClassRule annotations in JUnit 4 are removed. Instead, @ExtendWith and @RegisterExtension should be used.
JUnit 4 Operation of @Test annotation @Test(expected = Exception.class) public void testThrowsException() throws Exception { } JUnit 5 Operation of @Test annotation @Test void testThrowsException() throws Exception { Assertions.assertThrows(Exception.class, () -> { }); }
Annotations In JUnit 4 And JUnit 5
Annotations are vital in JUnit, which are listed as follows:
- @Test – Annotation used to define and declare a test.
- @RepeatedTest – Annotation used to specify that the function is a template for the tests that can be repeated a specific number of times.
- @TestFactory– Annotation used for defining a system which is a test plant for robust tests that are generated at runtime.
- @TestMethodOrder – Annotation used to define the order of carrying out the tests.
- @DisplayName – Annotation used to specify a custom display name for the function or class.
- @Tag – Annotation used to filter the tests at function or class position by defining the markers.
- @Disabled – Annotation used to disable a test system or class.
- @ParameterizedTest – Annotation used to indicate that the function is a parameterized test. These parameterized tests are analogous to normal test styles but you have to specify a source to give parameters for each incantation which in turn is used in the test.
- @BeforeEach – Annotation used to specify that the specific test system has to be executed before each@Test, @RepeatedTest, @ParameterizedTest, or @TestFactory system.
- @AfterEach – Annotation used to specify that the specific test system has to be executed after each@Test, @RepeatedTest, @ParameterizedTest, or @TestFactory system.
- @BeforeAll – Annotation used to specify that the specific test system has to be executed before all @Test, @RepeatedTest, @ParameterizedTest, or @TestFactory system.
- @AfterAll – Annotation used to specify that the specific test system has to be executed after all @Test, @RepeatedTest, @ParameterizedTest, or @TestFactory system.
PURPOSE | JUNIT 5 | JUNIT 4 |
To define and declare a test | @Test | @Test |
To execute before all test methods in the current class | @BeforeAll | @BeforeClass |
To execute before each test method | @BeforeEach | @Before |
To execute after all test methods in the current class | @AfterAll | @AfterClass |
To execute after each test method | @AfterEach | @After |
For tagging and filtering the tests | @Tag | @Category |
To disable a test method or class | @Disabled | @Ignore |
For nested tests | @Nested |
Step-by-Step Execution Flow Of Annotations In JUnit 5
Let us see the series wherein those annotations are carried out in JUnit 5.
- The technique applied below the @BeforeAll annotation is carried out.
- The technique applied below the @BeforeEach annotation executes earlier than the primary take a look at.
- The technique applied below the @Test annotation is carried out.
- The technique applied below the @AfterEach annotation runs after the test is carried out.
- The technique applied below the @AfterAll annotation might be carried out on the give up.
In Selenium Automation Testing with the JUnit, you would possibly need to put in force more than one take a look at scenarios (or techniques) below the equal class (or special classes).
As visible in the sooner part of the JUnit 5 tutorial, it’s vital to understand the execution order of the techniques applied below special annotations.
Here is the order of execution in case there are multiple tests to take a look at techniques withinside the class:
- The technique applied below the @BeforeAll annotation is carried out as soon as.
- The technique applied below the @BeforeEach annotation executes earlier than the primary take a look at.
- The technique applied below the @Test annotation is carried out.
- The technique applied below the @AfterEach annotation runs after the take a look at the case is carried out.
- The technique applied below the @BeforeEach annotation is carried out earlier than the second test case.
- The technique applied below the @Test annotation is carried out.
- The technique applied below the @AfterEach annotation is carried out after the execution of the second test case.
- The technique that has been annotated with @AfterAll is carried out as soon as on the give up.
Thus, for more than one take a look at instances, the techniques applied below @BeforeAll and @AfterAll annotations are carried out most effective as soon as, at the start and give up of the take a look at.
Alternatively, take a look at the techniques applied below the @BeforeEach and @AfterEach annotations are carried out before and after respectively, for every and each take a look at case.
Migrating Tests from JUnit 4 to JUnit 5
One of the not unusual place questions that involves JUnit customers is `Is it possible to run JUnit 4 assessments in JUnit 5`? By giving up on this phase of JUnit 5 tutorial, you’ll be in a role to run JUnit 4 assessments in JUnit 5. You can migrate assessments written in JUnit 4 to JUnit 5 with minimum effort.
Here are the steps to carry out the migration:
- The JUnit Vintage engine in JUnit 5 facilitates strolling, take a look at instances written in JUnit 4 (or JUnit 3) utilising the JUnit Platform.
- Annotations like @Before and @After in present JUnit 4 assessments should get replaced with equal annotations in JUnit 5 (i.e. @BeforeEach and @AfterEach respectively).
- Import accurate package deal for Assertions (i.e. org.junit.jupiter.Assertions) and Assumptions
- Make absolute the JUnit 4 annotations that are unsupported in JUnit 5 and replace those annotations with the ones supported in JUnit 5.
- In case your JUnit 4 design is defined with certain rules, the migration has to be done gradually. There are many rules of JUnit 4 that are compatible with JUnit Jupiter.
- The junit-jupiter-migration support module provides support for the @Ignore reflection in JUnit 4 which is original to Jupiter’s @Disabled reflection. Still, this is still in the experimental phase
For instance, let’s perceive how to run the test project that contains unit tests fabricated utilising JUnit 4 and JUnit 5.
Assuming having tests composed utilising JUnit 4. Adding the JUnit 5 tests to it. The pom.xml record would look like below:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>RunJUnitTest</groupId> <artifactId>RunJUnitTest</artifactId> <version>1.0-SNAPSHOT</version> <dependencies> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-nop</artifactId> <version>1.7.28</version> <scope>test</scope> </dependency> <dependency> <groupId>org.junit.platform</groupId> <artifactId>junit-platform-launcher</artifactId> <version>1.2.0</version> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-engine</artifactId> <version>5.2.0</version> </dependency> <dependency> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> <version>5.2.0</version> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-api</artifactId> <version>5.2.0</version> <scope>test</scope> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-nop</artifactId> <version>1.7.28</version> <scope>test</scope> </dependency> <dependency> <groupId>org.seleniumhq.selenium</groupId> <artifactId>selenium-java</artifactId> <version>4.0.0-alpha-7</version> </dependency> <dependency> <groupId>org.seleniumhq.selenium</groupId> <artifactId>selenium-remote-driver</artifactId> <version>4.0.0-alpha-7</version> </dependency> <dependency> <groupId>org.seleniumhq.selenium</groupId> <artifactId>selenium-chrome-driver</artifactId> <version>4.0.0-alpha-7</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> <version>4.5.13</version> </dependency> </dependencies> <properties> <maven.compiler.source>15</maven.compiler.source> <maven.compiler.target>15</maven.compiler.target> </properties> </project>
JUnit 4 Test
package com.browserstack; import com.browserstack.local.Local; import org.apache.http.NameValuePair; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.HttpPut; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.message.BasicNameValuePair; import org.json.simple.JSONArray; import org.json.simple.JSONObject; import org.json.simple.parser.JSONParser; import org.junit.After; import org.junit.Before; import org.junit.runner.RunWith; import org.junit.runners.Parameterized.Parameter; import org.junit.runners.Parameterized.Parameters; import org.openqa.selenium.WebDriver; import org.openqa.selenium.remote.DesiredCapabilities; import org.openqa.selenium.remote.RemoteWebDriver; import org.openqa.selenium.remote.SessionId; import java.io.FileReader; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.util.*; @RunWith(Parallelized.class) public class BrowserStackJUnitTest { public static String username, accessKey; private static JSONObject config; public WebDriver driver; @Parameter(value = 0) public int taskID; private Local l; @Parameters public static Iterable<? extends Object> data() throws Exception { List<Integer> taskIDs = new ArrayList<Integer>(); if (System.getProperty("config") != null) { JSONParser parser = new JSONParser(); config = (JSONObject) parser.parse(new FileReader("src/test/resources/conf/" + System.getProperty("config"))); int envs = ((JSONArray) config.get("environments")).size(); for (int i = 0; i < envs; i++) { taskIDs.add(i); } } return taskIDs; } public static void mark(SessionId sessionID, String status, String reason) throws URISyntaxException, IOException { URI uri = new URI("https://" + username + ":" + accessKey + "@api.browserstack.com/automate/sessions/" + sessionID + ".json"); HttpPut putRequest = new HttpPut(uri); ArrayList<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(); nameValuePairs.add((new BasicNameValuePair("status", status))); nameValuePairs.add((new BasicNameValuePair("reason", reason))); putRequest.setEntity(new UrlEncodedFormEntity(nameValuePairs)); HttpClientBuilder.create().build().execute(putRequest); } @Before public void setUp() throws Exception { JSONArray envs = (JSONArray) config.get("environments"); DesiredCapabilities capabilities = new DesiredCapabilities(); Map<String, String> envCapabilities = (Map<String, String>) envs.get(taskID); Iterator it = envCapabilities.entrySet().iterator(); while (it.hasNext()) { Map.Entry pair = (Map.Entry) it.next(); capabilities.setCapability(pair.getKey().toString(), pair.getValue().toString()); } Map<String, String> commonCapabilities = (Map<String, String>) config.get("capabilities"); it = commonCapabilities.entrySet().iterator(); while (it.hasNext()) { Map.Entry pair = (Map.Entry) it.next(); if (capabilities.getCapability(pair.getKey().toString()) == null) { capabilities.setCapability(pair.getKey().toString(), pair.getValue().toString()); } } username = System.getenv("BROWSERSTACK_USERNAME"); if (username == null) { username = (String) config.get("user"); } accessKey = System.getenv("BROWSERSTACK_ACCESS_KEY"); if (accessKey == null) { accessKey = (String) config.get("key"); } if (capabilities.getCapability("browserstack.local") != null && capabilities.getCapability("browserstack.local") == "true") { l = new Local(); Map<String, String> options = new HashMap<String, String>(); options.put("key", accessKey); l.start(options); } driver = new RemoteWebDriver(new URL("https://" + username + ":" + accessKey + "@" + config.get("server") + "/wd/hub"), capabilities); } @After public void tearDown() throws Exception { driver.quit(); if (l != null) l.stop(); } }
JUnit 5 Test
package tests; //* import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.remote.RemoteWebDriver; import org.openqa.selenium.remote.SessionId; import org.openqa.selenium.support.ui.ExpectedConditions; import org.openqa.selenium.support.ui.WebDriverWait; import runners.WebDriverTest; import utils.MarkSessionStatus; public class SingleTest { @WebDriverTest void singleTest(WebDriver driver) { SessionId sessionId = ((RemoteWebDriver)driver).getSessionId(); MarkSessionStatus sessionStatus = new MarkSessionStatus(sessionId); try { driver.get("https://bstackdemo.com/"); final WebDriverWait wait = new WebDriverWait(driver, 10); wait.until(ExpectedConditions.titleIs("StackDemo")); String product_name = wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath("//*[@id='1']/p"))).getText(); WebElement cart_btn = wait.until(ExpectedConditions.elementToBeClickable(By.xpath("//*[@id='1']/div[4]"))); cart_btn.click(); wait.until(ExpectedConditions.visibilityOfElementLocated(By.className("float-cart__content"))); final String product_in_cart = wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath("//*[@id='__next']/div/div/div[2]/div[2]/div[2]/div/div[3]/p[1]"))).getText(); if (product_name.equals(product_in_cart)) { sessionStatus.markTestStatus("passed", "Product has been successfully added to the cart!"); } else { sessionStatus.markTestStatus("failed", "There was some issue!"); } } catch (Exception e) { sessionStatus.markTestStatus("failed", "There was some issue!"); System.out.println("Exception: " + e.getMessage()); } driver.quit(); } //@WebDriverTest void bstackTest(WebDriver driver) { driver.get("https://bstackdemo.com/"); System.out.println("Test 1: " + Thread.currentThread().getName()); driver.quit(); } }
Parallel Tests in JUnit 5
package runners; //* import org.json.simple.JSONObject; import org.json.simple.parser.JSONParser; import org.junit.jupiter.api.extension.*; import org.openqa.selenium.WebDriver; import org.openqa.selenium.remote.DesiredCapabilities; import org.openqa.selenium.remote.RemoteWebDriver; import utils.SetupLocalTesting; import java.io.FileReader; import java.net.MalformedURLException; import java.net.URL; import java.util.*; import java.util.stream.Stream; public class BstackRunner implements TestTemplateInvocationContextProvider { public WebDriver driver; public DesiredCapabilities capabilities; public String username, accesskey, server; private JSONObject mainConfig; private JSONObject browserConfig; private JSONObject profileConfig; private JSONObject testConfig; private JSONObject platformConfig; private JSONObject commonCapsConfig; private HashMap<String, String> allCapsMap; private HashMap<String, String> commonCapsMap; public BstackRunner() { this.username = setupCredsAndServer().get("username"); this.accesskey = setupCredsAndServer().get("accesskey"); this.server = setupCredsAndServer().get("server"); } public HashMap<String, String> setupCredsAndServer() { try { JSONParser parse = new JSONParser(); mainConfig = (JSONObject) parse.parse(new FileReader("src/test/resources/caps.json")); server = (String) mainConfig.get("server"); username = System.getenv("BROWSERSTACK_USERNAME"); if (username == null) { username = (String) mainConfig.get("user"); } accesskey = System.getenv("BROWSERSTACK_ACCESS_KEY"); if (accesskey == null) { accesskey = (String) mainConfig.get("key"); } } catch (Exception e) { System.out.println(e.getMessage()); } HashMap<String, String> creds = new HashMap(); creds.put("username", username); creds.put("accesskey", accesskey); creds.put("server", server); return creds; } @Override public boolean supportsTestTemplate(ExtensionContext extensionContext) { return true; } @Override public Stream<TestTemplateInvocationContext> provideTestTemplateInvocationContexts(ExtensionContext extensionContext) { List<TestTemplateInvocationContext> desiredCapsInvocationContexts = new ArrayList<>(); //picks the test profile based on the maven command executed - single, local, parallel String profile = System.getProperty("config"); try { testConfig = (JSONObject) mainConfig.get("tests"); profileConfig = (JSONObject) testConfig.get(profile); platformConfig = (JSONObject) profileConfig.get("platform"); commonCapsConfig = (JSONObject) profileConfig.get("common_caps"); commonCapsMap = (HashMap<String, String>) commonCapsConfig; Iterator platformIterator = platformConfig.keySet().iterator(); while (platformIterator.hasNext()) { capabilities = new DesiredCapabilities(); Iterator commonCapsIterator = commonCapsMap.entrySet().iterator(); while (commonCapsIterator.hasNext()) { Map.Entry capsName = (Map.Entry) commonCapsIterator.next(); capabilities.setCapability((String) capsName.getKey(), capsName.getValue()); } final String platformName = (String) platformIterator.next(); browserConfig = (JSONObject) platformConfig.get(platformName); allCapsMap = (HashMap<String, String>) browserConfig; Iterator finalCapsIterator = allCapsMap.entrySet().iterator(); while (finalCapsIterator.hasNext()) { Map.Entry pair = (Map.Entry) finalCapsIterator.next(); capabilities.setCapability((String) pair.getKey(), pair.getValue()); } //Initialising local testing connection if (capabilities.getCapability("browserstack.local") != null && capabilities.getCapability("browserstack.local").toString().equals("true")) { HashMap<String, String> localOptions = new HashMap<>(); localOptions.put("key", accesskey); //Add more local options here, e.g. forceLocal, localIdentifier, etc. SetupLocalTesting.createInstance(localOptions); } desiredCapsInvocationContexts.add(invocationContext(capabilities)); } } catch (Exception e) { System.out.println(e); } return desiredCapsInvocationContexts.stream(); } private TestTemplateInvocationContext invocationContext(DesiredCapabilities caps) { return new TestTemplateInvocationContext() { @Override public List<Extension> getAdditionalExtensions() { return Collections.singletonList(new ParameterResolver() { @Override public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) { return parameterContext.getParameter().getType().equals(WebDriver.class); } @Override public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) { try { driver = new RemoteWebDriver(new URL("https://" + username + ":" + accesskey + "@" + server + "/wd/hub"), caps); } catch (MalformedURLException e) { e.printStackTrace(); } return driver; } }); } }; } }
Refer to the BrowserStack’s JUnit Repository on GitHub to learn more about executing JUnit 5 Tests on real devices
Advantages Of Using JUnit 5 For Unit Testing
Some of the core benefits of JUnit 5 are:
- Offers specific functions to outline the assessments in evaluation to the preceding versions.
- Allows using lambda features withinside the assessments for assertions in addition to assumptions.
- Offers distinguished annotations with well-described functions that may be used to beautify the unit assessments.
- Allows use of more than one runner.
- It has a specific extensible architecture.
- Migrates current tests written with preceding JUnit versions.
JUnit testing is the most preferred testing method if the project has been developed in Java. It is powerful and continually evolving for better test case execution. It has become a preferred choice for Test-driven development cycle.
Selenium is a convenient tool when it comes to automated web testing and using it along with JUnit is even more beneficial. JUnit supports multiple assertions and annotations. Using BrowserStack Automate, developers can conveniently run unit testing using Java with Selenium.
However, tests, no matter how well crafted, cannot be regarded as conclusive if they are not run on real devices. Emulators and simulators cannot replicate real user conditions properly, especially when it comes to aspects like weak networks and screen orientation. To compensate for these inadequacies, run tests directly on a real device cloud.
Use parallel testing. Instead of running tests sequentially, parallel testing allows for simultaneous test execution. By running JUnit Tests on cloud Selenium Grid you can run your Unit tests on multiple real browser-device combinations simultaneously, significantly reducing time and effort.