2012-12-13 27 views
1

我正在使用SPring 3.1.1.RELEASE和JUnit 4.8.1。在我的測試類,我想嘲笑的私人領域,並發現「ReflectionTestUtils」之美...如何將模擬注入標記爲「@Transactional」的Spring類?

@RunWith(SpringJUnit4ClassRunner.class) 
@ContextConfiguration({ "classpath:test-context.xml" }) 
public class OrderServiceTest extends AbstractTransactionalJUnit4SpringContextTests 
    … 
     @Autowired 
    private OrderService m_orderSvc; 

    @Test 
    public void testGetPDOrders() throws QuickBaseException, Exception { 
     … 
      ReflectionTestUtils.setField(m_orderSvc, "m_orderDao", mockOrderDao); 

下面是類和現場我試圖嘲弄......

@Service("orderService") 
@Transactional 
public class OrderServiceImpl implements OrderService { 

    … 
    @Autowired 
    private OrderDAO m_orderDao; 

令人失望的是,我得到了以下錯誤。我讀過這是因爲我的類被標記爲「@Transactional」,但我無法找到適當的解決方法,而僅僅爲了適應JUnit而編寫setter方法似乎很浪費。有沒有人有關於如何將我的模擬對象注入私人領域的另一個建議?

java.lang.IllegalArgumentException: Could not find field [m_orderDao] on target [[email protected]] 
    at org.springframework.util.Assert.notNull(Assert.java:112) 
    at org.springframework.test.util.ReflectionTestUtils.setField(ReflectionTestUtils.java:107) 
    at org.springframework.test.util.ReflectionTestUtils.setField(ReflectionTestUtils.java:84) 
    at org.mainco.subco.myclient.service.OrderServiceTest.testGetPDOrders(OrderServiceTest.java:130) 
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) 
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) 
    at java.lang.reflect.Method.invoke(Method.java:597) 
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44) 
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15) 
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41) 
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20) 
    at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:74) 
    at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:83) 
    at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:72) 
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:231) 
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:49) 
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193) 
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52) 
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191) 
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42) 
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184) 
    at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61) 
    at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:71) 
    at org.junit.runners.ParentRunner.run(ParentRunner.java:236) 
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:174) 
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50) 
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38) 
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467) 
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683) 
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390) 
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197) 

回答

2

我不知道什麼嘲笑你使用的庫,但春之間的Mockito一個有用的整合恰當和笨拙地命名爲「Springockito」,可以使嘲笑的戰術插入了更大的Spring上下文漂亮簡單。

這個想法是,你改變你的測試應用程序上下文來映射這個bean到一個模擬,而不是嘗試在運行時將模擬線連接到你的父bean。

所以你真的就這樣結束了:

文本的context.xml

<beans xmlns="http://www.springframework.org/schema/beans" 
     ... 
     xmlns:mockito="http://www.mockito.org/spring/mockito" 
     xsi:schemaLocation="... http://www.mockito.org/spring/mockito https://bitbucket.org/kubek2k/springockito/raw/tip/springockito/src/main/resources/spring/mockito.xsd"> 

    <mockito:mock id="m_orderDao" class="my.package.OrderDao"/> 

    <!--... other config ...--> 
</beans> 

然後,你可以自動裝配模擬本身到您的測試,如果你需要它,在互動與您現在所做的服務一樣。

如果你是而不是使用Mockito,你仍然可以使用上面的方法,而是使用你的模擬庫的方法來創建mock作爲bean的工廠。

+0

謝謝,我使用的Mockito。採用這種方法,我假設我的JUnit文件中的所有測試都必須使用模擬。有沒有辦法讓它只有一些測試使用模擬器,或者我必須將它們分離到他們自己的JUnit文件中? – Dave

+0

請參考@PeterSzanto的答案。這是一個更好的解決方案。 –

1

截至2014年,最簡單的解決方案是使用屬於Mockito的@InjectMocks註釋。這適用於任何類,包括用@Transactional標記的類。

下面是一個例子:

public class TestTestController { 

    @Mock 
    private TestService testService; 

    @InjectMocks 
    private TestController testController; 

    @Before 
    public void initMocks() { 
     MockitoAnnotations.initMocks(this); 
    } 

    @Test 
    public void testMocks() throws Exception { 
     String mockedReturnValue = "this is a mocked reply"; 
     when(testService.getMessage()).thenReturn(mockedReturnValue); 

     assertEquals(mockedReturnValue, testController.callTestService()); 

    } 
} 

和相關的類

public class TestController { 

    @Autowired 
    private TestService testService; 

    public String callTestService() { 
     return testService.getMessage(); 
    } 

} 

public class TestService { 

    public static final String THIS_IS_A_TEST = "this is a getMessage"; 

    public String getMessage() { 
     return THIS_IS_A_TEST; 
    } 

}