I have a prototype Bean which is instantiated by singleton bean with a Provider:
#Component
#Scope("prototype")
class MyPrototype {}
#Component
class MySingleton {
#Autowired
javax.inject.Provider<MyPrototype> prototypeFactory;
}
This works fine, but our company rules state that #Autowired is not allowed; the common pattern is #Resource(SingletonBeanClass.BEAN_ID).
Is it possible to annotate the Provider this way so the Spring lookup can create it?
I'm aware I can add a factory method with #Lookup, or a singleton factory bean, but I prefer the Provider.
EDIT:
I didn't get it to work this way and in the end had to edit spring.xml; see below for details.
As you have an XML configuration file, you can configure it via XML in the following way:
<bean id="myPrototype" class="some.package.MyPrototype" scope="prototype" />
<bean id="mySingleton" class="some.package.MySingleton">
<lookup-method name="getPrototypeFactory" bean="myPrototype "/>
</bean>
In this way, you have to access to the myPrototype with the getPrototypeFactory() and not directly to the property. You can even remove the annotations on those 2 classes.
For any extra details, you can look at the following blog post Injecting a prototype bean into a singleton bean
For reference, if someone comes across this via Google:
I ended up needing to declare it in the spring.xml. I tried #Lookup, but even that didn't work due to the prototype-bean referencing yet another prototype-bean.
This is how it was recommended here,
but it does not work:
#Component("proto1")
#Scope("prototype")
class MyPrototypeBean1 {
#Lookup(value="proto2")
protected MyPrototypeBean2 createBean2() { return null; }
}
#Component("proto2")
#Scope("prototype")
class MyPrototypeBean2 {
}
#Component("singleton")
class MySingleton {
#Lookup(value="proto1")
protected MyPrototypeBean1 createBean1() { return null; }
}
This results in the error message "Cannot apply #Lookup to beans without corresponding bean definition" when trying to create "innerBean...".
I assume it is due to "lookup methods cannot get replaced on beans returned from factory methods where we can't dynamically provide a subclass for them" as is quoted in the link above.
So what I ended up doing in the spring.xml:
<bean name="proto2" class="my.package.PrototypeBean2" />
<bean name="proto1" class="my.package.PrototypeBean1" >
<lookup-method name="createBean2" bean="proto2" />
</bean>
<bean name="singleton" class="my.package.SingletonBean" >
<lookup-method name="createBean1" bean="proto1" />
</bean>
And this works.
For the unit tests, I had to subclass the respective classes:
class SingletonUnitTest {
#Mock
MyPrototypeBean1 bean1;
#InjectMocks
DummySingleton sut;
#Before public void setBean1() {
sut.bean = bean1;
}
static class DummySingletonBean extends MySingeton {
MyPrototypeBean1 bean;
protected MyPrototypeBean1 createBean1() {
return bean;
}
}
}
I'm trying to write a unit test for a Controller in a Spring Boot application. The application runs smoothly, my problem is with running its tests.
Here is the test code:
#RunWith(SpringRunner.class)
#WebMvcTest(MyController.class)
#AutoConfigureTestEntityManager
public class MyControllerTest {
#Autowired
private MockMvc mockMvc;
#Mock
private MyRepository myRepository;
#Mock
ZendeskNotifier zendeskNotifier;
#Mock
ActivityLogger activityLogger;
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
}
#Test
public void cannotSendFooWithoutMessageBody() throws Exception {
this.mockMvc.perform(post("/api/v1/foo/1/send"))
.andDo(print())
.andExpect(status().is4xxClientError())
.andExpect(content().string(containsString("The message body cannot be empty.")));
}
}
When I try running it I get:
***************************
APPLICATION FAILED TO START
***************************
Description:
Field jobEventRepository in foo.bar.util.memsource.svc.MemsourceEventProcessor required a bean named 'entityManagerFactory' that could not be found.
Action:
Consider defining a bean named 'entityManagerFactory' in your configuration.
And that feels weird to me, since I'm providing the AutoConfigureTestEntityManager annotation and would expect all the EntityManager-related stuff to be in place.
If Google brought you here and you're using Spring Boot, you may need to add #DataJpaTest to your test class. It's found in org.springframework.boot:spring-boot-test-autoconfigure. You may also discover upon rerunning that you need to declare a dependency on org.hibernate:hibernate-validator.
Inject Spring's TestEntityManager in your mockito test class.
#Autowired
private TestEntityManager entityManager;
You are already using #AutoConfigureTestEntityManager on the test class to auto configure this test entity manager. So you don't have to do anything else in the config file.
Spring Boot is loading your application's configuration, causing your data layer to be initialized.
Excerpt from Spring Boot's docs, Detecting Test Configuration:
Spring Boot’s #*Test annotations search for your primary configuration
automatically whenever you do not explicitly define one.
The search algorithm works up from the package that contains the test
until it finds a class annotated with #SpringBootApplication or
#SpringBootConfiguration. As long as you structured your code in a
sensible way, your main configuration is usually found.
When the scanning hits your main class it is likely finding an annotation like #EnableJpaRepositories, which initializes your data layer and thus requires the entity manager factory. (You may see other side effects too, like Hibernate initializing your in-memory database if your application uses one.)
As other answers suggest, you could initialize the data layer, or try to wire/mock the missing beans. But since you aren't testing the data layer here it would be better to control the test's configuration.
The docs suggest some solutions:
Move #EnableJpaRepositories from the main class to a config class in child package. It will get scanned by the application (from the top package down) but not by the unit tests. This is discussed in the section User Configuration and Slicing.
Add a nested #Configuration class to override the configuration used by the unit test.
The first one seems like a good rule to follow in general. Other annotations like #EnableBatchProcessing and #EnableScheduling can be treated the same way, which will speed up your unit tests.
you need to configure entityManagerFactory, you can refer bellow code
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="packagesToScan" value="org.demo.test.entity" />
<property name="dataSource" ref="dataSource" />
<property name="jpaProperties">
<props>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.hbm2ddl.auto">create</prop>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
</props>
</property>
<property name="persistenceProvider">
<bean class="org.hibernate.jpa.HibernatePersistenceProvider">
</bean>
</property>
</bean>
Move #EnableJpaRepositories from the main class to a config class in a child package. It will get scanned by the application (from the top package down) but not by the unit tests.
#Configuration
#EnableJpaRepositories(
value = "com.company.repository"
)
public class JpaConfig {
}
I found out that there is an Annotation you can use to add JPA-Support to your WebMVCTest (#AutoConfigureDataJpa)
#ContextConfiguration(classes = { SurchargesTestConfiguration.class })
#WebMvcTest(SurchargesApiController.class)
#AutoConfigureDataJpa
#ActiveProfiles("local")
class SurchargesApiControllerTest {
#Autowired
private MockMvc mvc;
#Test
void testNotFound() throws Exception {
mvc.perform(get("/surcharges/marketnumber") //
.headers(getDefaultHeaders())) //
.andExpect(status().isNotFound());
}
}
I'm attempting to create unit tests for a rest service in my spring servlet. But when the controller object is created by #autowire, then all of its #autowired fields are null.
My test class is as follows, using the SpringJUnit runner and context configuration set
#ContextConfiguration(locations = "ExampleRestControllerTest-context.xml")
#RunWith(SpringJUnit4ClassRunner.class)
public class ExampleRestControllerTest {
#Autowired
private BaseService mockExampleService;
#Autowired
private ExampleRestController testExampleRestController;
The ExampleRestControllerTest-context.xml sets up the service to be mocked and injects the mocked object into the controller
<context:annotation-config/>
<import resource="classpath*:example-servlet.xml"/>
<bean id="mockExampleService" class="org.easymock.EasyMock" factory-method="createMock">
<constructor-arg index="0" value="za.co.myexample.example.services.BaseService"/>
</bean>
<bean id="testExampleRestController" class="za.co.myexample.example.rest.controller.ExampleRestController">
<property name="exampleService" ref="mockExampleService"/>
</bean>
The rest of the beans used by the controler are defined in the example-servlet.xml
<bean id="RESTCodeMapper" class="za.co.myexample.example.rest.util.RESTCodeMapper"/>
<bean id="restProperties" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="location" value="classpath:RESTServiceCodeMapping.properties"/>
</bean>
Along with my Jax2BMarshaller.
My IDE links to these definitions just fine and if I remove any of the definitions I get an "No qualifying bean" error as expected.
My problem is that when I run my unit tests the controller that is provided has all of its fields as nulls
#Controller
public abstract class BaseRestController {
private static Logger LOGGER = Logger.getLogger(BaseRestController.class);
protected final String HEADERS = "Content-Type=application/json,application/xml";
#Autowired
protected RESTCodeMapper restCodeMapper;
#Autowired
protected BaseService exampleService;
#Autowired
protected Jaxb2Marshaller jaxb2Marshaller;
(and its implementing class)
#Controller
#RequestMapping("/example")
public class ExampleRestController extends BaseRestController {
When I run the proper code in my Weblogic server the fields get properly populated. More so if I add these fields in my test class directly those fields also get #autowired correctly. I could thus #autowire those objects in my test class and then set them directly into my controller. But that's not the right way of doing it.
So the question is, why are the autowired fields of an autowired object null in my test class when it autowires those objects directly in the same test class or if I run the code normally on my Weblogic server.
In your test class, the #Autowired objects are null because of context configuration, so change it to:
#ContextConfiguration(locations = "ExampleRestControllerTest-context.xml")
to
#ContextConfiguration(locations = "classpath:/ExampleRestControllerTest-context.xml")
in ExampleRestControllerTest
So the question is, why are the autowired fields of an autowired
object null in my test class when it autowires those objects directly
in the same test class or if I run the code normally on my Weblogic
server.
They must be null in autowired objects' constructor. You can try to create #PostConstruct method in autowired object and in this method the autowired objects must be not null
I used #Autowired hundreds of time but today I don't get it, neither #Autowired nor #Inject work in a new project I just created, I get the famous error
Invalid property 'jdbcTemplate' of bean class [com.xxx.SomeDAO]: Bean
property 'jdbcTemplate' is not writable or has an invalid setter
method. Does the parameter type of the setter match the return type of the getter?
When I add a setter for jdbcTemplate in SomeDAO, it works...
applicationContext.xml:
...
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="com.xxx.SomeDAO" class="com.xxx.SomeDAO">
<property name="jdbcTemplate" ref="jdbcTemplate"/>
</bean>
...
SomeDAO.java:
import org.springframework.jdbc.core.JdbcTemplate;
import javax.inject.Inject;
//import org.springframework.beans.factory.annotation.Autowired;
public class SomeDAO {
#Inject // Doesn't work
//#Autowired // Doesn't work either
private JdbcTemplate jdbcTemplate;
...
/* Works if I add this setter
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}*/
}
What could prevent the injection through the annotation? Thanks!
It is not possible to use field-injection when the bean is created with the XML-context the way you have provided it. Your class is not part of any component-scanning and therefore the annotations does not kick in. Therefore, I can see at least the following options:
1. Use a setter as per your example and remove the #Inject
This is the simplest approach since you got the code and XML prepared. However, this means that your DAO must expose an unnecessary setter-method.
2. Use a constructor that sets your jdbcTemplate field
This is according to me a better alternative but it means that you need to rewrite the XML like this:
<bean id="com.xxx.SomeDAO" class="com.xxx.SomeDAO">
<constructor-arg ref="jdbcTemplate"/>
</bean>
And, your constructor like this:
public class SomeDAO {
private final JdbcTemplate jdbcTemplate;
public SomeDAO(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
}
3. Add component-scanning and use #Autowired (or #Inject)
I think that this is the best approach if you fully want to utilize the beauty of Spring's dependency injection capabilities. Add the following to your XML-context:
<context:component-scan base-package="com.xxx"/>
And then the code you previously provided should work. However, you should probably consider avoiding field-injection in favour of constructor-injection. The opinions differ on this matter but I find the code harder to test when field-injection is used.
Constructor injection looks like this:
// The component scanner will find this annotation and create
// the bean (and inject the dependencies)
#Component
public class SomeDAO {
private final JdbcTemplate jdbcTemplate;
#Autowired // enables constructor-injection
public SomeDAO(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
}
I personally prefer alternative #3 since it is clean, Java-based (practically no XML required) which also means that it is refactoring friendly, there is no field magic happening and the constructor-based approach makes the code testable and does not expose any unnecessary setter-methods.
A good starting point to learn more about this is the excellent Spring Documentation. There you can find a great explanation for all of the above!
I would like to make use of request scoped beans in my app. I use JUnit4 for testing. If I try to create one in a test like this:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "classpath:spring/TestScopedBeans-context.xml" })
public class TestScopedBeans {
protected final static Logger logger = Logger
.getLogger(TestScopedBeans.class);
#Resource
private Object tObj;
#Test
public void testBean() {
logger.debug(tObj);
}
#Test
public void testBean2() {
logger.debug(tObj);
}
With the following bean definition:
<?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 class="java.lang.Object" id="tObj" scope="request" />
</beans>
And I get:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'gov.nasa.arc.cx.sor.query.TestScopedBeans': Injection of resource fields failed; nested exception is java.lang.IllegalStateException: No Scope registered for scope 'request'
<...SNIP...>
Caused by: java.lang.IllegalStateException: No Scope registered for scope 'request'
So I found this blog that seemed helpful:
http://www.javathinking.com/2009/06/no-scope-registered-for-scope-request_5.html
But I noticed he uses AbstractDependencyInjectionSpringContextTests which seems to be deprecated in Spring 3.0.
I use Spring 2.5 at this time, but thought it shouldn't be too hard to switch this method to use AbstractJUnit4SpringContextTests
as the docs suggest (ok the docs link to the 3.8 version but I'm using 4.4). So I change the
test to extend AbstractJUnit4SpringContextTests... same message. Same problem. And now the prepareTestInstance() method I want
to override is not defined. OK, maybe I'll put those registerScope calls somewhere else... So I read more about TestExecutionListeners and think that would be better since I don't want to have to inherit the spring package structure. So
I changed my Test to:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "classpath:spring/TestScopedBeans-context.xml" })
#TestExecutionListeners({})
public class TestScopedBeans {
expecting I would have to create a custom listener but I when I ran it. It works! Great, but why? I don't see where any of the stock listeners
are registering request scope or session scope, and why would they? there's nothing to say I want that yet, this might not be a Test for Spring MVC code...
Solution for Spring 3.2 or newer
Spring starting with version 3.2 provides support for session/request scoped beans for integration testing.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = TestConfig.class)
#WebAppConfiguration
public class SampleTest {
#Autowired WebApplicationContext wac;
#Autowired MockHttpServletRequest request;
#Autowired MockHttpSession session;
#Autowired MySessionBean mySessionBean;
#Autowired MyRequestBean myRequestBean;
#Test
public void requestScope() throws Exception {
assertThat(myRequestBean)
.isSameAs(request.getAttribute("myRequestBean"));
assertThat(myRequestBean)
.isSameAs(wac.getBean("myRequestBean", MyRequestBean.class));
}
#Test
public void sessionScope() throws Exception {
assertThat(mySessionBean)
.isSameAs(session.getAttribute("mySessionBean"));
assertThat(mySessionBean)
.isSameAs(wac.getBean("mySessionBean", MySessionBean.class));
}
}
Read more: Request and Session Scoped Beans
Solution for Spring before 3.2 with listener
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = TestConfig.class)
#TestExecutionListeners({WebContextTestExecutionListener.class,
DependencyInjectionTestExecutionListener.class,
DirtiesContextTestExecutionListener.class})
public class SampleTest {
...
}
WebContextTestExecutionListener.java
public class WebContextTestExecutionListener extends AbstractTestExecutionListener {
#Override
public void prepareTestInstance(TestContext testContext) {
if (testContext.getApplicationContext() instanceof GenericApplicationContext) {
GenericApplicationContext context = (GenericApplicationContext) testContext.getApplicationContext();
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
beanFactory.registerScope(WebApplicationContext.SCOPE_REQUEST,
new SimpleThreadScope());
beanFactory.registerScope(WebApplicationContext.SCOPE_SESSION,
new SimpleThreadScope());
}
}
}
Solution for Spring before 3.2 with custom scopes
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = TestConfig.class, locations = "test-config.xml")
public class SampleTest {
...
}
TestConfig.java
#Configuration
#ComponentScan(...)
public class TestConfig {
#Bean
public CustomScopeConfigurer customScopeConfigurer(){
CustomScopeConfigurer scopeConfigurer = new CustomScopeConfigurer();
HashMap<String, Object> scopes = new HashMap<String, Object>();
scopes.put(WebApplicationContext.SCOPE_REQUEST,
new SimpleThreadScope());
scopes.put(WebApplicationContext.SCOPE_SESSION,
new SimpleThreadScope());
scopeConfigurer.setScopes(scopes);
return scopeConfigurer
}
or with xml configuration
test-config.xml
<bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
<property name="scopes">
<map>
<entry key="request">
<bean class="org.springframework.context.support.SimpleThreadScope"/>
</entry>
</map>
<map>
<entry key="session">
<bean class="org.springframework.context.support.SimpleThreadScope"/>
</entry>
</map>
</property>
</bean>
Source code
Source code for all presented solutions:
https://github.com/mariuszs/spring-test-web
I've tried several solutions, including #Marius's solution with the "WebContextTestExecutionListener", but it didn't work for me, as this code loaded the application context before creating the request scope.
The answer that helped me in the end is not a new one, but it's good:
http://tarunsapra.wordpress.com/2011/06/28/junit-spring-session-and-request-scope-beans/
I simply added the following snippet to my (test) application context:
<bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
<property name="scopes">
<map>
<entry key="request">
<bean class="org.springframework.context.support.SimpleThreadScope"/>
</entry>
</map>
</property>
</bean>
Good luck!
A solution, tested with Spring 4, for when you require request-scoped beans but aren't making any requests via MockMVC, etc.
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(/* ... */)
public class Tests {
#Autowired
private GenericApplicationContext context;
#Before
public void defineRequestScope() {
context.getBeanFactory().registerScope(
WebApplicationContext.SCOPE_REQUEST, new RequestScope());
RequestContextHolder.setRequestAttributes(
new ServletRequestAttributes(new MockHttpServletRequest()));
}
// ...
The test passes because it isn't doing anything :)
When you omit the #TestExecutionListeners annotation, Spring registers 3 default listeners, including one called DependencyInjectionTestExecutionListener. This is the listener responsible for scanning your test class looking for things to inject, including #Resource annotations. This listener tried to inject tObj, and fails, because of the undefined scope.
When you declare #TestExecutionListeners({}), you suppress the registration of the DependencyInjectionTestExecutionListener, and so the test never gets tObj injected at all, and because your test is not checking for the existence of tObj, it passes.
Modify your test so that it does this, and it will fail:
#Test
public void testBean() {
assertNotNull("tObj is null", tObj);
}
So with your empty #TestExecutionListeners, the test passes because nothing happens.
Now, on to your original problem. If you want to try registering the request scope with your test context, then have a look at the source code for WebApplicationContextUtils.registerWebApplicationScopes(), you'll find the line:
beanFactory.registerScope(WebApplicationContext.SCOPE_REQUEST, new RequestScope());
You could try that, and see how you go, but there might be odd side-effects, because you're not really meant to do this in a test.
Instead, I would recommend rephrasing your test so that you don't need request scoped beans. This shouldn't be difficult, the lifecycle of the #Test shouldn't be any longer than the lifecycle of a request-scoped bean, if you write self-contained tests. Remember, there's no need to test the scoping mechanism, it's part of Spring and you can assume it works.
This is still an open issue:
https://jira.springsource.org/browse/SPR-4588
I was able to get this to work (mostly) by defining a custom context loader as outlined in
http://forum.springsource.org/showthread.php?p=286280
Test Request-Scoped Beans with Spring explains very well how to register and create a custom scope with Spring.
In a nutshell, as Ido Cohn explained, it's enough to add the following to the text context configuration:
<bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
<property name="scopes">
<map>
<entry key="request">
<bean class="org.springframework.context.support.SimpleThreadScope"/>
</entry>
</map>
</property>
</bean>
Instead of using the predefined SimpleThreadScope, based on ThreadLocal, it's also easy to implement a Custom one, as explained in the article.
import java.util.HashMap;
import java.util.Map;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.config.Scope;
public class CustomScope implements Scope {
private final Map<String , Object> beanMap = new HashMap<String , Object>();
public Object get(String name, ObjectFactory<?> factory) {
Object bean = beanMap.get(name);
if (null == bean) {
bean = factory.getObject();
beanMap.put(name, bean);
}
return bean;
}
public String getConversationId() {
// not needed
return null;
}
public void registerDestructionCallback(String arg0, Runnable arg1) {
// not needed
}
public Object remove(String obj) {
return beanMap.remove(obj);
}
public Object resolveContextualObject(String arg0) {
// not needed
return null;
}
}
MariuszS' solution works, except I couldn't get the transaction committed properly.
It seems the newly released 3.2 has finally made testing request/session scoped beans first class citizens. Here's a couple of blogs for more details.
Rossen Stoyanchev's Spring Framework 3.2 RC1: Spring MVC Test Framework
Sam Brannen's Spring Framework 3.2 RC1: New Testing Features
NOT reading the docs sometimes drives one crazy. Almost.
If you are using shorter-lived beans (request scope for example), you most likely also need to change your lazy init default! Otherwise the WebAppContext will fail to load and tell you something about missing request scope, which is of course missing, because the context is still loading!
http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/beans.html#beans-factory-lazy-init
The Spring guys should definitely put that hint into their exception message...
If you don't want to change the default, there is also the annotation way: put "#Lazy(true)" after #Component etc. to make singletons initialize lazy and avoid instantiating request-scoped beans too early.