The following test illustrates that this test bean is initialized twice by Spring. I'm hoping someone can tell me why this is so, since it should only be once. Here's the test:
import org.apache.log4j.Logger;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {} )
public class TestAfterPropsSet implements InitializingBean {
private static final Logger logger = Logger.getLogger(TestAfterPropsSet.class);
#Test
public void test1() {
logger.debug("Test1");
}
#Test
public void test2() {
logger.debug("Test2");
}
public void afterPropertiesSet() throws Exception {
logger.debug("Bean Initialized");
}
} // end class
Here's the bean file:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
</beans>
and here's the output:
2009-10-13 21:20:04,393 [TestAfterPropsSet.java 26] DEBUG - Bean Initialized
2009-10-13 21:20:04,393 [TestAfterPropsSet.java 17] DEBUG - Test1
2009-10-13 21:20:04,393 [TestAfterPropsSet.java 26] DEBUG - Bean Initialized
2009-10-13 21:20:04,393 [TestAfterPropsSet.java 22] DEBUG - Test2
It's not a Spring convention. You should be following JUnit conventions, i.e. suite-wide initialization or deconstruction should be done in #BeforeClass and #AfterClass accordingly, or you can use #Autowire and let Spring handle the object's scope.
A new suite will be constructed for each test. This is more apparent in JUnit3 where you had to create a new suite using a specified test name.
Take a look at the JavaDoc:
The Test annotation tells JUnit that
the public void method to which it is
attached can be run as a test case. To
run the method, JUnit first constructs
a fresh instance of the class then
invokes the annotated method. Any
exceptions thrown by the test will be
reported by JUnit as a failure. If no
exceptions are thrown, the test is
assumed to have succeeded.
Your use case is a bit puzzling since your test isn't actually doing anything and there is no bean, which you reference. By default, Spring beans are declared with the default scope="singleton" attribute, so had you actually declared a bean, it would have been a cached singleton. However, this has nothing to do with method execution.
Related
I have utility classes exposed as beans in my source folders. I want to use some of those utilities in my test classes written in junit 4. For example , I have a utility class that has methods which marshal an object into JSON string. I want to inject this utility bean in my test class. I am unable to inject these beans using Autowired annotation. Should I copy all these classes over to test folder?
Edit:
I am trying to inject jsonUtil. Below is how my code looks like.
import static org.junit.Assert.*;
import java.math.BigDecimal;
#RunWith(MockitoJUnitRunner.class)
#SpringApplicationConfiguration(classes = ProxyApplicationMock.class)
public class ProxyApplicationMock {
#Mock
public SoapClient soapClientMock;
private JsonUtil jsonUtil;
Main Class
public class ProxyApplication {
public static void main(String[] args) {
SpringApplication.run(ProxyApplication.class, args);
}
}
Your main classes can be seen by your test classes, but not the other way around. So no, you don't need to copy them.
If your utility class is declared as a Spring managed bean in your test Spring context configuration (the class -or XML file- declared in the #ContextConfiguration) which may and probably should be different from your main configuration.
Then you can inject it in any Spring managed class, which includes your test classes if it's using the SpringJUnit4ClassRunner.
EDIT:
To sum up what we discussed in the comments, the main problem is that your test runner is not a SpringRunner (alias for SpringJUnit4ClassRunner), and thus JUnit is not running your test in a Spring context. Have a look at a test example here.
The simplest test case will look like this.
#RunWith(SpringRunner.class)
#SpringBootTest
public class CityRepositoryIntegrationTests {
#Autowired
private MySpringBean springBean;
//...
}
But as often with Spring Boot, there's some magic happening behind. #SpringBootTest is a convenient annotation that will detect automatically a class annotated with #SpringBootConfiguration, meaning if you don't have a specific Spring configuration for your test, it will use your main Spring configuration, and thus include and instanciate all the beans for your main app, and that's not usually what we want in a unit test cause we want to test a class independently by mocking its dependencies.
What you can do, is provide the Spring compenent classes you want to include in your tests, as such:
#RunWith(SpringRunner.class)
#SpringBootTest(classes = MySpringBean.class)
public class CityRepositoryIntegrationTests {
#Autowired
private MySpringBean springBean;
#Mock
private MyMockedSpringBeanDependency mocked;
//...
}
This question is to Matt, since adding comment is throwing error saying only one additional use can be notified.
NOTE: Not an Answer
I have an Application class & many config classes which are imported in Application class. Earlier it was #Configuration, which I converted to #SpringBootConfiguration in Application class and the actual Config class, whos bean I am trying to mock. Ended up in
java.lang.NoSuchMethodError: org.springframework.boot.SpringApplication.<init>([Ljava/lang/Object;)V
at org.springframework.boot.test.context.SpringBootContextLoader.getSpringApplication(SpringBootContextLoader.java:121)
at org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:84)
Please suggest How do I mock, I have the same setup as specified in the ticket.
#Bean
public CacheManager cacheManager()
{
EhCacheManagerFactoryBean factoryBean = new EhCacheManagerFactoryBean();
factoryBean.setConfigLocation(new ClassPathResource("ehcache.xml"));
factoryBean.setShared(true);
return new EhCacheCacheManager(factoryBean.getObject());
}
}
I am trying to auto-wire a spring managed bean to use in my unit test case. But autowired bean is always null. Below are my setting.
my unit test class
#RunWith(MockitoJUnitRunner.class)
#ContextConfiguration(locations = "classpath*:business-context-test.xml")
public class SMSGatewayTest {
#Autowired
#Qualifier("mySMSImpl")
private ISMSGateway smsGateway;
#Test
public void testSendTextMessage() throws Exception {
smsGateway.sendText(new TextMessage("TEST"));
// ^___________ this is null, even though I have set ContextConfiguration
}
}
spring managed bean
package com.myproject.business;
#Component("mySMSImpl")
public class MySMSImpl implements ISMSGateway {
#Override
public Boolean sendText(TextMessage textMessage ) throws VtsException {
//do something
}
}
context for unit test case
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd">
<context:annotation-config/>
<context:component-scan base-package="com.myproject.business"/>
</beans>
I have seen many questions and all are giving the answers that I've already incorporated in my code. Can some one tell me what I am missing. I am using intellij 14 to run my test case.
You are having code like this :
#RunWith(MockitoJUnitRunner.class)
#ContextConfiguration(locations = "classpath*:business-context-test.xml")
public class SMSGatewayTest {
..
..
}
Do you really wannna use MockitoJUnitRunner as I am not seeing any more mocks in the class.
Please try running the JUnit with like
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = "classpath*:business-context-test.xml")
public class SMSGatewayTest {
..
..
}
Edit -
#RunWith(SpringJUnit4ClassRunner.class) makes all those dependencies available which are declared using #ContextConfiguration(..) to the class on which it is used.
For example, in your case you have #RunWith(SpringJUnit4ClassRunner.class) on the class - SMSGateway. So it makes available all those dependencies to SMSGateway which are configured using #ContextConfiguration(locations = "classpath*:business-context-test.xml")
This helps to autowire 'smsGateway' inside your class SMSGateway. As you was using #RunWith(MockitoJUnitRunner.class), this dependency was not available for autowiring and hence spring was complaining. You will need MockitoJUnitRunner.class if you are using Mockito in your application. As you are not mocking any of your classes, you don't need MockitoJUnitRunner as of now.
Please take a look at - Mockito, JUnit and Spring
for more clarifications.
Take a look at http://www.alexecollins.com/tutorial-junit-rule/ . This will help you to understand how '#Runwith' and '#ContextConfiguration' annotations work behind the scenes.
There is an extra whitespace in the Bean name
#Component("mySMSImpl ")
#Qualifier("mySMSImpl")
Why would you use the Qualifier annotation in this case? Are there multiple implementations of the ISMSGateway interface?
I am trying to write integration test with spring. Below is the test class
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations={"classpath*:**/context*.xml"})
public class MyFirstTestClass {
#AutoWired
private ApplicationContext applicationContext;
#Test
public void testApplicationContext {
applicationContext.getName();
}
}
When I run the test from maven I am getting NullPointerException because applicationContext is null. I am not able to understand the reason. Also in logs also i don't see any error. I tried putting configuration <context:annotation-config/> in one of the application context files. Still I am getting same error.
When i use classpath*:**/context*.xml to create context manually it works. But i believe when i use context configuraton its not loading the application context file.I am not getting any error as well.
Kindly suggest
Okay i managed to load the application context . but now the iam getting NoSuchBeanDefinition exception.
bean file
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="messageService"
class="com.mycompany.app.MessageService">
</bean>
</beans>
Test class
package com.mycompany.app;
import org.junit.runner.RunWith;
import static org.junit.Assert.*;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import junit.framework.TestSuite;
#ContextConfiguration(locations={"classpath*:*/AppTest.context.xml"})
#RunWith(SpringJUnit4ClassRunner.class)
public class AppTest
{
#Autowired
private ApplicationContext applicationContext;
public AppTest( )
{
}
#Test
public void testApp(){
assertNotNull(applicationContext);
for(String str : applicationContext.getBeanDefinitionNames()){
System.out.println("Bean feifniaio count"+str);
}
applicationContext.getBean("messageService");
}
}
Log after execution of test
INFO: Refreshing
org.springframework.context.support.GenericApplicationContext#196751b:
startup date [Fri Nov 20 00:00:11 IST 2015]; root of context hierarchy
Bean feifniaio
countorg.springframework.context.annotation.internalConfigurationAnnotationProcessor
Bean feifniaio
countorg.springframework.context.annotation.internalAutowiredAnnotationProcessor
Bean feifniaio
countorg.springframework.context.annotation.internalRequiredAnnotationProcessor
Bean feifniaio
countorg.springframework.context.annotation.internalCommonAnnotationProcessor
Bean feifniaio
countorg.springframework.context.event.internalEventListenerProcessor
Bean feifniaio
countorg.springframework.context.event.internalEventListenerFactory
Bean feifniaio
countorg.springframework.context.annotation.ConfigurationClassPostProcessor.importAwareProcessor
Bean feifniaio
countorg.springframework.context.annotation.ConfigurationClassPostProcessor.enhancedConfigurationProcessor
I am not able to see message service bean. What could be the issue??
Finally i identified the issue. There was issue with my context file. I placed an empty context file in the resources folder of test which was getting copied in the target folders by maven. And the actual context file was not getting copied in target.Because of empty context file the context was getting initialized but since it did not had bean definition it was throwing NoSuchBeanException.Now i should highlight that ContextConfiguration was not throwing any error when it was not able to find the context file specified in the location attribute. Which i thought it ideally should.I tried and put some dummy file paths which does not exists in the location attributes and did not threw error.
I'm #Autowireing a org.springframework.core.io.ResourceLoader into one of my #Service classes.
During the tests, I'd like to get access to an instance of a ResourceLoader, so that it can be injected into the service being tested. What's the best way to get a fully functional instance of ResourceLoader during your tests?
If that's not possible, is there an alternative to ResourceLoader? Essentially, I need to have my service read some static files from the project.
Update:
Started using #RunWith(SpringJUnit4ClassRunner.class) + #ContextConfiguration on my test; however, the ResourceLoader that is now being injected via #Autowire into my service behaves differently than usual (ie. when it's not in the test context). In the test, ResourceLoader#getResource returns a Resource pointing to bad relative path, rather than the proper absolute path which appears during a regular execution.
Some more information discovered while debugging:
During tests, the #Autowired resourceLoader is an instanceof org.springframework.context.support.GenericApplicationContext
During regular execution, resourceLoader is an instance of org.springframework.web.context.support.XmlWebApplicationContext
You can use a DefaultResourceLoader. It handles URL and classpath resources which should be enough for simple tests.
DefaultResourceLoader doesn't need any special setup. Just create a new instance and pass it into your test.
What kind of test do you want to write?
If it's a unit test, you should probably mock ResourceLoader and inject that mock into the service instance. (Use mockito for example)
If it's an integration test, you would be better off using the Spring TestContext framework. Create a spring context that contains all components needed for the test, then annotate your test class with #RunWith(SpringJUnit4ClassRunner.class) +
#ContextConfiguration, which will make it possible to autowire fully configured beans (e.g. the service instance to be tested) in the test class.
I suppose you have your service defined as something like this:
public class ResourceService {
#Autowired
ResourceLoader loader;
}
Now, when you write your test for ResourceService :
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration({ "classpath:test-config.xml" })
public class ResourceServiceTest {
#Autowired
ResourceService resourceService;
#Test
public void test() {
...
}
}
The Spring TestContext Framework configures instances of your test classes via Dependency Injection. So, when you autowire ResourceService in your test class, Spring will inject the autowired ResourceLoader property into ResourceService.
ENVIRONMENT:
Java: 17
Springboot: 2.6.2
Junit-jupiter:5.8.2
Generally integration tests will need to start up a container to execute the test cases. This can be achieved using the following code:
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.core.io.ResourceLoader;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringRunner;
import static org.junit.jupiter.api.Assertions.assertTrue;
#RunWith(SpringRunner.class)
#SpringBootTest
#TestPropertySource(locations = "classpath:application-integration-test.properties")
class TemplateKeyValidatorIT {
#Autowired
ResourceLoader resourceLoader;
#Autowired
private ResourceLoaderService resourceLoaderService;
#Test
void testResourceLoading() {
// Given
String fileName = "myTestFile.txt";
// When
File file = resourceLoaderService.load(fileName);
// Then
assertTrue(file.exists());
}
}
#RunWith(SpringRunner.class): SpringRunner is the base Spring framework Runner. It extends SpringJUnit4ClassRunner, but it’s just an alias to that class.
#SpringBootTest: Annotation is needed to bootstrap the entire container; it creates an ApplicationContext that will be used by the integration tests.
#TestPropertySource(locations = "classpath:application-integration-test.properties"): Loads integration specific application properties.
Note: this will boot strap the real application context but you can separate test configuration class using:
#TestConfiguration
public class MyTestContextConfiguration {
#Bean
public ResourceLoaderService resourceLoaderService() {
return new ResourceLoaderService() {
// implement methods
};
}
}
And this can then be impoerted by your test class using:
#Import(MyTestContextConfiguration.class)
I'm trying to test out Spring Annotations to see how they work with some simple examples derived from the Spring 3.0 Source (in this case the "#Required" annotation specifically).
To start, I came up with a basic "Hello World" type example that doesn't use any annotations. This works as expected (i.e. prints "Hello Spring 3.0~!").
I then added a DAO object field to the Spring3HelloWorld class. My intention was to deliberately cause an exception to occur by annotating the setter for the DAO with #Required but then not setting it. However, I get a null pointer exception (since this.dao is null) when I was expecting an exception based on not following the annotation "rules/requirements".
I thought I would have needed to set the DAO object before calling any method from Spring3HelloWorld, but apparently that's not the case. I assume I'm misunderstanding how #Required works.
So basically how would I get the following to give me an error along the lines of "Hey you can't do that, you forgot to set DAO blah blah blah".
Spring3HelloWorldTest.java:
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;
public class Spring3HelloWorldTest {
public static void main(String[] args) {
XmlBeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource ("SpringHelloWorld.xml"));
Spring3HelloWorld myBean = (Spring3HelloWorld) beanFactory.getBean("spring3HelloWorldBean");
myBean.sayHello();
}
}
Spring3HelloWorld.java:
import org.springframework.beans.factory.annotation.Required;
public class Spring3HelloWorld {
private DAO dao;
#Required
public void setDAO( DAO dao ){
this.dao = dao;
}
public void sayHello(){
System.out.println( "Hello Spring 3.0~!" );
//public field just for testing
this.dao.word = "BANANA!!!";
}
}
SpringHelloWorld.xml:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<context:annotation-config/>
<bean class="org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor"/>
<bean id="dao" class="src.DAO" ></bean>
<bean id="spring3HelloWorldBean" class="src.Spring3HelloWorld" ></bean>
</beans>
My first guess is you won't get any of the advanced behaviour with Spring and annotations because you are using an XmlBeanFactory instead of the recommended ApplicationContext.
-- edit --
Yup - see this Stack Overflow question/answer.