2

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?

Peter Jurkovic
  • 2,686
  • 6
  • 36
  • 55
  • 5
    Because the test runner doesn't use the `ContextLoader` to load the context. This is used by the `SpringBeanAutowiringSupport`. It basically doesn't detect a context. As an added difficulty it also expects it to be a `WebApplicationContext` instead of a regular `ApplicationContext`. As a workaround you could inject the `ApplicationContext` and do the wiring manually yourself by calling `getAutowireCapableBeanFactory().autowireBean(yourInstance);`. – M. Deinum Sep 19 '14 at 05:46

3 Answers3

1

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());
            // ...
        }
      // ...
    }
Peter Jurkovic
  • 2,686
  • 6
  • 36
  • 55
1

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

AntJavaDev
  • 1,204
  • 1
  • 18
  • 24
0

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 {
...
}
Archimedes Trajano
  • 35,625
  • 19
  • 175
  • 265