project layout and spring-test support issues - java

We have project layout as below.
src
src/test/java
src/test/resources
and we cant add src/main/(java, resources) for code, because of earlier developemnt done.
src/test/java is having both unit and integration tests in same package as class under test has in src folder.
unit tests are running fine in current setup but issues are with running integration tests.
integration tests run perfectly fine when test class and configuration files are along side class under test, in src folder and same package as class.
but when i put test class in src/test/java and config files in src/test/resources test fails to run because of context initialization issues.
please note following about env setup
1 build output folder for all the src , src/test/java and src/test/resources is src folder only.
2 i am using classpath*: to specify config location, as otherwise spring fails to locate config file in resource folder.
#ContextConfiguration(locations={"classpath*:applicationContext_getCorpAcctPrefDetailsSP.xml"})
3 tried both #Autowired and setter based DI for test classes
> (i). in case of #Autowired i get error for depedency saying
No unique bean of type GetCorpAccountPreferencesDetailsSP is defined expected at least 1 matching bean
also i am using base package scan
> (ii). in case of Setter based DI context get initialized and unit test run but all the dependencies injected are null in test class.
please expalin what can be reason for issue and any solution.
As everything is working fine when integration tests are in src folder alongside class under test.
i suspect differect source folders (src and test)creating issue when spring create context as class under test is not in same source folder as test.
thanks
nBhati

At run-time Spring doesn't care (or know) which folder your original source code is in. All that matters is the classpath - which compiled files and which resource folders are being put on the classpath. If you are getting errors about XML files that cannot be found when you run your tests, that strongly suggests that those XML files are not on the classpath when the tests run.

Related

Is ApplicationContext file necessary if I want to run the unit test?

everyone,
I'm new on web development. Recently, I start a tutorial to build Registration and Login with Spring Boot and MySQL Database. When I run the unit test, there are errors showing up. I cannot find ApplicationContext file in the tutorial, and also try serval methods on stack overflow e.g. change plug setting in pom.xml but isn't working.
Can anyone answer this problem?
Errors on IDE
The application context is not a file – it's a term Spring uses to describe the entire state of your application. So the error means that Spring is not able to start your application because of missing database configuration.
Looks like you are using maven (src/main/java). In this case put the applicationContext.xml file in the src/main/resources directory. It will be copied in the classpath directory and you should be able to access it with
#ContextConfiguration("/applicationContext.xml")
A plain path, for example, "context.xml", will be treated as a classpath resource from the same package in which the test class is defined. A path starting with a slash is treated as a fully qualified classpath location, for example "/org/example/config.xml".
So, it's important that you add the slash when referencing the file in the root directory of the classpath.

How to read generated properties file in Spring Boot integration test?

In a Spring Boot web application, I use the git-commit-id-plugin Maven plugin to generate a file named git.properties, containing all the git commit information, e.g:
git.commit.id=35ca97298544d4ee6f8a5392211ebaa0d9bdafeb
This file is generated directly in the target/classes repository. So it's included in the classpath. At runtime the file is loaded via an annotation on my main application class :
#PropertySource({"git.properties"})
I can then use expressions in my beans to get the value of the properties contained in the git.properties file :
#Value("${git.commit.id}")
private String gitCommitIdFull; // will contain "35ca97298544d4ee6f8a5392211ebaa0d9bdafeb"
It all works very well when running the app normally.
But I am now trying to run some integration tests that are run with :
#RunWith(SpringRunner.class)
#SpringBootTest
public class SampleSearchDAOTest {
//tests here...
}
I get the following exception :
java.lang.IllegalStateException: Failed to load ApplicationContext
(...)
Caused by: org.springframework.beans.factory.BeanDefinitionStoreException:
Failed to parse configuration class [ch.cscf.mds.MdsApiApplication];
nested exception is java.io.FileNotFoundException: Could not open ServletContext resource [/git.properties]
Obviously, the way the tests are run, they seem to not use target/classes as the base for the classpath.
What does it use ? How can I make the runtime for these tests aware of the target/classes/git.properties file ?
I tried to generate the git.properties file into the src/main/resources directory instead of the target/classes repository. I still have the same error.
Test runner by default looks for resources relative to the test folder. For example when the git.properties file would have been present in src/test/resources, then it should also work.
#PropertySource({"classpath:git.properties"}) tells to look for sources from the entire classpath.

log4j2.xml and log4j2-test.xml in eclipse

I have these two log files on my eclipse classpath, in src/main/resources and src/test/resources respectively.
The problem is that log4j2-test.xml is higher priority and is always the chosen configuration file when running my application. How do I tell eclipse to ignore log4j2-test.xml and use log4j2.xml when running my application and to fall back to log4j2-test.xml when running unit tests?
Just for the case that somebody had still some major trouble like me.
A working solution is:
create a plugin MyLog4J that contains and provides the log4j jar files.
add "Eclipse-BuddyPolicy: registered" the Manifest.MF of the MyLog4J plugin
in your plugin that defines your actual application (e.g. Entrypoint) put log4j2.xml in the src folder
AND add "Eclipse-RegisterBuddy: MyLog4J" to it's Manifest.MF
That Eclipse-RegisterBuddy is the key step to allow log4j to find the log4j2.xml file during startup!
Now you can re-use the MyLOG4J plugin in all your projects and have an individual log4j2.xml file for each application.
You can execute this below statement in junit setup() method.This file should be avilable in the same package where your running this or you can provide the full path for this file.
PropertyConfigurator.configure("log4j2-test.xml");
In log4j2 you can write the following
org.apache.logging.log4j.LogManager ctx = (org.apache.logging.log4j.LogManager) LogManager.getContext(true);
ctx.setConfigLocation(LoggerTest.class.getClassLoader().getResource("log4j2-test.xml").toURI());
assuming log4j2-test.xml is in your classpath.

CDI: beans.xml, where do I put you?

I am using Weld as CDI implementation. My integration test, that tries to assemble object graph instantiating Weld container works well, when I have empty beans.xml in src/test/java/META-INF/beans.xml. Here is that simple test:
public class WeldIntegrationTest {
#Test
public void testInjector() {
new Weld().initialize();
// shouldn't throw exception
}
}
Now when I run mvn clean install, I always get: Missing beans.xml file in META-INF!
My root folders are "src" and "web" which contains WEB-INF folder, but I also tried to use default maven structure and renamed "web" to "webapp" and moved it to src/main. I tried all the reasonable locations I could thought of:
- src/main/java/META-INF/beans.xml
- src/test/java/META-INF/beans.xml
- web/WEB-INF/beans.xml
- src/main/webapp/WEB-INF/beans.xml
- src/main/webapp/META-INF/beans.xml
- src/main/webapp/META-INF/(empty) and src/main/webapp/WEB-INF/beans.xml
Nothing works so far :/
For EJB and JAR packaging you should place the beans.xml in src/main/resources/META-INF/.
For WAR packaging you should place the beans.xml in src/main/webapp/WEB-INF/.
Remember that only .java files should be put in the src/main/java and src/test/java directories. Resources like .xml files should be in src/main/resources.
Just to complement the above answer, here is an official reference on this:
https://docs.oracle.com/javaee/6/tutorial/doc/gjbnz.html
quote:
An application that uses CDI must have a file named beans.xml. The file can be completely empty (it has content only in certain limited situations), but it must be present. For a web application, the beans.xml file must be in the WEB-INF directory. For EJB modules or JAR files, the beans.xml file must be in the META-INF directory.
http://www.javamonamour.org/2017/11/cdi-in-java-se-8.html here you can see where I have put it with success, that is under src/META-INF . The post contains a full working example of CDI in Java SE.

How to access file in the static resources(WEB-INF) folder from the referencing java project?

I have a web application, that contains a configuration xml file for one of my application services that is exposed as spring bean.
Also I have a standalone java application(which references my web app project from its pom.xml) in the same workspace, that runs tests using Spring TestContext framework and one of the tests checks the configuration of that XML file.
However I have a problem with accessing this xml file from the standalone app:
Before setting-up the test, in my previous configuration, the file was accessed through ServletContext and was located in WEB-INF/ folder. However, to make it accessable from the test project I had to move it to source/ folder and load it with getClassLoader().getResourceAsStream() method instead that of ServletContext. But it makes editing the file cumbersome because every time the app has to be redeployed.
Is it possible to keep the file in WEB-INF/ folder but load it from the referencing project during the test-runs?
P.S. Currently it's an STS project with Tomcat server.
Definitely keep the file under WEB-INF/ folder if that's where it is supposed to live.
For your test classes that are being executed from the command line. Your can use getClassLoader().getResource() on a file that you know is in the root of your classpath (e.g. application.properties file). From there you know the structure of your project and where to find WEB-INF/ relative to the properties file. Since it returns a URL you can use it to figure out a path to the XML files you're looking for.
URL url = this.getClass().getClassLoader().getResource("application.properties");
System.out.println(url.getPath());
File file = new File(url.getFile());
System.out.println(file);
// now use the Files' path to obtain references to your WEB-INF folder
Hopefully you find this useful. I have had to make assumptions about how your test classes are runing etc.
Take a look at the File Class and it's getPath(), getAbsolutePath(), and getParent() methods that could be of use to you.
I ended up using Spring MockServletContext class and injecting it directly to my service bean, before the test runs, as my service implemented ServletContextAware :
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "/test-ctx.xml" } )
public class SomeServiceTest {
#Autowired
private MyServletContextAwareService myService;
#Before
public void before(){
//notice that I had to use relative path because the file is not available in the test project
MockServletContext mockServletContext = new MockServletContext("file:../<my web project name>/src/main/webapp");
myService.setServletContext(mockServletContext);
}
If I had several classes using Servlet Context, then the better solution would be to use WebApplicationContext instead the default one (currently provided by DelegatingSmartContextLoader), but it would require implementing custom ContextLoader class and passing its class name to #ContextConfiguration annotation.
alternative and somewhat cleaner solution which later came to my mind is to refactor the service and inject ServletContext via #Autowired instead of messing with ServletContextAware, and provide the bean of corresponding type(effectively a MockServletContext instance).
Possibly, in future the direct support of MockServletContext from test classes will be added to Spring see SPR-5399 and SPR-5243.
UPDATE FOR SPRING 3.2
In Spring 3.2 initialization of the servlet context became as simple as adding one #WebAppConfiguration annotation:
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration("file:../<my web project name>/src/main/webapp")
#ContextConfiguration(locations = { "/test-ctx.xml" } )
public class SomeServiceTest {
see details in the article
In a Maven project I had same problem. I had no servletContext and couldn't access static file in
WEB-INF directory.
I came across to a solution by adding entry to pom.xml which gave me access to the directory. It actually includes this path to the classpath.
PS: I was using Tomcat container
<project>
<build>
<resources>
<resource>
<directory>src/main/webapp/WEB-INF</directory>
</resource>
</resources>
</project>
It's a classpath resource, so put it on the classpath: $webapp/WEB-INF/classes
Maven projects will copy things in $module/src/main/resources to this location when packaging the webapp.
(the former is a sourcepath, the latter - WEB-INF/classes - is always put on the classpath by the servlet container, per spec.)

Categories

Resources