欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Mockito实战 博客分类: 技术总结单元测试 Mockito实战 

程序员文章站 2024-02-12 14:22:52
...

Mockito,测试框架,语法简单,功能强大!
静态、私有、构造等方法测试需要配合PowerMock,PowerMock有Mockito和EasyMock两个版本,语法相同,本文只介绍Mockito.

测试对像:ArticleManager

ArticleManager 类

publicclassArticleManager{void setDatabase(ArticleDatabase database){...}Stringget(String val){
        database.get(val);}String add(String val){
        database.set(val);}}

ArticleDatabase 类

publicclassArticleDatabase{Stringget(String val){...}String add(String val){...}}

注解

@Mock

创建Mock对像

@MockprivateArticleDatabase ds;

实例ds是一个虚拟对像,ds对像本身的所有方法返回值都为Null。

@InjectMocks

@InjectMocksprivateArticleManager articleManager;

将现有Mock对像(ds)注入到articleManager,支持方法包括constructor, setter, property 和 Spring的@Autowired !

@Captor

参数调用截取器

//定义@Captor,如果要获取多个参数, 需要定义多个@Captor@CaptorprivateArgumentCaptor<String> argumentString;
articleManager.add("content");
verify(ds, times(1)).add(argumentString.capture());//打印参数值
logger.debug(argumentString.getValue());

@Spy

Mock类的部分真实或定制后调用。没有定制的方法就调用真实的方法。

@SpyprivateArticleManager articleManager2;//定制方法,这个地方用`when`还会调用真实的方法后返回定制结果//见:http://docs.mockito.googlecode.com/hg/latest/org/mockito/Spy.html
doReturn("0").when(articleManager2).get(0);
articleManager2.get(0);
verify(articleManager2).get(0);//调用真实的方法
articleManager2.get(1)
verify(articleManager2).get(1);

@Spy真实调用也可以用@Mock对像thenCallRealMethod方法调用:

when(articleManager2.get(2)).thenCallRealMethod();

Stubbing

特别制定Mock对像某方法的返回值、类型或异常等,默认是Null,

when(ds.get(0)).thenReturn("first");when(ds.get(1)).thenThrow(newRuntimeException());

verify

对某方法返回结果的校验,包括调用次数,返回值等

articleManager.add("one");
verify(ds).add("one");

参考:@Captor

模糊匹配

在Stubbing或verify里可用模糊匹配来适配多种情况的调用,Mockito在包org.mockito.Matchers下提供有anyString(), anyInt()或者直接anyObject()等大量模糊匹配函数,如果有需要还可自己写,实现(org.hamcrest.Matcher)。 例:

when(articleManager.get(anyString())).toReturn("element");System.out.println(articleManager.get("1"));
verify(articleManager).get(anyString());

在使用校验函数verify时,参数如果有模糊匹配则所有参数都要用模糊匹配,如:

//正确的写法:
verify(mock).articleManager(anyInt(), anyString(), eq("third argument"));//错误用法:
verify(mock).articleManager(anyInt(), anyString(),"third argument");

更多模糊匹配函数请参考:http://mockito.googlecode.com/svn/branches/1.8.5/javadoc/org/mockito/Matchers.html

 

 

1自动生成Mock类

 

在需要Mock的属性上标记@Mock注解,然后@RunWith(MockitoJUnitRunner.class)或者在setUp()方法中显示调用MockitoAnnotations.initMocks(this);生成Mock类即可。

 

2自动注入Mock类到被测试类

 

只要在被测试类上标记@InjectMocks,Mockito就会自动将标记@Mock、@Spy等注解的属性值注入到被测试类中。

 

[java] view plain copy
 
  1. import static org.mockito.Mockito.when;  
  2.    
  3. import java.util.Collections;  
  4. import java.util.List;  
  5.    
  6. import javax.annotation.Resource;  
  7.    
  8. import org.junit.Assert;  
  9. import org.junit.Before;  
  10. import org.junit.Test;  
  11. import org.junit.runner.RunWith;  
  12. import org.mockito.InjectMocks;  
  13. import org.mockito.Mock;  
  14. import org.mockito.MockitoAnnotations;  
  15. importorg.springframework.test.context.ContextConfiguration;  
  16. import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;  
  17.    
  18. import com.cdai.ssi.user.dao.UserDao;  
  19. import com.cdai.ssi.user.domain.UserDomain;  
  20. import com.cdai.ssi.user.dto.UserDto;  
  21. importcom.cdai.ssi.user.service.UserService;  
  22.    
  23. @RunWith(SpringJUnit4ClassRunner.class)  
  24. @ContextConfiguration({"classpath:spring/spring-config.xml"})  
  25. public class UserServiceTest {  
  26.           
  27.          @InjectMocks  
  28.          @Resource(name= "userService")  
  29.          privateUserService userService;  
  30.           
  31.          @Mock  
  32.          privateUserDao userDao;  
  33.    
  34.          @Before  
  35.          publicvoid setUp() {  
  36.                    MockitoAnnotations.initMocks(this);  
  37.          }  
  38.           
  39.          @Test  
  40.          publicvoid testQueryAll() {  
  41.                    when(userDao.selectAll()).  
  42.                             thenReturn(Collections.<UserDomain>emptyList());  
  43.                     
  44.                    List<UserDto>dtoList = userService.queryAll();  
  45.                     
  46.                    Assert.assertTrue(dtoList.isEmpty());  
  47.          }  
  48.           
  49. }  

 

@InjectMocks的问题是:如果被测试类是代理类,那么注入会失效。比如上面的UserService如果是事务或者其他AOP代理类,那么进入@Test方法时UserService中的DAO属性不会被Mock类替换。

 

3 Mock方法定制再也不用录制、播放了

 

 

Mockito的Mock方法定制可读性很强,而且也不需要像EasyMock那样录制播放,定制后就可以使用。

例如:

when(userDao.selectAll()).

                   thenReturn(Collections.<UserDomain>emptyList());

 

4有些方法想Mock定制,有些想调用真实方法

 

因为@Mock针对接口生成Mock类,所以我们是没法调用到真实的实现类的方法。可以使用@Spy注解标注属性,并且标注@Resource注解让Spring注入真实实现类,那么Mockito就会自动生成Spy类。

 

例如:

@InjectMocks

@Resource(name ="userService")

 

privateUserService userService;

        

@Spy

@Resource

privateUserDao userDao;

 

Spy类就可以满足我们的要求。如果一个方法定制了返回值或者异常,那么就会按照定制的方式被调用执行;如果一个方法没被定制,那么调用的就是真实类的方法。

 

如果我们定制了一个方法A后,再下一个测试方法中又想调用真实方法,那么只需在方法A被调用前,调用Mockito.reset(spyObject);就行了。

 

[java] view plain copy
 
  1. import static org.mockito.Mockito.when;  
  2.    
  3. import org.mockito.Mockito;  
  4.    
  5. public class TestMockObject implementsITestMock {  
  6.    
  7.          publicstatic void main(String[] args) {  
  8.                     
  9.                    ITestMockmock = Mockito.mock(TestMockObject.class);  
  10.                    System.out.println(mock.test1());  
  11.                    System.out.println(mock.test2());  
  12.                     
  13.                    ITestMockspy = Mockito.spy(new TestMockObject());  
  14.                    System.out.println(spy.test1());  
  15.                    System.out.println(spy.test2());  
  16.                     
  17.                    when(spy.test1()).thenReturn(100);  
  18.                    System.out.println(spy.test1());  
  19.                     
  20.                    Mockito.reset(spy);  
  21.                    System.out.println(spy.test1());  
  22.                    System.out.println(spy.test2());  
  23.                     
  24.                    when(spy.test1()).thenReturn(104);  
  25.                    System.out.println(spy.test1());  
  26.          }  
  27.    
  28.          @Override  
  29.          publicint test1() {  
  30.                    System.out.print("RealTest1()!!! - ");  
  31.                    return1;  
  32.          }  
  33.    
  34.          @Override  
  35.          publicint test2() {  
  36.                    System.out.print("RealTest2()!!! - ");  
  37.                    return2;  
  38.          }  
  39.    
  40. }  

 

输出为:

0

0

Real Test1()!!! - 1

Real Test2()!!! - 2

Real Test1()!!! - 100

Real Test1()!!! - 1

Real Test2()!!! - 2

Real Test1()!!! - 104

 

要注意的是,对Spy对象的方法定制有时需要用另一种方法:

===============================================================================

Importantgotcha on spying real objects!

 

Sometimes it's impossible to usewhen(Object) for stubbing spies. Example:

  List list = new LinkedList();

  List spy = spy(list);

  

  //Impossible: real method is called so spy.get(0) throwsIndexOutOfBoundsException (the list is yet empty)

  when(spy.get(0)).thenReturn("foo");

  

  //You have to use doReturn() for stubbing

  doReturn("foo").when(spy).get(0);

===============================================================================

因为用when(spy.f1())会导致f1()方法被真正执行,所以就需要另一种写法。

 

 

参考资料

 

Mockito文档

http://mockito.googlecode.com/svn/branches/1.6/javadoc/org/mockito/Mockito.html

 

 

相关标签: Mockito 实战