{"id":206,"date":"2020-03-30T21:22:06","date_gmt":"2020-03-30T19:22:06","guid":{"rendered":"https:\/\/nissel.it\/?p=206"},"modified":"2021-04-05T07:54:40","modified_gmt":"2021-04-05T05:54:40","slug":"java-selenium-tests-als-standalone-jar-ausfuehren","status":"publish","type":"post","link":"https:\/\/nissel.it\/index.php\/2020\/03\/30\/java-selenium-tests-als-standalone-jar-ausfuehren\/","title":{"rendered":"Java Selenium Tests als standalone jar ausf\u00fchren"},"content":{"rendered":"\n<p>Selenium Tests werden normalerweise in der IDE oder \u00fcber einen Jenkins ausgef\u00fchrt. M\u00f6chte man die Test aber von einem Benutzer lokal ohne Java Kenntnisse ausf\u00fchren lassen, so ergeben sich einige H\u00fcrden.<br>Voraussetzung ist der Artikel <a href=\"https:\/\/nissel.it\/index.php\/2020\/03\/29\/einrichten-von-selenium\/\">Einrichten von Selenium.<\/a> <\/p>\n\n\n\n<!--more-->\n\n\n\n<h2 class=\"wp-block-heading\">Junit Tests \u00fcber Konsole starten<\/h2>\n\n\n\n<p>Dazu gibt es f\u00fcr Junit5 ein Modul welches in der build.gradle unter dependencies eingebunden werden muss.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>implementation group: 'org.junit.platform', name: 'junit-platform-console-standalone', version: '1.6.1'<\/code><\/pre>\n\n\n\n<p>Danach kann entweder die main() Methode der Klasse ConsoleLauncher direkt aufgerufen werden oder \u00fcber eine eigene Main Methode gestartet werden.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>public static void main(String&#91;] args) throws IOException {\n    System.setOut(outputFile(\"result.txt\"));\n    ConsoleLauncher.main(\"--select-package=it.nissel.selenium\");\n}\nprivate static PrintStream outputFile(String name) throws FileNotFoundException {\n    return new PrintStream(new BufferedOutputStream(new FileOutputStream(name)), true);\n}<\/code><\/pre>\n\n\n\n<p>Welche Test ausgef\u00fchrt werden kann man \u00fcber verschiedene Wege mitgeben. Hier wurde dies anhand des package &#8222;it.nissel.sekenium&#8220; festgelegt. <a href=\"https:\/\/junit.org\/junit5\/docs\/current\/user-guide\/#running-tests-console-launcher\">Weitere Informationen.<\/a><br>Die Standard-Ausgabe wird in eine result.txt geschrieben. Da der Benutzer sp\u00e4ter das Programm nur ausf\u00fchren soll und am Ende keine Konsolenausgabe sieht, wird das Ergebnis in eine Datei geschrieben. In der Error-Ausgabe zeigt Selenium allerlei M\u00fcll an, was man \u00fcber System.<em>setErr<\/em>() z.B: in eine logfile schreiben k\u00f6nnte.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Download des Treibers<\/h2>\n\n\n\n<p>Um einen Seleniumtest auszuf\u00fchren ist ein entsprechender Treiber notwendig. Dieser muss sich je nach Plattform heruntergeladen werden. Damit dies nicht mitgeliefert werde muss, kann man sich die Datei \u00fcber Java herunter laden.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>@SneakyThrows\nprivate static File downloadDriverZip(Driver driver, String version) {\n    String urlString = driver.getDownloadUrl(OS.getOs(), version);\n    log.info(\"download driver from \" + urlString);\n    URL url = new URL(urlString);\n    URLConnection connection = url.openConnection();\n    InputStream driverInputStream = connection.getInputStream();\n    File targetFile = File.createTempFile(\"Selenium-driver\", \".zip\");\n    targetFile.deleteOnExit();\n\n    java.nio.file.Files.copy(\n            driverInputStream,\n            targetFile.toPath(),\n            StandardCopyOption.REPLACE_EXISTING);\n\n    driverInputStream.close();\n    return targetFile;\n}<\/code><\/pre>\n\n\n\n<p>In dem Enum Driver ist die URL zum Download enthalten und \u00fcber OS.getOS() wird das aktuelle Betriebssystem ermittel.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Betriebssystem ermitteln<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>public enum OS {\n    WINDOWS,\n    MAC,\n    LINUX;\n\n    public static OS getOs() {\n        String osString = System.getProperty(\"os.name\").toLowerCase();\n\n        if(osString.contains(\"win\")) {\n            return OS.WINDOWS;\n        } else if (osString.contains(\"mac\")) {\n            return OS.MAC;\n        } else if(osString.contains(\"nix\") || osString.contains(\"nux\") || osString.contains(\"aix\")) {\n            return OS.LINUX;\n        }\n        throw new RuntimeException(\"Unknown os \" + osString);\n\n    }\n}<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">URL generieren<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>public enum Driver {\n    CHROME(\"https:\/\/chromedriver.storage.googleapis.com\/\", \"chromedriver\");\n\n    private final String serverUrl;\n    private final String driverFileName;\n\n    Driver(String serverUrl, String driverFileName) {\n        this.serverUrl = serverUrl;\n        this.driverFileName = driverFileName;\n    }\n\n    public String getDownloadUrl(OS os, String version) {\n        String fileName;\n        switch (os) {\n            case WINDOWS:\n                fileName = \"chromedriver_win32.zip\";\n                break;\n            case MAC:\n                fileName = \"chromedriver_mac64.zip\";\n                break;\n            case LINUX:\n                fileName = \"chromedriver_linux64.zip\";\n                break;\n            default:\n                throw new RuntimeException(\"Unknown OS\" + os);\n        }\n        return serverUrl + version + \"\/\" + fileName;\n    }\n\n    public String getDriverFileName(OS os) {\n        String fileName = driverFileName;\n        if(OS.WINDOWS.equals(os)) {\n            fileName += \".exe\";\n        }\n        return fileName;\n    }\n\n}<\/code><\/pre>\n\n\n\n<p>Hier habe ich mir den Aufbau der <a href=\"https:\/\/chromedriver.storage.googleapis.com\/index.html\">Downloadseite des Google Chrome Treiber<\/a> angeschaut und mit getDownloadUrl() einen URL simplen Generator gebaut. getDriverFileName() wird sp\u00e4ter zum cachen ben\u00f6tigt.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Zipdatei entpacken<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>public static File downloadDriver(Driver driver,String version) {\n    List&lt;File&gt; fileList = ZipUtils.unzip(downloadDriverZip(driver, version).getAbsolutePath());\n    if(fileList.size() != 1) {\n        String filenames = fileList.stream().map(File::getName).collect(Collectors.joining(\", \"));\n        throw new RuntimeException(\"The driver file not found: \" + filenames);\n    }\n    File driverFile = fileList.get(0);\n    if(!driverFile.setExecutable(true)) {\n        log.warn(\"The driver file \" + driverFile.getAbsolutePath() + \" cannot be set executable\");\n    }\n    return driverFile;\n}<\/code><\/pre>\n\n\n\n<p>Die herunter geladene Datei muss entpackt werden und nur wenn eine Datei enthalten war wird dies der Treiber sein. Dies ist sehr primitiv aber funktioniert. Die Klasse ZipUtils ist eine selbst geschriebene Klasse um einfach Zip Dateien zu entpacken. Danach muss die Datei (auf jeden Fall unter Linux) ausf\u00fchrbar gemacht werden.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>public class ZipUtils {\n\n    public static List&lt;File&gt; unzip(String zipFile) {\n        return unzip(zipFile, \".\");\n    }\n\n    public static List&lt;File&gt; unzip(String zipFile, String destination) {\n        List&lt;File&gt; unzippedFiles = new ArrayList&lt;&gt;();\n        try {\n            ZipInputStream zipInputStream = new ZipInputStream(new FileInputStream(zipFile));\n            ZipEntry zipEntry = zipInputStream.getNextEntry();\n            while (zipEntry != null) {\n                File unzippedFile = new File(destination, zipEntry.getName());\n                if(zipEntry.isDirectory()) {\n                    if(!unzippedFile.mkdirs()) {\n                        throw new RuntimeException(\"Cannot create directory \" + unzippedFile.getAbsolutePath());\n                    }\n                }\n\n                unzippedFiles.add(unzippedFile);\n                if(!unzippedFile.isDirectory()) {\n                    zipInputStream.transferTo(new FileOutputStream(unzippedFile));\n                }\n                zipEntry = zipInputStream.getNextEntry();\n            }\n            zipInputStream.closeEntry();\n            zipInputStream.close();\n        } catch (IOException e) {\n            throw new RuntimeException(e.getMessage(), e);\n        }\n        return unzippedFiles;\n    }\n\n}<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Download Cache<\/h3>\n\n\n\n<p>Anhand des Dateinamens wird der Donwloadvorgang gecached.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>public static File cachedDownloadDriver(Driver driver,String version) {\n    String osFileName = driver.getDriverFileName(OS.getOs());\n    File cachedFile = new File(osFileName);\n    if (!cachedFile.exists()) {\n        return downloadDriver(driver, version);\n    } else {\n        return cachedFile;\n    }\n}<\/code><\/pre>\n\n\n\n<p>Hier k\u00f6nnte man sich auch einen intelligenteren Cache vorstellen der z.B: die Versionsnummer ber\u00fccksichtigt.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Ausf\u00fchrbares Jar File erzeugen<\/h2>\n\n\n\n<p>Da die Testf\u00e4lle unter src\/main\/test liegen werden sie normalerweise nicht mit in ein jar File aufgenommen. Dazu muss in der build.gradle folgender Abschnitt hinzugef\u00fcgt werden.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>jar {\n    manifest {\n        attributes \"Main-Class\": \"it.nissel.selenium.TestRunner\"\n    }\n\n    from {\n        configurations.testRuntimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }\n    }\n    from {\n        configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }\n    }\n    from sourceSets.test.output\n    from sourceSets.main.output\n}<\/code><\/pre>\n\n\n\n<p>Damit wird eine Jar Datei erzeugt die alle Abh\u00e4ngigkeiten aus dem Test und der Implementierung und die Klassen aus dem Test und der Implementierung in eine Jar packen. Zus\u00e4tzlich wird die Klasse it.nissel.selenium.TestRunner als Startpunkt f\u00fcr die Jar festgelegt. Die Datei wird durch den Gradle Build unter Build\/libs erzeugt.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Ausf\u00fchrbare .exe f\u00fcr Windows Benutzer erzeugen<\/h2>\n\n\n\n<p>Eigentlich kann bereits jetzt die jar Datei mit folgenden Befehl gestartet werden:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>java -jar MeineJar.jar<\/code><\/pre>\n\n\n\n<p>Wem das nicht reicht, der kann eine Java Runtime &gt;= 11 herunter bei sich lokal installieren und den Ordner unter c:\\Programme\\java\\jre11 (genauer Pfad ist mir gerade unbekannt) kopieren und mit in den Ordner der Jar legen. Dann kann mit <a href=\"http:\/\/launch4j.sourceforge.net\/\">Launch4J<\/a> eine .exe Datei erzeugt werden die, die JRE benutzt und die Jar Datei startet.<br>Hier gibt es glaube ich modernere M\u00f6glichkeiten, in die ich mich noch nicht eingearbeitet habe. <\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Links<\/h2>\n\n\n\n<p><a href=\"https:\/\/junit.org\/junit5\/docs\/current\/user-guide\/#running-tests-console-launcher\">https:\/\/junit.org\/junit5\/docs\/current\/user-guide\/#running-tests-console-launcher<\/a><\/p>\n\n\n\n<p><a href=\"http:\/\/launch4j.sourceforge.net\/\">http:\/\/launch4j.sourceforge.net\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Selenium Tests werden normalerweise in der IDE oder \u00fcber einen Jenkins ausgef\u00fchrt. M\u00f6chte man die Test aber von einem Benutzer lokal ohne Java Kenntnisse ausf\u00fchren lassen, so ergeben sich einige H\u00fcrden.Voraussetzung ist der Artikel Einrichten von Selenium.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[7],"tags":[16,10],"class_list":["post-206","post","type-post","status-publish","format-standard","hentry","category-softwareentwicklung","tag-java","tag-selenium"],"_links":{"self":[{"href":"https:\/\/nissel.it\/index.php\/wp-json\/wp\/v2\/posts\/206","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/nissel.it\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/nissel.it\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/nissel.it\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/nissel.it\/index.php\/wp-json\/wp\/v2\/comments?post=206"}],"version-history":[{"count":3,"href":"https:\/\/nissel.it\/index.php\/wp-json\/wp\/v2\/posts\/206\/revisions"}],"predecessor-version":[{"id":375,"href":"https:\/\/nissel.it\/index.php\/wp-json\/wp\/v2\/posts\/206\/revisions\/375"}],"wp:attachment":[{"href":"https:\/\/nissel.it\/index.php\/wp-json\/wp\/v2\/media?parent=206"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/nissel.it\/index.php\/wp-json\/wp\/v2\/categories?post=206"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/nissel.it\/index.php\/wp-json\/wp\/v2\/tags?post=206"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}