私には2つのクラスがあります。
public MyService {
@Autowired
private MyDao myDao;
private List<Items> list;
@PostConstruct
private void init(){
list = myDao.getItems();
}
}
ここで、ユニットテストにMyService
を関与させたいので、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>
単体テスト:
@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);
}
}
これの問題は、MyService
Beanが作成され、モックされた動作が定義される前に@PostConstruct Beanが呼び出されることです。
XMLでモック動作を定義するか、または@PostConstruct
単体テストのセットアップ後まで?
私のプロジェクトでも同じような要件があります。 @PostConstructorを使用して文字列を設定する必要があり、Springコンテキストを実行したくない、つまり簡単なモックが必要な場合。私の要件は次のとおりです:
public class MyService {
@Autowired
private SomeBean bean;
private String status;
@PostConstruct
private void init() {
status = someBean.getStatus();
}
}
解決:
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は、外部システムの抽象化のように聞こえます。一般に、外部システムは_@PostConstruct
_メソッドで呼び出すべきではありません。代わりに、MyService
内の別のメソッドによってgetItems()
を呼び出します。
MockitoインジェクションはSpringの開始後に行われ、その時点ではモックは機能していません。 _@PostConstruct
_を遅らせることはできません。これに打ち勝ち、ロードを自動的に実行するには、MyService
実装SmartLifecycle
を使用し、getItems()
でstart()
を呼び出します。