您的位置:软件测试 > 开源软件测试 > 开源单元测试工具 > junit
JUnit + Mockito单元测试
作者:网络转载 发布时间:[ 2016/12/29 11:43:39 ] 推荐标签:单元测试 Junit

  再静态导入 org.mockito.Mockito.*; 里的静态方法,这样能在测试方法进行对象的 Mock。Mockito 支持通过静态方法 mock() 来 Mock 对象,或者通过 @Mock 注解,来创建 Mock 对象,但必须将其实例化。先演示下如何 Mock 对象:
  import static org.mockito.Mockito.*;
  @Test
  publicvoidmockIterator(){
  Iterator i = mock(Iterator.class);
  when(i.next()).thenReturn("hello").thenReturn("world");
  String result = i.next() + " " + i.next();
  assertEquals("hello world", result);
  }
  mock 出来的对象拥有和源对象同样的方法和属性, when() 和 thenReturn() 方法是对源对象的配置,怎么理解,是说在第一步 mock() 时,mock 出来的对象还不具备被 Mock 对象实例的行为特征,而 when(...).thenReturn(...) 是根据条件去配置源对象的预期行为,即:当执行 when() 中的操作时,返回 thenReturn() 中的结果。比如上面的代码中,mock 出来的 i 实例在被遍历时会依次输出 “hello” 和 “world”, assertEquals() 是对预期结果和实际结果的判断。
  同理,也可以 Mock 网络请求,比如 HttpServletRequest 里的参数,也可以通过上面的方式来设定被 Mock 的源对象的表现行为。
  对于 when() 不定条件,Mockito 定义了 any() 、 anyInt() 、 anyString() 、 anySet() 等方法来匹配指定类型的不定输入, anyInt() 匹配 int 参数, anyString() 匹配 String 参数, any() 匹配 任意类型的参数。如果需要匹配自定义的类型,可以通过 any(CustomedClass.class) 来配置。
  thenReturn() 返回的是一个确定值,这在模拟可见的行为时是没问题的,但有时候,我们需要得到一个复杂的不定输出的行为,比如返回一个回调方法,或者返回一个类实例,Mockito 可以通过 thenAnswer() 来实现。参考 StackOverflow 上的这篇问答 Mockito : thenAnswer Vs thenReturn 。
  @Test
  publicvoidcount()throwsException{
  Duplicator counter = mock(Counter.class);
  Answer<Integer> answer = new Answer<Integer>() {
  publicIntegeranswer(InvocationOnMock invocationOnMock)throwsThrowable{
  return ((String) invocationOnMock.getArguments()[0]).length();
  }
  };
  when(counter.count(anyString())).thenAnswer(answer);
  }
  InvocationOnMock 接口提供了获取被测试方法的调用信息的几个重要方法:
  getMock() 接口返回 mock 对象;
  getMethod() 接口返回被调用方法的 Method 对象;
  getArguments() 接口返回被测试方法的入参列表;
  getArgument() 接口返回北侧方法指定位置的入参;
  callRealMethod() 接口返回实际的调用方法;
  上面的例子已经说明了 Mockito 能跟踪被 Mock 对象所有的方法调用和它们的入参。除了对方法调用结果是否正确的测试,有时还需要验证一些方法的行为,比如验证方法被调用的次数,验证方法的入参等,Mockito 通过 verify() 方法实现这些场景的测试需求。这被称为“行为测试”。
@Test
publicvoidtestVerify(){
Duplicator mock = mock(Duplicator.class);
when(mock.getUniqueId()).thenReturn(43);
mock.duplicate("Halo");
mock.getUniqueId();
mock.getUniqueId();
verify(mock).duplicate(Matchers.eq("Halo"));
verify(mock, times(2)).getUniqueId();
verify(mock, never()).someMethod();
verify(mock, atLeastOnce()).someMethod();
verify(mock, atLeast(2)).someMethod();
verify(mock, atMost(3)).someMethod();;
}
  verify() 内的条件设置简洁明了,第一个参数是 mock 对象,第二个参数可选,作为状语描述,从方法的名称上能知道具体的用法,不多赘述了。
  Mockito 支持通过 @Spy 注解或 spy() 方法包裹实际对象,除非明确指定对象,否则都会调用包裹后的对象。这种方式实现了对实际对象的部分自定义修改。
  @Test
  publicvoidtestSpy(){
  List<String> spyList = spy(new ArrayList<String>());
  assertEquals(0, spyList.size());
  doReturn(100).when(spyList).size();
  assertEquals(100, spyList.size());
  }
  上面的测试代码中, spy() 修改了 ArrayList 对象的 size() 。但是如果只是在执行某个操作是返回一个期望值,用之前的 mock() 也能实现, spy() 存在的理由是什么,看下面的代码能解释二者之间的差异:
  @Test
  publicvoiddifferMockSpy(){
  List mock = mock(ArrayList.class);
  mock.add("one");
  verify(mock).add("one");
  assertEquals(0, mock.size());
  List spy = spy(new ArrayList());
  spy.add("one");
  verify(spy).add("one");
  assertEquals(1, spy.size());
  }
  从上面的运行结果可以看出, mock() 传入的是类,创建出来的是一个裸的实例,只是为了跟踪该实例下的方法调用,而不会对实例有其他副作用产生;而 spy() 传入的是类实例,它会对该实例进行包裹,创建出来的实例和源实例相同,的不同在于, spy() 包裹后的实例可以对实例内部进行自定义的改动。
  对于依赖注入,Mockito 支持通过 @InjectMocks 注解将被标记的对象自动注入,其依赖会由 mock 出来的对象实例来填充。Mockito 会依次尝试通过 constructor injection、 property injection 和 filed injection,注意,如果其中任一注入策略失败,Mockito 也不会报告错误,必须自行解决依赖。
  Constructor injection : @InjectMocks 优先选择的注入策略,如果对象通过构造函数成功 mock 出来,则不会再进行后面的注入策略。
  Property setter injection :会首先根据属性的类型(如果类型匹配则忽略变量名),如果有多个匹配项,则选择 mock 名和属性名相同的变量进行注入。
  Field injection :同样首先根据域的类型(如果类型匹配则忽略变量名),如果有多个匹配项,则选择 mock 名和域名相同的变量进行注入。
  参考下面的样例代码:
public classArticleManagerTestextendsSampleBaseTestCase{
@Mock
private ArticleCalculator calculator;
@Mock(name = "database")
private ArticleDatabase dbMock; // note the mock name attribute
@Spy
private UserProvider userProvider = new ConsumerUserProvider();
@InjectMocks
private ArticleManager manager;
@Test
publicvoidshouldDoSomething(){
manager.initiateArticle();
verify(database).addListener(any(ArticleListener.class));
}
}
public classSampleBaseTestCase{
@Before
publicvoidinitMocks(){
MockitoAnnotations.initMocks(this);
}
}
  上面代码中, @InjectMocks 注解会把 mock 出来的 dbMock 和 calculator 注入进 manager 中。 ArticleManager 可以只有一个有参构造函数,或者只有无参构造器,或者都有。需要注意的是,Mockito 无法实例化 inner class、local class、abstract class 和 interface。
  对需要注入的域,Constructor injection 会发生在下面的代码中:
public classArticleManager{
ArticleManager(ArticleCalculator calculator, ArticleDatabase database) {
// parameterized constructor
}
}
Property setter injection 在下面的代码中完成:
public classArticleManager{
// no-arg constructor
ArticleManager() {  }
// setter
voidsetDatabase(ArticleDatabase database){ }
// setter
voidsetCalculator(ArticleCalculator calculator){ }
}
Field injection:
public classArticleManager{
private ArticleDatabase database;
private ArticleCalculator calculator;
}

上一页12下一页
软件测试工具 | 联系我们 | 投诉建议 | 诚聘英才 | 申请使用列表 | 网站地图
沪ICP备07036474 2003-2017 版权所有 上海泽众软件科技有限公司 Shanghai ZeZhong Software Co.,Ltd