Spring boot: How to read resource from classpath in unit test - java

I'm trying to read a file from classpath like this in my unit test:
#Value("classpath:state.json")
Resource stateFile;
I have state.json file in src/test/resources directory.
When I try to read this file using stateFile.getInputStream(), it doesn't return any contents. What am I doing wrong?
My test class is annotated like this
#RunWith(SpringRunner.class)
#SpringBootTest
I can see that the code fails if I try with a incorrect file. So I think its seeing the file in classpath but for some reason not reading contents.

I just ran into this. I'm using Maven. I took a look at my target/test-classes folder and my resource file wasn't in there (even though it was in my src/test/resources folder).
I ran mvn clean install and then rechecked my target/test-classes folder and the resource file was now there. After that, my test was able to find the file and the test worked.
So it seems that your resources aren't copied until you do a mvn clean. JUnit is looking in the classpath built by maven and until the file actually makes it into the target/test-classes folder, JUnit won't be able to find it.

You cant access a #Value resource unless its a property defined.
It should be this way.
#Value("${stateJsonPath}")
Resource stateFile;
If you have to get the resource from hardcoded path then use this way.
Resource stateFile = new ClassPathResource("state.json");

Sharing the working solution for posterity. Files present in the classpath can be read using org.springframework.core.io.ResourceLoader. For instance, I have a sample data file called posts.json under directory src/test/java/resources/data and I have to load it during the test case execution as part of #Before
#ActiveProfiles("test")
#ExtendWith(SpringExtension.class)
#SpringBootTest(classes = Application.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class SampleTest {
#Autowired
private ResourceLoader resourceLoader = null;
#BeforeEach
void init() {
File dataFile = resourceLoader.getResource("classpath:data/posts.json").getFile();
...
...
}
}
NOTE: the file should present in the classpath when you use the classifier classpath:

This should simply work, notice that the path starts with a dot, indicating current directory;
#Test
public void testFile() {
String path = "./src/test/resources";
String fileName = "test.zip";
File file = new File(path, fileName);
assertTrue(file.exists());
}

Related

Spring boot test: #Sql annotation Unable to locate sql files placed in src/test/resources

I didn't want to load the entire Spring Boot configuration for unit-testing my DAO layer, and therefore created a nested configuration class to suppress default configurations. But when I try to specify SQL scripts for it to run before tests, its unable to find them.
Here's the code:
package com.test.customer.controller;
..
#RunWith(SpringRunner.class)
#JdbcTest
#Sql({"data.sql"})
public class InterviewInformationControllerTest {
#Configuration
static class TestConfiguration{
}
#Test
public void testCustomer() {
// code
}
}
I get the error: Cannot read SQL script from class path resource [com/test/customer/controller/data.sql]; nested exception is java.io.FileNotFoundException: class path resource [com/test/customer/controller/data.sql] cannot be opened because it does not exist
I've tried placing the file at both src/main/resources (not preferred) as well as at src/test/resources (which I prefer)
Note: I'm running the Unit test from inside Eclipse by doing Run as -> JUnit test.
Edit: Added the static keyword to the configuration class
your inner configuration class will not work unless you add a static keyword before its definition. However you should know that for the #Sql annotation
Path Resource Semantics
Each path will be interpreted as a Spring Resource. A plain path — for
example, "schema.sql" — will be treated as a classpath resource that
is relative to the package in which the test class is defined. A path
starting with a slash will be treated as an absolute classpath
resource, for example: "/org/example/schema.sql". A path which
references a URL (e.g., a path prefixed with classpath:, file:, http:,
etc.) will be loaded using the specified resource protocol.
So try to prefix the value inside #Sql with classpath: like this :
#Sql(scripts={"classpath:data.sql"})
Good luck!
Check your out directory of the module. While creating directory in resources, if you have named it directly with the namespace like com.gakshintala.bookmybook, it takes that name completely as the directory name, so the out also contains this directory under resources with name com.gakshintala.bookmybook. PFB right vs wrong. The top is right and bottom is wrong.
Spring always looks for nested directory resources->com->gakshintala->bookmybook.
So create that directory structure.

JUnit Error: "Failed to load ApplicationContext"

Nothing seems to work
I'm trying to run simple test:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {"/applicationContext.xml"})
#Transactional
public class EmailServiceTest {
I've also tried:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {"src/main/webapp/WEB-INF/applicationContext.xml"})
#Transactional
public class EmailServiceTest {
Along with a few different things in place of "location," such as "classpath" and "file."
The "ApplicationContext" is located:
src
main
webapp
WEB-INF
applicationContext.xml
But JUnit test still says: Failed to load ApplicationContext
The issue was solved after we realized our build file was missing information pertaining to testing. Information such as the "app.properties" and " applicationContext" were not being copied into the testing resources. So technically, none of these were on the classpath.
"Failed to load ApplicationContext" can happen due to other reasons. Go through the stacktrace and find the cause of this error.
In my case, I got this error because I had a typo in the application context xml file name.
you need to use #ContextConfiguration(locations = { "file:./src/main/resources/ApplicationContext.xml" }) after #RunWith(SpringJUnit4ClassRunner.class). and test.java file create at /src/test/java/your-package-name.
You have to specify an application context location on the classpath. src/main is added to the classpath. Therefore, you need to specify only webapp/WEB-INF/applicationContext.xml as follows: #ContextConfiguration(locations = {"webapp/WEB-INF/applicationContext.xml"}).

how to load application-context.xml using #ContextConfiguration annotation

My project structure is as below:
src/main/java -> contains java classes
src/main/resources/spring/context/application-context.xml
src/test/java -> contains J-unit test
I would like to use #ContextConfiguration annotation to load my application-context.xml
How can I load this file and how can I make sure that all beans are loaded?
I tried it using classpath and file. But nothing works for me.
I am confused when to use classpath and file. Some one please help me with this.
Thanks in advance.
#ContextConfiguration("classpath:/spring/context/application-context.xml") should work.
In conventional Maven project layout, src/main/resources contains classpath resources, therefore you should use classpath: or no prefix at all, because classpath: should be a default one in this case.
If it still doesn't work, perhaps something is wrong with your project configuration and files from src/main/resources doesn't appear in the classpath.
If context loads successfully, all beans in it should be loaded as well, otherwise context will fail to load.
Try with:
ApplicationContext APPLICATION_CONTEXT = new ClassPathXmlApplicationContext("/spring/context/application-context.xml");
If it does not work, try putting application-context.xml directly in src/main/resources and then load it with
ApplicationContext APPLICATION_CONTEXT = new ClassPathXmlApplicationContext("application-context.xml");

Can not open beans.xml (config file) because does not exist

Exception in thread "main"
org.springframework.beans.factory.BeanDefinitionStoreException:
IOException parsing XML document from class path resource
[com/main/beans.xml]; nested exception is
java.io.FileNotFoundException: class path resource
[com/main/beans.xml] cannot be opened because it does not exist
ApplicationContext context =
new ClassPathXmlApplicationContext("com/main/beans.xml");
I have tried before with
ApplicationContext context =
new FileSystemXmlApplicationContext("src/main/java/com/main/beans.xml");
And it works well.
How to do that relative to the classpath?
Note: classpath is in the build path
In the example I'm following, it has the following structure and it works
Project structure
Classpath
ApplicationContext context =
new ClassPathXmlApplicationContext("com/caveofprogramming/spring/test/beans/beans.xml");
Here's the file structure I normally use, which works fine. As #M.Deinum said, you'll want to put your xml file in a src/main/resources. I normally put to the it in a complete package path with the resources so during compile time, maven will add all the resources to same path as the corresponding classes that use them.
resources get copied to the class package when you do the above
public class App {
public static void main(String[] args) {
ApplicationContext context
= new ClassPathXmlApplicationContext("com/underdogdevs/stackmaven/beans.xml");
Hello hello = (Hello) context.getBean("hello");
hello.sayHello();
}
}
Works fine for me. If you're wondering why you still need to use the complete package name when the xml is already in the same class packages, its it will first be searched for in the class root
UPDATE
put the package with the bean.xml into the src/main/resources. It should work with the path your using.
UPDATE 2
"Yes, it worked. But why is it working the example, I'm following as well. If the beans.xml is out of src/main/resources .. I can't find out how that works? *
The thing is, the Spring container will look from the class root. It has nothing to with the resources folder. The resources is a convenience dir for maven projects to build to your class path. The reason the tutorial works, is that the beans.xml is in a package, that will get put into the class path in the build, as seen below. It is only preferred to use a resources, but a package` will also build to the class path.

FileNotFoundException in test case run with maven using Spring resource loader

Our program is sending mails. For sending mails it includes an attachment.
The project is set up with maven. The JUnit test case loads the spring configuration first. Spring is loading the File with its DefaultResourceLoader. When running this directly within the workspace its running fine, however as a maven test it fails.
Spring Configuration for resource loader:
<property name="defaultResourceLoader">
<bean class="org.springframework.core.io.DefaultResourceLoader" />
</property>
ImplementingClass:
#Service
public class SomeClassA {
#Autowired
private DefaultResourceLoader defaultResourceLoader;
#Value("${mailLogoPath}")
private String mailLogo;
public void someMethod(){
String filePath = defaultResourceLoader.getResource(mailLogo).getFile().getAbsolutePath();
}
}
The exception points to the actual problem and difference:
java.io.FileNotFoundException: class path resource [templates/efmaillogo.jpg] cannot be resolved to absolute file path because it does not reside in the file system: jar:file:/C:/IBM/workspace/efMy10/ef-mvxsrv-reactor/ef-mvxsrv-service-resources/target/ef-mvxsrv-service-resources-1.5.23-SNAPSHOT.jar!/templates/efmaillogo.jpg
at org.springframework.util.ResourceUtils.getFile(ResourceUtils.java:204)
at org.springframework.core.io.AbstractFileResolvingResource.getFile(AbstractFileResolvingResource.java:52)
at SomeClassA.someMethod(SomeClassA.java:9)
Spring tries to look the file up within the jar. When running it as a junit test within eclipse, everything is working, because spring finds the file directly within the workspace. However when running the same unit test with maven it fails, because it doesn't find the file, because its in a jar in a different module. The file in the other module is needed by different modules, so I don't want to move it. On the application server where everything is deployed its working as well, because the .ear file is expanded.
I'm wondering if there is a different way to access the file with Spring, so that I don't have to skip this test case with maven.
Update
Tried doing a lookup for the file with:
String filePath = defaultResourceLoader.getResource(mailLogo).getURL.getFile();
However it now fails when actually sending the mail with Transport.send(msg).
java.io.FileNotFoundException: file:\C:\Users\uhe.m2\repository\com\clavisit\ef\mvxsrv\ef-mvxsrv-service-resources\1.5.23-SNAPSHOT\ef-mvxsrv-service-resources-1.5.23-SNAPSHOT.jar!\templates\efmaillogo.jpg (The filename, directory name, or volume label syntax is incorrect.)
at com.sun.mail.smtp.SMTPTransport.sendMessage(SMTPTransport.java:1141)
at javax.mail.Transport.send0(Transport.java:195)
at javax.mail.Transport.send(Transport.java:124)
at com.clavisit.ef.ep.service.integration.handler.mail.SendMail.sendMessage(SendMail.java:153)
String filePath = defaultResourceLoader.getResource(mailLogo).getURL().getFile();
The above code change must work as per the discussion in this thread http://www.coderanch.com/t/474047/Spring/Spring-cannot-find-file-classpath

Categories