SpringBeanAutowiringSupport does not inject beans in jUnit tests - java

I use SpringBeanAutowiringSupport for bean injection in some objects. Problem is, that injection of beans does not work in jUnit tests. For testing is used SpringJUnit4ClassRunner.
public class DossierReportItemXlsImporterImpl implements DossierRerportItemXlsImporer {
private final Logger logger = Logger.getLogger(getClass());
// are not autowired.
#Autowired
private DossierReportService dossierReportService;
#Autowired
private DossierReportItemService dossierReportItemService;
#Autowired
private NandoCodeService nandoCodeService;
public DossierReportItemXlsImporterImpl(){
SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
}
//...
}
public class DossierRerportItemXlsImporerTest extends AuditorServiceTest{
// injected OK
#Autowired
private DossierReportService dossierReportService;
#Autowired
private DossierReportItemService dossierReportItemService;
#Test
public void testXlsImport(){
DossierRerportItemXlsImporer importer = new DossierReportItemXlsImporterImpl();
importer.processImport(createDossierReport(), loadFile());
// ...
}
// ...
}
Does anyone have any idea, why injection using SpringBeanAutowiringSupport does not work in jUnit tests?

well spring + junit team have already fixed this . look this link -- >
spring unit testing
otherwise you can call the spring context and use the getBean method , but in that way you can even do it with a simple main test inside your class instead of junit test
**note if you use the spring + junit config you have to put the test-spring-context.xml into the test package

Thanks to M. Denium's, his solution workds.
public class DossierReportItemXlsImporterImpl implements DossierRerportItemXlsImporer {
private final Logger logger = Logger.getLogger(getClass());
#Autowired
private DossierReportService dossierReportService;
#Autowired
private DossierReportItemService dossierReportItemService;
#Autowired
private NandoCodeService nandoCodeService;
public DossierReportItemXlsImporterImpl(final ApplicationContext contex){
contex.getAutowireCapableBeanFactory().autowireBean(this);
}
//...
}
public class DossierRerportItemXlsImporerTest extends AuditorServiceTest{
#Autowired
private ApplicationContext context;
#Autowired
private DossierReportService dossierReportService;
#Autowired
private DossierReportItemService dossierReportItemService;
#Test
public void testXlsImport(){
DossierRerportItemXlsImporer importer = new DossierReportItemXlsImporterImpl(context);
importer.processImport(createDossierReport(), loadFile());
// ...
}
// ...
}

I made my own version that supports passing in an ApplicationContext not just limited to WebApplicationContext. This will allow it to work in both test and normal context.
/**
* This is an implementation of {#link org.springframework.web.context.support.SpringBeanAutowiringSupport} that
* has a fallback that can be used in unit tests.
*/
public final class SpringBeanAutowiringSupport {
private static final ThreadLocal<ApplicationContext> applicationContextThreadLocal = new ThreadLocal<>();
private SpringBeanAutowiringSupport() {}
public static void setApplicationContext(final ApplicationContext applicationContext) {
applicationContextThreadLocal.set(applicationContext);
}
public static void processInjectionBasedOnCurrentContext(Object target) {
var cc = ContextLoader.getCurrentWebApplicationContext();
if (cc != null) {
org.springframework.web.context.support.SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(target);
} else if (applicationContextThreadLocal.get() != null) {
AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
bpp.setBeanFactory(applicationContextThreadLocal.get().getAutowireCapableBeanFactory());
bpp.processInjection(target);
}
}
public static void unload() {
applicationContextThreadLocal.remove();
}
}
To make it easier to use on tests, I add a TestExecutionListener
public class SpringBeanAutowiringSupportTestExecutionListener extends AbstractTestExecutionListener {
#Override
public void afterTestMethod(final TestContext testContext) {
SpringBeanAutowiringSupport.unload();
}
#Override
public void beforeTestMethod(final TestContext testContext) {
SpringBeanAutowiringSupport.setApplicationContext(testContext.getApplicationContext());
}
}
Then use it in my tests with
#RunWith(SpringRunner.class)
#TestExecutionListeners(listeners = {SpringBeanAutowiringSupportTestExecutionListener.class}, mergeMode = MERGE_WITH_DEFAULTS)
public class MyTest {
...
}

Related

Static method to return a singleton spring bean

Trying to build a task executor app in spring boot. The idea is to design a template to retrieve the default TaskConfig so that executor can just execute it.
#Component
public class TaskExecutor {
private final TaskTemplate taskTemplate;
#Autowired
public TaskExecutor(TaskTemplate taskTemplate) {
this.taskTemplate=taskTemplate;
}
public void runTask() {
final TaskConfiguration taskConfig = taskTemplate.getTaskConfig("taskName");
taskConfig.do();
}
}
#Component
public class TaskTemplate {
private final TaskParam1 taskParam1;
private final TaskParam2 taskParam2;
#Autowired
public TaskTemplate(TaskParam1 taskParam1, TaskParam2 taskParam2) {
this.taskParam1 = taskParam1;
this.taskParam2 = taskParam2;
}
public TaskConfiguration getTaskConfig() {
// Logic to build the task configuration from task template params
}
}
The problem I see is that the TaskTemplate is coupled with the TaskExecutor (Autowired), which I wish to remove.
I wanted to replace it with a static convenient method to return the singleton Template so that I could execute the getTaskConfig with it.
Looking for suggestion to improve upon this.
Thanks
You can inject ApplicationContext to another bean like below code. After spring initialized you can use BeanGetter.getTaskTemplate() to get TaskTemplate singleton bean.
...
final TaskConfiguration taskConfig = BeanGetter.getTaskTemplate().getTaskConfig("taskName");
...
#Service
public class BeanGetter {
private static ApplicationContext applicationContext;
#Autowired
public BeanGetter(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
public static TaskTemplate getTaskTemplate(){
return applicationContext.getBean(TaskTemplate.class);
}
}

Can I inject mocks into a prototype bean with Autowired constructor?

Is it possible to inject a mock service into a prototype bean using the #Autowired constructor? I realize I could switch to setter injection but I would prefer to use the constructor if possible.
#Component
#Scope(value = "prototype")
public class Prototype {
private DependantService dependantService;
#Autowired
public Prototype(DependantService dependantService) {
this.dependantService = dependantService;
}
}
#SpringBootTest
public class TestPrototype {
#Autowired
private ApplicationContext ctx;
#Mock
private DependantService dependantService;
#Test
public void testPrototype() {
// How can I inject the mock service?
ctx.getBean(Prototype.class);
}
}
Turns out there is an overloaded version of the getBean method that accepts arguments. I would downvote my on question if I could.
#SpringBootTest
public class TestPrototype {
#Autowired
private ApplicationContext ctx;
#Mock
private DependantService dependantService;
#Test
public void testPrototype() {
Prototype p = ctx.getBean(Prototype.class, dependantService);
// Test p
}
}
If you want to speed up your unit tests, [and do true isolated unit testing,] I suggest taking a look at the #InjectMocks mockito annotation. #SpringBootTest fires up the Spring container which is pretty cumbersome.
#Controller
public class MyController {
#Inject
private Logger log;
public methodThatNeedsTesting(){
log.info("hey this was called");
}
}
#TestInstance(Lifecycle.PER_CLASS)
#ExtendWith({ MockitoExtension.class })
class MyControllerTest {
#Mock
private Logger log;
#InjectMocks
private MyController myController;
#Test
void test_methodThatNeedsTesting() throws Exception {
myController.methodThatNeedsTesting();
// myController will not throw an NPE above because the log field has been injected with a mock
}

Autowired NullPointerException [duplicate]

Is there some way to use #Autowired with static fields. If not, are there some other ways to do this?
In short, no. You cannot autowire or manually wire static fields in Spring. You'll have to write your own logic to do this.
#Component("NewClass")
public class NewClass{
private static SomeThing someThing;
#Autowired
public void setSomeThing(SomeThing someThing){
NewClass.someThing = someThing;
}
}
#Autowired can be used with setters so you could have a setter modifying an static field.
Just one final suggestion... DON'T
Init your autowired component in #PostConstruct method
#Component
public class TestClass {
private static AutowiredTypeComponent component;
#Autowired
private AutowiredTypeComponent autowiredComponent;
#PostConstruct
private void init() {
component = this.autowiredComponent;
}
public static void testMethod() {
component.callTestMethod();
}
}
Create a bean which you can autowire which will initialize the static variable as a side effect.
Wanted to add to answers that auto wiring static field (or constant) will be ignored, but also won't create any error:
#Autowired
private static String staticField = "staticValue";
You can achieve this using XML notation and the MethodInvokingFactoryBean. For an example look here.
private static StaticBean staticBean;
public void setStaticBean(StaticBean staticBean) {
StaticBean.staticBean = staticBean;
}
You should aim to use spring injection where possible as this is the recommended approach but this is not always possible as I'm sure you can imagine as not everything can be pulled from the spring container or you maybe dealing with legacy systems.
Note testing can also be more difficult with this approach.
You can use ApplicationContextAware
#Component
public class AppContext implements ApplicationContextAware{
public static ApplicationContext applicationContext;
public AppBeans(){
}
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
then
static ABean bean = AppContext.applicationContext.getBean("aBean",ABean.class);
Disclaimer This is by no means standard and there could very well be a better spring way of doing this. None of the above answers address the issues of wiring a public static field.
I wanted to accomplish three things.
Use spring to "Autowire" (Im using #Value)
Expose a public static value
Prevent modification
My object looks like this
private static String BRANCH = "testBranch";
#Value("${content.client.branch}")
public void finalSetBranch(String branch) {
BRANCH = branch;
}
public static String BRANCH() {
return BRANCH;
}
We have checked off 1 & 2 already now how do we prevent calls to the setter, since we cannot hide it.
#Component
#Aspect
public class FinalAutowiredHelper {
#Before("finalMethods()")
public void beforeFinal(JoinPoint joinPoint) {
throw new FinalAutowiredHelper().new ModifySudoFinalError("");
}
#Pointcut("execution(* com.free.content.client..*.finalSetBranch(..))")
public void finalMethods() {}
public class ModifySudoFinalError extends Error {
private String msg;
public ModifySudoFinalError(String msg) {
this.msg = msg;
}
#Override
public String getMessage() {
return "Attempted modification of a final property: " + msg;
}
}
This aspect will wrap all methods beginning with final and throw an error if they are called.
I dont think this is particularly useful, but if you are ocd and like to keep you peas and carrots separated this is one way to do it safely.
Important Spring does not call your aspects when it calls a function. Made this easier, to bad I worked out the logic before figuring that out.
Generally, setting static field by object instance is a bad practice.
to avoid optional issues you can add synchronized definition, and set it only if private static Logger logger;
#Autowired
public synchronized void setLogger(Logger logger)
{
if (MyClass.logger == null)
{
MyClass.logger = logger;
}
}
:
Solution 1 : Using Constructor #Autowired For Static Field
#Component
public class MyClass {
private static MyService service;
#Autowired
public MyClass(MyService service) {
TestClass.service= service;
}
}
Solution 2 : Using #PostConstruct to set the value to Static Field
#Component
public class MyClass {
private static MyService service;
#Autowired
private MyService srv;
#PostConstruct
public void init() {
this.service= srv;
}
}
Refer here for more detail
I use private static inner Component: FieldSetter, to inject static field: MyBean, at last SelfDestroyBean will help me remove redundant FiledSetter bean
public final class MyClass {
private static MyBean myBean;
#Component
private static class FieldSetter extends SelfDestroyBean {
public FieldSetter(MyBean myBean) {
MyClass.myBean = myBean;
}
}
}
#SuppressWarnings("SpringJavaAutowiredMembersInspection")
public abstract class SelfDestroyBean {
#Autowired
private ApplicationContext context;
#PostConstruct
public void destroy() {
final String[] beanNames = context.getBeanNamesForType(this.getClass());
final BeanDefinitionRegistry registry =
((BeanDefinitionRegistry) context.getAutowireCapableBeanFactory());
for (String beanName : beanNames) {
registry.removeBeanDefinition(beanName);
}
}
}
private static UserService userService = ApplicationContextHolder.getContext().getBean(UserService.class);

How do I inject dependencies in controller tests?

This is my class and its constructor and the dependencies.
public class FavouriteProfilesController extends BaseController implements CurrentUser, JsonHelper {
private final UserProvider userProvider;
private MessagesApi msg;
#javax.inject.Inject
public FavouriteProfilesController(
UserProvider userProvider,
MessagesApi msgApi) {
this.userProvider = userProvider;
this.msg = msgApi;
}
// methods etc...
This is the test code I just copied from the docs:
public class FavouriteProfilesControllerTest extends WithApplication {
#Override
protected Application provideApplication() {
return new GuiceApplicationBuilder()
.configure("play.http.router", "javaguide.tests.Routes")
.build();
}
#Test
public void testIndex() {
Result result = new FavouriteProfilesController().index(); // Inject dependencies here
assertEquals(OK, result.status());
assertEquals("text/html", result.contentType().get());
assertEquals("utf-8", result.charset().get());
assertTrue(contentAsString(result).contains("Welcome"));
}
}
The controller has 2 dependencies, UserProvider and MessagesApi, how do I inject/mock them into the controller test?
If you use Mockito, you can mock them like this:
#RunWith(MockitoJUnitRunner.class)
public class FavouriteProfilesControllerTest extends WithApplication {
#InjectMocks
private FavouriteProfilesController controller;
#Mock
private UserProvider userProvider;
#Mock
private MessagesApi msg;
#Test
public void test() {
Assert.assertNotNull(userProvider);
Assert.asserNotNull(msg);
}
}
The solution depends on what you intend to test. If you mean to mock the whole behavior of UserProvider and MessageApi, using Mockito may be a proper solution.
In case you want to test controller functionality with real objects, you need to inject real objects. This may be done like this:
public class FavouriteProfilesControllerTest extends WithApplication {
#Test
public void testIndex() {
running(Helpers.fakeApplication(), () -> {
RequestBuilder mockActionRequest = Helpers.fakeRequest(
controllers.routes.FavouriteProfilesController.index());
Result result = Helpers.route(mockActionRequest);
assertEquals(OK, result.status());
assertEquals("text/html", result.contentType().get());
assertEquals("utf-8", result.charset().get());
assertTrue(contentAsString(result).contains("Welcome"));
});
}
}
Using of GuiceApplicationBuilder is not necessary, if you do not mean to use different injection binding for your test. Call to Helpers.fakeApplication() invokes the default dependency injection.
You can find more about unit testing in Play here.

Can you use #Autowired with static fields?

Is there some way to use #Autowired with static fields. If not, are there some other ways to do this?
In short, no. You cannot autowire or manually wire static fields in Spring. You'll have to write your own logic to do this.
#Component("NewClass")
public class NewClass{
private static SomeThing someThing;
#Autowired
public void setSomeThing(SomeThing someThing){
NewClass.someThing = someThing;
}
}
#Autowired can be used with setters so you could have a setter modifying an static field.
Just one final suggestion... DON'T
Init your autowired component in #PostConstruct method
#Component
public class TestClass {
private static AutowiredTypeComponent component;
#Autowired
private AutowiredTypeComponent autowiredComponent;
#PostConstruct
private void init() {
component = this.autowiredComponent;
}
public static void testMethod() {
component.callTestMethod();
}
}
Create a bean which you can autowire which will initialize the static variable as a side effect.
Wanted to add to answers that auto wiring static field (or constant) will be ignored, but also won't create any error:
#Autowired
private static String staticField = "staticValue";
You can achieve this using XML notation and the MethodInvokingFactoryBean. For an example look here.
private static StaticBean staticBean;
public void setStaticBean(StaticBean staticBean) {
StaticBean.staticBean = staticBean;
}
You should aim to use spring injection where possible as this is the recommended approach but this is not always possible as I'm sure you can imagine as not everything can be pulled from the spring container or you maybe dealing with legacy systems.
Note testing can also be more difficult with this approach.
You can use ApplicationContextAware
#Component
public class AppContext implements ApplicationContextAware{
public static ApplicationContext applicationContext;
public AppBeans(){
}
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
then
static ABean bean = AppContext.applicationContext.getBean("aBean",ABean.class);
Disclaimer This is by no means standard and there could very well be a better spring way of doing this. None of the above answers address the issues of wiring a public static field.
I wanted to accomplish three things.
Use spring to "Autowire" (Im using #Value)
Expose a public static value
Prevent modification
My object looks like this
private static String BRANCH = "testBranch";
#Value("${content.client.branch}")
public void finalSetBranch(String branch) {
BRANCH = branch;
}
public static String BRANCH() {
return BRANCH;
}
We have checked off 1 & 2 already now how do we prevent calls to the setter, since we cannot hide it.
#Component
#Aspect
public class FinalAutowiredHelper {
#Before("finalMethods()")
public void beforeFinal(JoinPoint joinPoint) {
throw new FinalAutowiredHelper().new ModifySudoFinalError("");
}
#Pointcut("execution(* com.free.content.client..*.finalSetBranch(..))")
public void finalMethods() {}
public class ModifySudoFinalError extends Error {
private String msg;
public ModifySudoFinalError(String msg) {
this.msg = msg;
}
#Override
public String getMessage() {
return "Attempted modification of a final property: " + msg;
}
}
This aspect will wrap all methods beginning with final and throw an error if they are called.
I dont think this is particularly useful, but if you are ocd and like to keep you peas and carrots separated this is one way to do it safely.
Important Spring does not call your aspects when it calls a function. Made this easier, to bad I worked out the logic before figuring that out.
Generally, setting static field by object instance is a bad practice.
to avoid optional issues you can add synchronized definition, and set it only if private static Logger logger;
#Autowired
public synchronized void setLogger(Logger logger)
{
if (MyClass.logger == null)
{
MyClass.logger = logger;
}
}
:
Solution 1 : Using Constructor #Autowired For Static Field
#Component
public class MyClass {
private static MyService service;
#Autowired
public MyClass(MyService service) {
TestClass.service= service;
}
}
Solution 2 : Using #PostConstruct to set the value to Static Field
#Component
public class MyClass {
private static MyService service;
#Autowired
private MyService srv;
#PostConstruct
public void init() {
this.service= srv;
}
}
Refer here for more detail
I use private static inner Component: FieldSetter, to inject static field: MyBean, at last SelfDestroyBean will help me remove redundant FiledSetter bean
public final class MyClass {
private static MyBean myBean;
#Component
private static class FieldSetter extends SelfDestroyBean {
public FieldSetter(MyBean myBean) {
MyClass.myBean = myBean;
}
}
}
#SuppressWarnings("SpringJavaAutowiredMembersInspection")
public abstract class SelfDestroyBean {
#Autowired
private ApplicationContext context;
#PostConstruct
public void destroy() {
final String[] beanNames = context.getBeanNamesForType(this.getClass());
final BeanDefinitionRegistry registry =
((BeanDefinitionRegistry) context.getAutowireCapableBeanFactory());
for (String beanName : beanNames) {
registry.removeBeanDefinition(beanName);
}
}
}
private static UserService userService = ApplicationContextHolder.getContext().getBean(UserService.class);

Categories

Resources