1. 程式人生 > >Spring中如何使用Mockito做單元測試

Spring中如何使用Mockito做單元測試

在Spring環境下做單元測試常常遇到很多問題,Controller層還好說,Spring本身提供了很多方法來支援,這個可以看我的另一篇文章

但是在服務層和持久層,如果是使用註解注入的方式來設計的話,用Mock來替換真實的注入類就很麻煩了,還好我們有Mockito框架。但一段程式碼,你會發現它比easyMock好用多了

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath*:application-context-test.xml" })
public class RouteServiceTestCase{
   @InjectMocks
   @Autowired
   private IRouteService service;

   @Mock
   private IRouteMatrixDataProvider provider;

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

   @Test
   public void testGetAirlineCode() {
       RouteMatrix rm = new RouteMatrix();
       rm.setAirlineCode("kkk");
       Mockito.when(this.provider.getRevenueRoute("HKG", "AMM", true)).thenReturn(rm);
       String code = this.service.getAirlineCode("HKG", "AMM", this.brand, this.cInfo, true);
       Assert.assertNotNull(code);
       Assert.assertEquals("kkk", code);
       code = this.service.getAirlineCode("HKG", "KKK", this.brand, this.cInfo, true);
       Assert.assertNull(code);
   }

   @Test
   public void testGetAirlineCode2() {
       String code = this.service.getAirlineCode("HKG", "AMM", this.brand, this.cInfo, true);
       Assert.assertNotNull(code);
       Assert.assertEquals("kkk", code);
   }
}

@Spy
@Autowired
private IRouteMatrixDataProvider provider;

service 被標記了 @InjectMocks , 在 myBefore方法中 執行 MockitoAnnotations.initMocks(this); 的時候,會將標記了 @Mock 或 @Spy 的屬性注入到 service 中。
如果是 @Mock, 那就是通常的方式,service裡面的provider完全被Mock例項替換,所有的呼叫都是針對Mock生成類的。
如果是 @Autowired 加 @Spy , 那麼對於定製了返回值的會呼叫 Mock例項,否則會呼叫真實注入的屬性,但是這裡有一個限制,如果是代理類會報錯,比如Spring 的AOP代理。 


對於AOP的代理類,如果想一部分用Mock,一部分用真實的例項,感覺目前是有些困難,暫時想到的辦法就是寫兩個測試類,一個注入真實屬性,一個注入mock屬性。

方法呼叫時,如果不想指定一個明確的引數,就可以用下面這樣的寫法來表示任意的引數。

Mockito.when(this.provider.getRevenueRoute(Matchers.anyString(), Matchers.anyString(), Matchers.anyBoolean()))
           .thenReturn(rm);

但是這裡也有一個限制就是,如果有一個引數使用了any***(),則所有的引數都必需使用這種方式,不能像下面這樣寫


Mockito.when(this.provider.getRevenueRoute(Matchers.anyString(), Matchers.anyString(), true))