Search code examples
javagradlejarappiumintellij-14

Create stand-alone jar for appium test scripts


I would like to create a stand-alone (thin jar) jar without dependencies for Appium test scripts.

I have a Runner class

import org.junit.runner.JUnitCore;
import java.net.MalformedURLException;
public class Runner {
    public static void main(String[] args) throws MalformedURLException {
        try{
            JUnitCore.runClasses(Calculator.class);
        }finally {
        }
    }
}

and I have a Calculator test class


import java.net.MalformedURLException;
import java.net.URL;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
//import org.openqa.selenium.remote.CapabilityType;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.openqa.selenium.remote.RemoteWebDriver;
import io.appium.java_client.MobileElement;
import io.appium.java_client.android.AndroidDriver;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;


public class Calculator {
//    WebDriver driver;
    public AndroidDriver<MobileElement> driver;

    @Before
    public void setUp() throws MalformedURLException{
        DesiredCapabilities caps = new DesiredCapabilities();
        caps.setCapability("udid", "ZH33L2Z6KL"); //Give Device ID of your mobile phone
        caps.setCapability("platformName", "Android");
        caps.setCapability("platformVersion", "6.0.1");
        caps.setCapability("automationName", "uiautomator2");
        caps.setCapability("skipUnlock","true");
        caps.setCapability("appPackage", "com.google.android.calculator");
        caps.setCapability("appActivity", "com.android.calculator2.Calculator");
        caps.setCapability("noReset","true");
        driver = new AndroidDriver<MobileElement>(new URL("http://127.0.0.1:4723/wd/hub"), caps);
    }

    @Test
    public void testCal() throws Exception {
        //locate the Text on the calculator by using By.name()
        WebElement two=driver.findElement(By.id("digit_2"));
        two.click();
        WebElement plus=driver.findElement(By.id("op_add"));
        plus.click();
        WebElement four=driver.findElement(By.id("digit_4"));
        four.click();
        WebElement equalTo=driver.findElement(By.id("eq"));
        equalTo.click();
        //locate the edit box of the calculator by using By.tagName()
        WebElement results=driver.findElement(By.id("result_final"));
        //Check the calculated value on the edit box
        assert results.getText().equals("6"):"Actual value is : "+results.getText()+" did not match with expected value: 6";

    }

    @After
    public void teardown(){
        //close the app
        driver.quit();
    }
}

I have gone through one article about ThinJar and hollowJar.

https://dzone.com/articles/the-skinny-on-fat-thin-hollow-and-uber

Questions

  1. How to add Gradle task (in intellij)to build thin jar as per the article?
  2. How to add Gradle task to build 'Hollow' jar as per the article?
  3. If I build a 'fat' jar my jar size is 18mb. How to build skinny or thin jar with less size, and keep dependencies separately?
  4. How to run the created 'skinny' or 'thin' jar in different PC?

Solution

  • The terminology used in your link is a bit strange. With gradle, the "skinny" jar is always built. It is the default artifact, check the build/libs folder. If you apply the application plugin, there is a distribution zip built as well under build/distribution which is pretty much the fat jar (it is a zip of all relevant jars). But by definition you cannot build a fat jar into a smaller size, and you cannot simply run the "skinny" or "thin" jar on the target host.

    Running your application always requires just three things:

    1. The compiled artifact of your code - a bunch of .class files corresponding to, and only to, the code you write, usually packaged in a jar format. This is the skinny jar in that terminology. This is also the commonest artifact produced out of a build (any build system, maven, gradle etc) if you don't do anything special.

    2. The library dependencies - all 3rd party jars

    3. The runtime - this usually refers to Java itself.

    All of the above need to present on the host where you are about to run your application. Now, what gets a bit complicated is the stuff you actually need to ship to that host (this is called deployment):

    • Obviously you will need No. 1 shipped to the host
    • Usually you would expect/assume No. 3 is pre-installed on the host

    What about No.2 the 3rd party dependencies? The answer is it depends.

    • If (some of) these dependencies can be pre-installed on the target host, you don't need to ship them. In this case usually people would just call these dependencies as also the "runtime". For example, Maven is a runtime, so is Gradle. These are, in themselves, Java libraries to you when you are writing a Maven/Gradle plugin. You would normally expect people using your code to have maven/gradle installed already. They run your code through maven/gradle, and maven/gradle will provide the dependencies your code requires when running it. This is why in maven this kind of dependencies is called "Provided". Maven has a dedicated dependency scope for it.

    • If any of your dependencies is not provided on the target host, you need to ship it, period.

    In Gradle, if you apply the application plugin (which will automatically apply the distribution plugin), you can have both your artifact and your dependencies (exclude java runtime) in a single zip - this is called a distribution.

    plugins {
      id 'java'
      id 'application'
    }
    

    Once you build, you will find a zip file under build/distributions. There are two folders if you unzip the file: bin and lib. Within the lib folder sits your jar, and all your dependencies jars. This technically is not a fat jar, because it is not a single jar. But jar-or-not is just a format. Eventually what you are after is just to get your code and dependencies across to the target host. The distribution zip does not mess around with jar because jar-merging is not as simple as folder merges. Instead distribution zip expects you to unzip on the target host and invoke the script under bin folder to start the application.