0

I can't seem to build a 'solution' that works with the resources in my project. When I run my test during build, it can naturally access the resources, but after build the test can't find the file. How do I adjust my code so it works in both cases, or adjust my project. I'm interested to know the fundamentals on Java project setup with respect to this, in addition to my particular situation using the maven-shade plugin, time allowing.

I have tried various path variations with no luck

I have tried getClass().ClassLoader this and that with no luck.

My pom.xml

<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>company.myproj</groupId>
  <artifactId>myproj</artifactId>
  <version>0.0.1</version>
  <packaging>jar</packaging>

    <properties>
       <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
       <skipTests>true</skipTests>
  </properties>
    <dependencies>
        <!-- https://mvnrepository.com/artifact/org.testng/testng -->
        <dependency>
            <groupId>org.testng</groupId>
            <artifactId>testng</artifactId>
            <version>7.0.0-beta3</version>
            <!-- <scope>provided</scope> -->
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.seleniumhq.selenium/selenium-java -->
        <dependency>
            <groupId>org.seleniumhq.selenium</groupId>
            <artifactId>selenium-java</artifactId>
            <version>3.141.59</version>
            <!-- <scope>provided</scope> -->
        </dependency>

    </dependencies>


    <build>

        <!-- Source directory configuration -->
        <sourceDirectory>src</sourceDirectory>

        <resources>
                <resource>    
                    <directory>src/resources</directory>
                </resource>
        </resources>

        <plugins>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>


                <!-- // Following plugin executes the testng tests  -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>2.14.1</version>
                <configuration>
                    <!-- // Suite testng xml file to consider for test execution  -->
                    <suiteXmlFiles>
                        <suiteXmlFile>testtest.xml</suiteXmlFile>
                    </suiteXmlFiles>
                      <skipTests>${skipTests}</skipTests>
                </configuration>
            </plugin>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <version>3.2.1</version>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                        <configuration>
                            <filters>
                                <filter>
                                    <artifact>*:*</artifact>
                                    <excludes><exclude>META-INF/versions/**</exclude></excludes>
                                </filter>
                            </filters>
                            <transformers>
                                <transformer
                                        implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                                    <mainClass>org.testng.TestNG</mainClass>
                                </transformer>
                            </transformers>
                        </configuration>
                    </execution>
                </executions>
            </plugin>

        </plugins>
     </build>

</project>

My java

package myproj;

import java.io.IOException;
import java.net.URISyntaxException;
import java.nio.file.Path;
import java.nio.file.Paths;

import org.testng.annotations.Test;

public class TestTest{

    @Test(groups = { "basic1" })
    public void Test05BasicPASS() throws URISyntaxException, IOException {


        Path filePath = Paths.get(getClass().getResource("/images/hydrant.jpg").toURI());


        System.out.println("My Path is: "+filePath.toString());


        System.out.println("This is test 5, Basic Pass");
    }

}

My testNg TestTest.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >
<suite name="SuiteB1" parallel="false">
  <test name="TestTest">

    <groups>
        <run>
          <include name="basic1"/> 
        </run>      
    </groups>

    <classes>
      <class name="myproj.TestTest"/>
    </classes>
  </test> <!-- Test -->
</suite> <!-- Suite -->

My directory structure:

src
  -myproj
    TestTest.java
  -resources
    -images
      hydrant.jpg

TestTest.xml
pom.xml

when I run: mvn package -DskipTests=false the test runs and can access the image file.

after build, when I run on command line(builds to 'target' dir: java -jar C:\Dev\testproj\target\myproj-0.0.1.jar TestTest.xml

I get in test results FileSystemNotFoundException on the line where the file is being accessed.

Eelke
  • 21
  • 6
  • Does this help? https://stackoverflow.com/questions/3861989/preferred-way-of-loading-resources-in-java – Matthew Kerian Jun 18 '19 at 18:34
  • 2
    I strongly recommend to follow the convention over configuration paradigm and put java code for production into `src/main/java` for unit tests into `src/test/java` and resources for production into `src/main/resources` and for unit tests `src/test/resources` so in the end remove `sourceDirectory` and `resources` definition ... – khmarbaise Jun 18 '19 at 18:47
  • @MatthewKerian, I tried the different options in that post. I must still be not getting something fundemental. Why is it not as simple as, it is a directory relative to your code, you build, it gets built in? – Eelke Jun 18 '19 at 19:26
  • @khmarbaise, I'm building a test 'application' that I want buildable and deployable to test machines that can run the tests on their own. Looking for a simple setup. the test code is the application/product. test code is typically not compiled into the resulting jar as I am doing and desiring. I would like either a single jar with everything in it aside from the testng.xml files, or the jar with a single 'resource' directory that lives alongside the jar, if the first option isn't possible. – Eelke Jun 18 '19 at 19:29
  • @khmarbaise, I would add, i do appreciate and understand your thought, but I believe one way or another, what I am doing is not conventional. I don't have production java code and test java code. I have test java code that wants to be production code. if that makes better sense. the application under test is not java and external to this, a web application. I am trying to cobble together a buildable/deployable/runnable test product containing my test code which is built using TestNG and Selenium. I am using the TestNG main. this works aside from accessing img files in built prd. – Eelke Jun 18 '19 at 20:26
  • If your project is meant to be used for testing _other_ projects then your code belongs in the production directory (i.e. `src/main/java`)—even if that code uses dependencies that are typically used in the test scope. The resources used for this project belong in `src/main/resources`. Consider, both the JUnit and TestNG projects have all their main code in `src/main/java` even though the projects are themselves testing frameworks. – Slaw Jun 18 '19 at 21:29
  • Ok, I've attempted the recommendations of @Slaw and khmarbaise. Still results in a FileSystemNotFoundException when I try to run jar from command line. Now what? – Eelke Jun 18 '19 at 22:49
  • what would it be like if I used a file in an external directory, either absolute or relative path? Is that better? easier? – Eelke Jun 18 '19 at 22:53
  • Put your image file into `src/test/resources/images` and use `getResourcesAsStream("/images/hydrant.jpg")` and don't use file based access... – khmarbaise Jun 20 '19 at 11:49

1 Answers1

0

In the end I have decided that what Java "dictates" for convention, with directories being required in specific places and methods that work in one case and not another, was too convoluted for the simple test application I was implementing.

I found the following supports what I need in a simple manner:

For starters the images I used are for test comparisons, so as these may need to be swapped out on the fly for debugging, it helps to have the directory external to the application. If someone shares how to change the title of this question, I'm happy to do it.

So, with that in mind. I am now having an "images" directory in the root of my project, AND this directory will be deployed adjacent to the application, resulting in the following basic product:

-myTestApp
   myTestApp.jar
   -images
      baseImage.jpg
      testImage.jpg

The code I am using to access the files from my test method is as follows and works for build test and running as a jar application:

Path screenshotsPath = Paths.get(System.getProperty("user.dir"), "images");

Path baseFile = Paths.get(screenshotsPath.toString(), "baseImage.jpg" );
Path testFile = Paths.get(screenshotsPath.toString(), "testImage.jpg" );

double imageDiff =  calcImageDiff(baseFile, testFile);

Within the calcImageDiff I am just using the following, where file1 and file2 are Path inputs:

img1 = ImageIO.read(file1.toFile());
img2 = ImageIO.read(file2.toFile());

Eelke
  • 21
  • 6
  • Ah. Yes, if the images (or other "resources") are meant to be configurable by the end user then they're not actually _resources_. Putting such files in some known, external location is the correct approach. This is also true of any file that will be written to—even by the application itself—as _resources_ are typically read-only once the application is deployed. – Slaw Jun 22 '19 at 03:18
  • And note that it's not Java that "dictates" the standard directory layout; it's actually [specified by the Maven build tool](https://maven.apache.org/guides/introduction/introduction-to-the-standard-directory-layout.html) and the community has widely accepted it. Other tools, such as Gradle, also follow this directory layout by default. It helps to keep Java source files in one directory and all resources in another. – Slaw Jun 22 '19 at 03:19