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?
The method SalesDataAggregate is running on startup because of the @PostConstruct annotation. If you want to keep it from running during tests you can create the class containing the post construct in your test folder and add the @primary annotation so it takes precedence over the class in your main project.
The PostConstruct annotation is used on a method that needs to be executed after dependency injection is done to perform any initialization. This method MUST be invoked before the class is put into service. This annotation MUST be supported on all classes that support dependency injection.
if you are writing unit tests a recommend you use @Mock and @InjectMocks . But if you really want test all the flow and need to inject classes, you can @RunWith(SpringJUnit4ClassRunner. class) and @Autowired your classes.
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);
}
}
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()
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With