Unit testing: Call @PostConstruct after defining mocked behaviour
Asked Answered
I

2

20

I have two classes:

public MyService {
    @Autowired
    private MyDao myDao;     
    private List<Items> list; 

    @PostConstruct
    private void init(){
         list = myDao.getItems(); 
    }
}

Now I'm wanting to involve MyService in a unit test, and so I'll mock the behaviour MyDao.

XML:

<bean class = "com.package.MyService"> 
<bean  class="org.mockito.Mockito" factory-method="mock"> 
     <constructor-arg value="com.package.MyDao"/>
</bean>

<util:list id="responseItems" value-type="com.package.Item">
    <ref bean="item1"/>
    <ref bean="item2"/>
</util:list>

Unit Test:

@ContextConfiguration("/test-context.xml")
@RunWith(SpringJUnit4ClassRunner.class)
public class MyServiceTest {

    @Autowired 
    MyService myService

    @Autowired 
    MyDao myDao;

    @Resource
    @Qualifier("responseItems")
    private List<Item> responseItems; 

    @Before
    public void setupTests() {
        reset(myDao); 
        when(myDao.getItems()).thenReturn(responseItems); 
    }
}

The problem with this is that the MyService bean is created, and its @PostConstruct bean called before the mocked behaviour is defined.

How can I either define the mocked behaviour in the XML or delay @PostConstruct until after the unit test setup?

Imperil answered 4/7, 2016 at 1:27 Comment(0)
L
26

I have same kind of requirement in my project. where i need to set a string using @PostConstructor and I did not want to ran spring context or in other words I want simple mock. My requirement was follow:

public class MyService {

@Autowired
private SomeBean bean;

private String status;

@PostConstruct
private void init() {
    status = someBean.getStatus();
} 

}

Solution:

public class MyServiceTest(){

@InjectMocks
private MyService target;

@Mock
private SomeBean mockBean;

@Before
public void setUp() throws NoSuchMethodException,  InvocationTargetException, IllegalAccessException {

    MockitoAnnotations.initMocks(this);

    when(mockBean.getStatus()).thenReturn("http://test");

    //call post-constructor
    Method postConstruct =  MyService.class.getDeclaredMethod("init",null); // methodName,parameters
    postConstruct.setAccessible(true);
    postConstruct.invoke(target);
  }

}
Lacunar answered 8/6, 2017 at 14:15 Comment(2)
perfect solutionMervin
perfect solution.Crossfertilization
G
4

MyDao sounds like it is an abstraction of an external system. Generally external systems shouldn't be called in @PostConstruct methods. Instead have your getItems() called by another method in MyService.

Mockito injections will take place after the Spring initiation at which point the mock isn't working as you see. You cannot delay the @PostConstruct. To beat this and have the load run automatically have MyService implement SmartLifecycle and call getItems() in start().

Gouge answered 4/7, 2016 at 19:7 Comment(1)
Yeah - I think you're probably right, that generally my problem indicates a code smell that I should solve.Imperil

© 2022 - 2024 — McMap. All rights reserved.