Introduction to Apache Maven

  

What is Maven?

Maven is a build automation and project management tool primarily used for Java projects, although it can be used for other programming languages as well. It was developed by the Apache Software Foundation.


Why Maven?

Before Maven, Java developers used tools like Ant or wrote custom scripts to manage builds. However, these methods had drawbacks:

  • Complex and inconsistent project structures

  • Manual handling of dependencies

  • Difficulties in project configuration sharing


Maven solves these issues by offering a standardized and automated way to manage:

  • Project builds

  • Dependencies

  • Documentation

  • Testing

  • Deployment


Core Concepts of Maven

1. Project Object Model (POM)

  • The heart of a Maven project.

  • A file named pom.xml that contains:

    • Project details (name, version, etc.)

    • Dependencies

    • Plugins

    • Build configuration

    • Repository information

  • It acts as a blueprint of the project.


2. Convention over Configuration

  • Maven uses standard conventions to reduce the need for configuration.

  • Default folder structure:

src/
  main/java      source code
  main/resources  configuration files
  test/java       unit test code
  • You can override defaults, but following the convention simplifies project setup.


3. Dependency Management

  • Maven can automatically download and manage external libraries (dependencies).

  • It uses Maven Central Repository by default, but other repositories can be added.

  • Transitive dependency resolution: if library A depends on B, and B depends on C, Maven includes all of them automatically.



4. Build Lifecycle

  • Maven defines a set of phases for building a project:


PhaseDescription
validateChecks project structure
compileCompiles source code
testRuns unit tests
packagePackages compiled code (example: JAR or WAR)
verifyRuns checks (like integration tests)
installInstalls the package to local repository
deployDeploys the package to remote repository


5. Repositories

  • Local Repository: Stored on your system. Maven downloads dependencies here.

  • Central Repository: Official online repository (https://search.maven.org).

  • Remote Repository: Custom or enterprise-level repositories like Nexus or Artifactory.


6. Plugins

  • Provide additional functionality like:

    • Compiling code

    • Running tests

    • Creating documentation

    • Deploying applications



Advantages of Using Maven

  • Standardization: Enforces consistent project structures and builds.

  • Automation: Handles the full build lifecycle from compilation to deployment.

  • Dependency Management: Automatically downloads and updates libraries.

  • Portability: Same build works across machines/environments.

  • Integration: Works with IDEs like IntelliJ, Eclipse, and build servers like Jenkins.




Maven can be used for:

  • Building Java applications (JAR, WAR, EAR)
  • Managing complex dependency trees
  • Automating tests and deployment pipelines
  • Sharing libraries across teams or organizations

Integration of Karate API with JUnit 4

  

Integrating JUnit 4 with Karate API

Karate is a powerful testing framework based on BDD (Behavior Driven Development), especially useful for testing web services and APIs. It uses Gherkin syntax (like Cucumber) and can be integrated with JUnit 4 for test execution.


Step-by-Step Integration of Karate with JUnit 4


Step 1: Add Dependencies


<dependencies>
    <!-- Karate Core -->
    <dependency>
        <groupId>com.intuit.karate</groupId>
        <artifactId>karate-junit4</artifactId>
        <version>1.4.1</version> <!-- or latest stable version -->
        <scope>test</scope>
    </dependency>

    <!-- For Assertions and Test Reporting -->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.13.2</version>
        <scope>test</scope>
    </dependency>
</dependencies>




Step 2: Create Feature File

Create a file named sample.feature under:


src/test/java/examples




Feature file:

Feature: Sample API Test

  Scenario: Verify GET request
    Given url 'https://jsonplaceholder.typicode.com/posts/1'
    When method get
    Then status 200
    And match response.userId == 1


Step 3: Create JUnit 4 Test Runner

Create a Java class to run the above feature with JUnit 4.

src/test/java/examples/SampleRunnerTest.java:


package examples;

import com.intuit.karate.junit4.Karate;
import org.junit.runner.RunWith;

@RunWith(Karate.class)
public class SampleRunnerTest {
    // No need to write any code inside
}

This class tells JUnit 4 to use Karate as the test runner and will automatically find the .feature file in the same package.


Step 4: Run the Test

  • Right-click on SampleRunnerTest.java and choose Run As → JUnit Test

  • Or run via Maven:


mvn test



Step 5: View Reports

Karate automatically generates reports in target/karate-reports:

  • karate-summary.html

  • karate.log

  • karate-report.html




Optional: Run All Feature Files in a Folder

package examples;

import com.intuit.karate.junit4.Karate;
import org.junit.runner.RunWith;

@RunWith(Karate.class)
public class AllTests {
    // This will run all *.feature files in this package
}




Benefits of Karate + JUnit 4 Integration


FeatureDescription
BDD SyntaxSimple, readable Gherkin language
JUnit CompatibilityEasily integrates into CI/CD
Rich ReportingAuto HTML & JSON reports
No Java Code NeededScenarios are defined in .feature files

Test Runner in Cucumber using JUnit

  

To implement a Test Runner class with JUnit in Cucumber, you need to create a class annotated with @RunWith and @CucumberOptions. This class serves as the entry point for running your Cucumber test scenarios written in .feature files using JUnit.


Below are the steps to implement Test runner in Cucumber with Testng


1. Add Maven Dependencies

Make sure your pom.xml contains these dependencies:

<dependencies>
    <!-- Cucumber JUnit -->
    <dependency>
        <groupId>io.cucumber</groupId>
        <artifactId>cucumber-junit</artifactId>
        <version>7.11.1</version>
        <scope>test</scope>
    </dependency>

    <!-- JUnit -->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.13.2</version>
        <scope>test</scope>
    </dependency>

    <!-- Cucumber Java -->
    <dependency>
        <groupId>io.cucumber</groupId>
        <artifactId>cucumber-java</artifactId>
        <version>7.11.1</version>
    </dependency>
</dependencies>


 

2. Create Feature File

Example: src/test/resources/features/login.feature

Feature: Login functionality

  Scenario: Valid login
    Given user is on login page
    When user enters valid username and password
    Then user is navigated to the homepage



3. Create Step Definition File

Example: src/test/java/stepdefinitions/LoginSteps.java

package stepdefinitions;

import io.cucumber.java.en.*;

public class LoginSteps {

    @Given("user is on login page")
    public void user_is_on_login_page() {
        System.out.println("User is on login page");
    }

    @When("user enters valid username and password")
    public void user_enters_valid_username_and_password() {
        System.out.println("User enters credentials");
    }

    @Then("user is navigated to the homepage")
    public void user_is_navigated_to_the_homepage() {
        System.out.println("User navigated to homepage");
    }
}



4. Create Test Runner Class

Example: src/test/java/testrunner/TestRunner.java

package testrunner;

import org.junit.runner.RunWith;
import io.cucumber.junit.Cucumber;
import io.cucumber.junit.CucumberOptions;

@RunWith(Cucumber.class)  // Tells JUnit to run Cucumber tests
@CucumberOptions(
    features = "src/test/resources/features", // path to feature files
    glue = "stepdefinitions",                // package of step definitions
    plugin = {"pretty", "html:target/cucumber-reports.html"},
    monochrome = true
)
public class TestRunner {
    // No code needed here
}



5. Run the Test

You can run the TestRunner.java file like a normal JUnit test class. Cucumber will pick the .feature files and corresponding step definitions.



Important Points:

ComponentDescription
@RunWith(Cucumber.class)Instructs JUnit to use Cucumber's test runner
@CucumberOptionsProvides feature path, glue (steps), plugin, formatting
featuresPath to .feature files
gluePackage containing step definition files
pluginReporting output (HTML, JSON, etc.)
monochromeMakes console output more readable

How to execute Parallel Test Cases in Appium with JUnit for Android Apps

  


To execute parallel test cases in Appium with JUnit for Android apps, you need to:

  • Set up multiple Android devices/emulators.
  • Assign unique ports and capabilities to each device.
  • Use JUnit's @Parameterized or a custom parallel runner.
  • Launch tests in parallel threads.



Prerequisites

  • Java (JDK 11+)

  • Appium server installed

  • Android SDK with AVDs or real devices

  • Eclipse or IntelliJ IDE

  • Appium Java client dependency in your Maven pom.xml:





Maven Dependency:

<dependency>
  <groupId>io.appium</groupId>
  <artifactId>java-client</artifactId>
  <version>9.0.0</version>
</dependency>






Step-by-step Implementation

1. Create a Device Configuration Class

public class DeviceConfig {
    public String deviceName;
    public String udid;
    public String platformVersion;
    public int systemPort;
    public int appiumPort;

    public DeviceConfig(String deviceName, String udid, String platformVersion, int systemPort, int appiumPort) {
        this.deviceName = deviceName;
        this.udid = udid;
        this.platformVersion = platformVersion;
        this.systemPort = systemPort;
        this.appiumPort = appiumPort;
    }
}






2. Create a Base Test Class to Setup Driver


public class BaseTest {
    protected AndroidDriver<MobileElement> driver;
    protected DeviceConfig device;

    public void setUp(DeviceConfig device) throws MalformedURLException {
        this.device = device;
        DesiredCapabilities caps = new DesiredCapabilities();
        caps.setCapability("platformName", "Android");
        caps.setCapability("deviceName", device.deviceName);
        caps.setCapability("udid", device.udid);
        caps.setCapability("platformVersion", device.platformVersion);
        caps.setCapability("automationName", "UiAutomator2");
        caps.setCapability("systemPort", device.systemPort);
        caps.setCapability("app", System.getProperty("user.dir") + "/apps/yourapp.apk");

        driver = new AndroidDriver<>(new URL("http://127.0.0.1:" + device.appiumPort + "/wd/hub"), caps);
    }

    public void tearDown() {
        if (driver != null) {
            driver.quit();
        }
    }
}






3. Define Your Test Class With @RunWith(Parameterized.class)


@RunWith(Parameterized.class)
public class ParallelAppiumTest extends BaseTest {

    @Parameterized.Parameters
    public static Collection<DeviceConfig> devices() {
        return Arrays.asList(
            new DeviceConfig("emulator-5554", "emulator-5554", "11", 8200, 4723),
            new DeviceConfig("emulator-5556", "emulator-5556", "11", 8201, 4725)
        );
    }

    private DeviceConfig config;

    public ParallelAppiumTest(DeviceConfig config) {
        this.config = config;
    }

    @Before
    public void init() throws Exception {
        setUp(config);
    }

    @Test
    public void sampleTest() {
        System.out.println("Running test on: " + config.deviceName);
        // Your test code here
    }

    @After
    public void cleanup() {
        tearDown();
    }
}






Step 4: Start Multiple Appium Servers

Start different Appium servers on different ports manually or via command line:


appium -p 4723 --default-capabilities '{"systemPort": 8200}'
appium -p 4725 --default-capabilities '{"systemPort": 8201}'

Or use AppiumServiceBuilder from Java code to start servers dynamically (optional).

JUnit by default runs parameterized tests sequentially. To achieve true parallel execution, use JUnit Parallel Computer or a custom test runner like JUnitParams + multithreading.




Things to Remember:

  • Always assign unique systemPort and Appium server port for each device.
  • Don’t reuse driver objects across threads.
  • Avoid shared static variables unless they are synchronized.
  • You can scale it up using a Grid or Appium with Docker.

How to execute Parallel Test Cases in Appium with JUnit for IOS Apps

 


1. Prerequisites

  • Xcode installed (with WebDriverAgent setup)

  • Appium installed and running

  • iOS devices or simulators connected and available

  • Maven project with dependencies set up

  • iOS app (.ipa or .app)

  • Java 11+ and JUnit 5 recommended

  • Unique udid and wdaLocalPort for each device






Maven Dependencies


<dependencies>
    <dependency>
        <groupId>io.appium</groupId>
        <artifactId>java-client</artifactId>
        <version>8.3.0</version>
    </dependency>
    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter</artifactId>
        <version>5.9.3</version>
    </dependency>
</dependencies>





3. Create a Base Test Class

This handles session setup for each iOS device.



public class BaseIOSDriver {

    protected IOSDriver<MobileElement> driver;

    public IOSDriver<MobileElement> createDriver(String udid, int port, int wdaPort) {
        try {
            DesiredCapabilities caps = new DesiredCapabilities();
            caps.setCapability("platformName", "iOS");
            caps.setCapability("automationName", "XCUITest");
            caps.setCapability("udid", udid);
            caps.setCapability("deviceName", "iPhone");
            caps.setCapability("app", "/path/to/your.app");
            caps.setCapability("wdaLocalPort", wdaPort); // Must be unique
            caps.setCapability("noReset", true);

            URL appiumServer = new URL("http://localhost:" + port + "/wd/hub");
            driver = new IOSDriver<>(appiumServer, caps);
            return driver;
        } catch (Exception e) {
            throw new RuntimeException("Driver creation failed: " + e.getMessage());
        }
    }
}





4. Write Test Class for Each Device

You can use threads or @ParameterizedTest with parallel configuration.


public class ParallelIOSTest1 extends BaseIOSDriver {

    @Test
    public void testDevice1() {
        IOSDriver<MobileElement> driver = createDriver("DEVICE_UDID_1", 4723, 8100);
        // Your test steps
        System.out.println("Running on Device 1");
        driver.quit();
    }
}



public class ParallelIOSTest2 extends BaseIOSDriver {

    @Test
    public void testDevice2() {
        IOSDriver<MobileElement> driver = createDriver("DEVICE_UDID_2", 4725, 8200);
        // Your test steps
        System.out.println("Running on Device 2");
        driver.quit();
    }
}





5. Enable Parallel Execution in Maven

Create a junit-platform.properties file inside src/test/resources/:



junit.jupiter.execution.parallel.enabled=true
junit.jupiter.execution.parallel.mode.default=concurrent





6. Run Tests with Maven

mvn test



Or, run specific classes in parallel:


mvn -Dtest=ParallelIOSTest1,ParallelIOSTest2 test





Things to Remember:
  • Each device needs its own wdaLocalPort

  • Use Appium servers on different ports (47234725, etc.)

  • Consider running separate Appium instances for each device:

appium -p 4723 --webdriveragent-port 8100
appium -p 4725 --webdriveragent-port 8200