IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 开发测试 -> Android单元测试学习总结,层层深入 -> 正文阅读

[开发测试]Android单元测试学习总结,层层深入

@Test

public void isTeenager() {

    Assert.assertFalse(simpleClass.isTeenager(20));

    Assert.assertTrue(simpleClass.isTeenager(14));

}



@Test

public void add() {

    Assert.assertEquals(simpleClass.add(3, 2), 5);

    Assert.assertNotEquals(simpleClass.add(3, 2), 4);

}



@Test

public void getNameById() {

    Assert.assertEquals(simpleClass.getNameById(1), "小明");

    Assert.assertEquals(simpleClass.getNameById(2), "小红");

    Assert.assertEquals(simpleClass.getNameById(10), "");

}

}




其中`setUp()`是自动生成的添加了`@Before`注解,这会在每个测试方法执行前执行,因此在这里创建一个目标对象,也可以选择添加`@BeforeClass`注解但这时`setUp()`应该改为静态的方法。然后在每个测试方法中编写测试用例,这里使用`org.junit.Assert`包中的断言方法,有很多`assertXXX`方法,可以自己选择用来判断目标方法的结果是否满足预期。



#### [](
)2\. Assert类中的常用断言方法



| 方法 | 含义 |

| :-- | :-- |

| assertNull(Object object) | 断言对象为空 |

| assertNull(String message, Object object) | 断言对象为空,如果不为空抛出异常携带指定的message信息 |

| assertNotNull(Object object) | 断言对象不为空 |

| assertNotNull(Object object) | 断言对象不为空,如果为空抛出异常携带指定的message信息 |

| assertSame(Object expected, Object actual) | 断言两个对象引用的是同一个对象 |

| assertSame(String message, Object expected, Object actual) | 断言两个对象引用的是同一个对象,否则抛出异常携带指定的message信息 |

| assertNotSame(Object expected, Object actual) | 断言两个对象引用的不是同一个对象 |

| assertNotSame(String message, Object expected, Object actual) | 断言两个对象引用的不是同一个对象,否则抛出异常携带指定的message信息 |

| assertTrue(boolean condition) | 断言结果为true |

| assertTrue(String message, boolean condition) | 断言结果为true, 为false时抛出异常携带指定的message信息 |

| assertFalse(boolean condition) | 断言结果为false |

| assertFalse(String message, boolean condition) | 断言结果为false, 为true时抛出异常携带指定的message信息 |

| assertEquals(long expected, long actual) | 断言两个long 类型 expected 和 actual 的值相等 |

| assertEquals(String message, long expected, long actual) | 断言两个long 类型 expected 和 actual 的值相等,如不相等则抛异常携带指定message信息 |

| assertEquals(Object expected, Object actual) | 断言两个对象相等 |

| assertEquals(String message, Object expected, Object actual) | 断言两个对象相等,如果不相等则抛出异常携带指定的message信息 |

| assertEquals(float expected, float actual, float delta) | 断言两个 float 类型 expect 和 actual 在 delta 偏差值下相等,delta是误差精度 |

| assertEquals(String message, float expected, float actual, float delta) | 断言两个 float 类型 expect 和 actual 在 delta 偏差值下相等,如果不相等则抛出异常携带指定的message信息 |

| assertEquals(double expected, double actual, double delta) | 断言两个 double 类型 expect 和 actual 在 delta 偏差值下相等 |

| assertEquals(String message, double expected,double actual, double delta) | 断言两个 double 类型 expect 和 actual 在 delta 偏差值下相等,如果不相等则抛出异常携带指定的message信息 |

| assertArrayEquals(T\[\] expected, T\[\] actual) | 断言两个相同类型的数组的元素一一对应相等 |

| assertArrayEquals(String message, T\[\] expected, T\[\] actual) | 断言两个相同类型的数组的元素一一对应相等,如果不相等则抛出异常携带指定的message信息 |

| fail() | 直接让测试失败 |

| fail(String message) | 直接让测试失败并给出message错误信息 |

| assertThat(T actual, Matcher<? super T> matcher) | 断言actual和matcher规则匹配 |

| assertThat(String reason, T actual, Matcher<? super T> matcher) | 断言actual和matcher规则匹配,否则抛出异常携带指定的reason信息 |



其中`assertEquals`的方法,都对应有一个`assertNotEquals`方法,这里不列了,`assertThat`是一个强大的方法:



Assert.assertThat(1, is(1));

Assert.assertThat(0, is(not(1)));

Assert.assertThat(“hello”, startsWith(“h”));

List items = new ArrayList<>();

items.add(“aaa”);

items.add(“bbb”);

Assert.assertThat(items, hasItem(“aaa”));




需要静态导入`org.hamcrest.Matchers`类里面的方法,更多匹配方法请参考这个类。



#### [](
)3\. 运行测试类



选中测试类右键Run运行,控制面板中就会显示测试结果:  

![在这里插入图片描述](https://img-blog.csdnimg.cn/2019041811152953.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2x5YWJjMTIzNDU2,size_16,color_FFFFFF,t_70)  

如果所有的测试用例都正常返回了预期的结果,则面板中左侧每个测试方法前面会带一个绿色的对勾,否则方法前面会变成红色感叹号并且控制面板会输出异常,现在来改一个业务方法试一下:



public boolean isTeenager(int age) {

    if (age < 15) {

        return false;

    }

    return false;

} 



这里将`age < 15`改为输出false,假设这是我们在编码的时候由于疏忽粗心造成的,然后运行测试类:  

![在这里插入图片描述](https://img-blog.csdnimg.cn/20190418112115158.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2x5YWJjMTIzNDU2,size_16,color_FFFFFF,t_70)  

控制面板会告诉那一行出错了:  

![在这里插入图片描述](https://img-blog.csdnimg.cn/20190418112201648.png)  

也就是说这里没有返回预期的结果,说明我们编写的业务逻辑是有错误的,这时就需要改bug了。



#### [](
)4\. 运行单个测试方法或多个测试类



上面是运行的整个测试类,如果要运行测试类的单个方法,则鼠标只选中某个要运行的测试方法,然后右键选择Run即可。如果要同时运行多个测试类,而如果多个测试类在同一个包下面,则选中多个测试类所在的包目录,然后右键选择Run运行。否则可以通过下面的方式指定,创建一个空的测试类,然后添加注解:



@RunWith(Suite.class)

@Suite.SuiteClasses({SimpleClassTest.class, SimpleClass2Test.class})

public class RunMultiTest {

}




运行这个测试类就可以将指定的测试类的方法一起运行。



### [](
)二、Mockito测试框架的使用



前面介绍的只能测试不涉及Android相关Api的java代码用例,如果涉及到Android相关Api的时候,就不方便了,这时如果不依赖第三方库的话可能需要使用仪器化测试跑到Android设备上去运行,于是有一些比较好的第三方的替代框架可以来模拟使用Android的代码测试,Mockito就是基于依赖注入实现的一个测试框架。



#### [](
)1\. Mock概念的理解



什么是Mock, 这个单词的中文意思就是“模仿”或者“虚假”的意思,也就是要模仿一个对象,为啥要模仿?  

在传统的JUnit单元测试中,没有消除在测试中对对象的依赖,如A对象依赖B对象方法,在测试A对象的时候,我们需要构造出B对象,这样子增加了测试的难度,或者使得我们对某些类的测试无法实现。这与单元测试的思路相违背。  

还有一个主要的问题就是本地单元测试由于是运行本地JVM环境,无法依赖Android的api,只靠纯Junit的测试环境很难模拟出完整的Android环境,导致无法测试Android相关的代码,而Mock就能解决这个问题,通过Mock能够很轻易的实现对象的模拟。



添加依赖:



dependencies {

implementation fileTree(dir: 'libs', include: ['*.jar'])

testImplementation 'org.mockito:mockito-core:2.19.0'

....

}




#### [](
)2\. Mockito中几种Mock对象的方式



使用之前通过静态方式导入会使用更方便:



// 静态导入会使代码更简洁

import static org.mockito.Mockito.*;




直接mock一个对象:



@Test

public void testMock() {

    SimpleClass mockSimple = Mockito.mock(SimpleClass.class);

    assertNotNull(mockSimple);

} 



注解方式mock一个对象:



@Mock

SimpleClass simple;



@Before

public void setUp() {

    MockitoAnnotations.initMocks(this);

}



@Test

public void testMock() {

    assertNotNull(simple);

} 



运行器方式mock一个对象:



@RunWith(MockitoJUnitRunner.class)

public class ExampleUnitTest {

@Mock

SimpleClass simple;



@Test

public void testMock() {

    assertNotNull(simple);

}

}




MockitoRule方式mock一个对象:



public class ExampleUnitTest {

@Mock

SimpleClass simple;



@Rule //<--使用@Rule

public MockitoRule mockitoRule = MockitoJUnit.rule();



@Test

public void testMock() {

    assertNotNull(simple);

}

}




#### [](
)3\. 验证行为



##### [](
)verify(T mock)函数的使用



`verify(T mock)`的作用是验证发生的某些行为等同于`verify(mock, times(1))` 例如:



@Test

public void testMock() {

 //创建mock对象

 List mockedList = mock(List.class);

 //使用mock对象

 mockedList.add("one");

 mockedList.clear();



 //验证mockedList.add("one")是否被调用,如果被调用则当前测试方法通过,否则失败

 verify(mockedList).add("one");

 //验证 mockedList.clear()是否被调用,如果被调用则当前测试方法通过,否则失败

 verify(mockedList).clear();

}


@Test

public void testMock() {

	mock.someMethod("some arg");

	//验证mock.someMethod("some arg")是否被调用,如果被调用则测试方法通过,否则失败

	verify(mock).someMethod("some arg");

	

} 

```



也就是说如果把调用的方法注释掉,则运行testMock()方法就会失败。



通过`verify`关键字,一旦mock对象被创建了,mock对象会记住所有的交互。然后你就可能选择性的验证你感兴趣的交互。



通常需要配合一些测试方法来验证某些行为,这些方法称为"打桩方法"(Stub),打桩的意思是针对mock出来的对象进行一些模拟操作,如设置模拟的返回值或抛出异常等。



常见的打桩方法:



| 方法名 | 方法含义 |

| :-- | :-- |

| doReturn(Object toBeReturned) | 提前设置要返回的值 |

| doThrow(Throwable… toBeThrown) | 提前设置要抛出的异常 |

| doAnswer(Answer answer) | 提前对结果进行拦截 |

| doCallRealMethod() | 调用某一个方法的真实实现 |

| doNothing() | 设置void函数什么也不做 |

| thenReturn(T value) | 设置要返回的值 |

| thenThrow(Throwable… throwables) | 设置要抛出的异常 |

| thenAnswer(Answer<?> answer) | 对结果进行拦截 |



例如:



```

 @Test

 public void testMock() {

	 // 你可以mock具体的类型,不仅只是接口

	 List mockedList = mock(List.class);

	 // 打测试桩

	 when(mockedList.get(0)).thenReturn("first");

	 doReturn("aaaa").when(mockedList).get(1);

	 when(mockedList.get(1)).thenThrow(new RuntimeException());

	 doThrow(new RuntimeException()).when(mockedList).clear();



	 // 输出“first”

	 System.out.println(mockedList.get(0));

	 // 因为get(999) 没有打桩,因此输出null, 注意模拟环境下这个地方是不会报IndexOutOfBoundsException异常的

	 System.out.println(mockedList.get(999));

	 // get(1)时会抛出异常

	 System.out.println(mockedList.get(1));

	 // clear会抛出异常

	 mockedList.clear();

 } 

```



`doXXX`和`thenXXX`使用上差不多,一个是调用方法之前设置好返回值,一个是在调用方法之后设置返回值。默认情况下,Mock出的对象的所有非void函数都有返回值,对象类型的默认返回的是null,例如返回`int、boolean、String`的函数,默认返回值分别是`0、false`和`null`。



##### [](
)使用`when(T methodCall)`函数



打桩方法需要配合`when(T methodCall)`函数,意思是使测试桩方法生效。当你想让这个mock能调用特定的方法返回特定的值,那么你就可以使用它。



例如:



```

when(mock.someMethod()).thenReturn(10);

 //你可以使用灵活的参数匹配,例如 

 when(mock.someMethod(anyString())).thenReturn(10);



 //设置抛出的异常

 when(mock.someMethod("some arg")).thenThrow(new RuntimeException());



 //你可以对不同作用的连续回调的方法打测试桩:

 //最后面的测试桩(例如:返回一个对象:"foo")决定了接下来的回调方法以及它的行为。

 

 when(mock.someMethod("some arg"))

  .thenReturn("foo")//第一次调用someMethod("some arg")会返回"foo"

  .thenThrow(new RuntimeException());//第二次调用someMethod("some arg")会抛异常

  

 //可以用以下方式替代比较小版本的连贯测试桩:

 when(mock.someMethod("some arg"))

  .thenReturn("one", "two");

 //和下面的方式效果是一样的

 when(mock.someMethod("some arg"))

  .thenReturn("one")

  .thenReturn("two");



 //比较小版本的连贯测试桩并且抛出异常:

 when(mock.someMethod("some arg"))

  .thenThrow(new RuntimeException(), new NullPointerException(); 

```



##### [](
)使用`thenAnswer`为回调做测试桩



```

when(mock.someMethod(anyString())).thenAnswer(new Answer() {

     Object answer(InvocationOnMock invocation) {

         Object[] args = invocation.getArguments();

         Object mock = invocation.getMock();

         return "called with arguments: " + args;

     }

 });



 // 输出 : "called with arguments: foo"

 System.out.println(mock.someMethod("foo")); 

```



##### [](
)使用`doCallRealMethod()`函数来调用某个方法的真实实现方法



注意,在Mock环境下,所有的对象都是模拟出来的,而方法的结果也是需要模拟出来的,如果你没有为mock出的对象设置模拟结果,则会返回默认值,例如:



```

public class Person {

    public String getName() {

        return "小明";

    }

}



@Test

public void testPerson() {

    Person mock = mock(Person.class);

    //输出null,除非设置发回模拟值when(mock.getName()).thenReturn("xxx");

    System.out.println(mock.getName());

} 

```



因为getName()方法没有设置模拟返回值,而getName()返回值是String类型的,因此直接调用的话会返回String的默认值null,所以上面代码如果要想输出getName()方法的真实返回值的话,需要设置doCallRealMethod():



```

 @Test

 public void testPerson() {

     Person mock = mock(Person.class);

     doCallRealMethod().when(mock).getName();

     //输出“小明”

     System.out.println(mock.getName());

 } 

```



##### [](
)使用`doNothing()`函数是为了设置void函数什么也不做



需要注意的是默认情况下返回值为void的函数在mocks中是什么也不做的但是,也会有一些特殊情况。如:



测试桩连续调用一个void函数时:



```

 doNothing().doThrow(new RuntimeException()).when(mock).someVoidMethod();

   //does nothing the first time:

   mock.someVoidMethod();

   //throws RuntimeException the next time:

   mock.someVoidMethod(); 

```



监控真实的对象并且你想让void函数什么也不做:



```

List list = new LinkedList();

List spy = spy(list);



//let's make clear() do nothing

doNothing().when(spy).clear();



spy.add("one");



//clear() does nothing, so the list still contains "one"

spy.clear(); 

```



##### [](
)使用`doAnswer()`函数测试void函数的回调



当你想要测试一个无返回值的函数时,可以使用一个含有泛型类Answer参数的doAnswer()函数做回调测试。假设你有一个void方法有多个回调参数,当你想指定执行某个回调时,使用thenAnswer很难实现了,如果使用doAnswer()将非常简单,示例代码如下:



```

MyCallback callback = mock(MyCallback.class);

Mockito.doAnswer(new Answer() {

    @Override

    public Object answer(InvocationOnMock invocationOnMock) throws Throwable {

        //获取第一个参数

        MyCallback call = invocation.getArgument(0);

        //指定回调执行操作

        call.onSuccess();

        return null;

    }



}).when(mockedObject.requset(callback));



doAnswer(new Answer() {

         @Override

          public Object answer(InvocationOnMock invocation) throws Throwable {

              System.out.println("onSuccess answer");

              return null;

          }

 }).when(callback).onSuccess();

 

mockedObject.requset(callback) 

```



##### [](
)需要使用doReturn函数代替thenReturn的情况



如当监控真实的对象并且调用真实的函数带来的影响时



```

List list = new LinkedList();

List spy = spy(list);



//不可能完成的:真实方法被调用的时候list仍是空的,所以spy.get(0)会抛出IndexOutOfBoundsException()异常

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



//这时你应该使用doReturn()函数

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

```



##### [](
)使用`doThrow()`函数来测试void函数抛出异常



```

SimpleClass mock = mock(SimpleClass.class);

doThrow(new RuntimeException()).when(mock).someVoidMethod();

mock.someVoidMethod(); 

```



总之使用`doThrow(), doAnswer(), doNothing(), doReturn() and doCallRealMethod()` 这些函数时可以在适当的情况下调用`when()`来解决一些问题., 如当你需要下面这些功能时这是必须的:



*   测试void函数

*   在受监控的对象上测试函数

*   不只一次的测试同一个函数,在测试过程中改变mock对象的行为



#### [](
)4\. 验证方法的调用次数



需要配合使用一些方法



| 方法 | 含义 |

| :-- | :-- |

| times(int wantedNumberOfInvocations) | 验证调用方法的次数 |

| never() | 验证交互没有发生,相当于times(0) |

| only() | 验证方法只被调用一次,相当于times(1) |

| atLeast(int minNumberOfInvocations) | 至少进行n次验证 |

| atMost(int maxNumberOfInvocations) | 至多进行n次验证 |

| after(long millis) | 在给定的时间后进行验证 |

| timeout(long millis) | 验证方法执行是否超时 |

| description(String description) | 验证失败时输出的内容 |

| verifyZeroInteractions | 验证mock对象没有交互 |



例如:



```

mock.someMethod("some arg");

mock.someMethod("some arg");

//验证mock.someMethod("some arg")被连续调用两次,即如果没有调用两次则验证失败

verify(mock, times(2)).someMethod("some arg"); 

//注意,下面三种是等价的,都是验证someMethod()被只调用一次

verify(mock).someMethod(“some arg”);

verify(mock, times(1)).someMethod(“some arg”);

verify(mock, only()).someMethod(“some arg”);


mPerson.getAge();

mPerson.getAge();

//验证至少调用2次

verify(mPerson, atLeast(2)).getAge();

//验证至多调用2次

verify(mPerson, atMost(2)).getAge(); 

//下面两种等价,验证调用次数为0

verify(mPerson, never()).getAge();

verify(mPerson, times(0)).getAge();


mPerson.getAge();

mPerson.getAge();

long current = System.currentTimeMillis();

System.out.println(current );

//延时1s后验证mPerson.getAge()是否被执行了2次

verify(mPerson, after(1000).times(2)).getAge();

System.out.println(System.currentTimeMillis() - current); 

mPerson.getAge();

mPerson.getAge();

//验证方法在100ms超时前被调用2次

verify(mPerson, timeout(100).times(2)).getAge();


 @Test

  public void testVerifyZeroInteractions() {

      Person person = mock(Person.class);

      person.eat("a");

	  //由于person对象发生了交互,所以这里验证失败,把上面的调用注释掉这里就会验证成功

      verifyZeroInteractions(person);

      //可以验证多个对象没有交互

      //verifyZeroInteractions(person,person2 );

  } 

@Test

public void testVerifyZeroInteractions() {

  Person person = mock(Person.class);

  person.eat("a");

  verify(person).eat("a");

  //注意,这将会无法到达验证目的,不能跟verify()混用

  verifyZeroInteractions(person,person2 );

}




#### [](
)5\. 参数匹配器 (matchers)



Mockito以自然的java风格来验证参数值: 使用equals()函数。有时,当需要额外的灵活性时你可能需要使用参数匹配器,也就是argument matchers :



// 使用内置的anyInt()参数匹配器

when(mockedList.get(anyInt())).thenReturn(“element”);

// 使用自定义的参数匹配器( 在isValid()函数中返回你自己的匹配器实现 )

when(mockedList.contains(argThat(isValid()))).thenReturn(“element”);

// 输出element

System.out.println(mockedList.get(999));

// 你也可以验证参数匹配器

verify(mockedList).get(anyInt());




常用的参数匹配器:



| 方法名 | 含义 |

| :-- | :-- |

| anyObject() | 匹配任何对象 |

| any(Class type) | 与anyObject()一样 |

| any() | 与anyObject()一样 |

| anyBoolean() | 匹配任何boolean和非空Boolean |

| anyByte() | 匹配任何byte和非空Byte |

| anyCollection() | 匹配任何非空Collection |

| anyDouble() | 匹配任何double和非空Double |

| anyFloat() | 匹配任何float和非空Float |

| anyInt() | 匹配任何int和非空Integer |

| anyList() | 匹配任何非空List |

| anyLong() | 匹配任何long和非空Long |

| anyMap() | 匹配任何非空Map |

| anyString() | 匹配任何非空String |

| contains(String substring) | 参数包含给定的substring字符串 |

| argThat(ArgumentMatcher matcher) | 创建自定义的参数匹配模式 |

| eq(T value) | 匹配参数等于某个值 |



一些示例代码:



@Test

public void testPersonAny(){

    when(mPerson.eat(any(String.class))).thenReturn("米饭");

    //或:

    when(mPerson.eat(anyString())).thenReturn("米饭");

    //输出米饭

    System.out.println(mPerson.eat("面条"));

}



@Test

public void testPersonContains(){

    when(mPerson.eat(contains("面"))).thenReturn("面条");

    //输出面条

    System.out.println(mPerson.eat("面"));

}



@Test

public void testPersonArgThat(){

    //自定义输入字符长度为偶数时,输出面条。

    when(mPerson.eat(argThat(new ArgumentMatcher<String>() {

        @Override

        public boolean matches(String argument) {

            return argument.length() % 2 == 0;

        }

    }))).thenReturn("面条");

    //输出面条

    System.out.println(mPerson.eat("1234"));

} 



需要注意的是,如果你打算使用参数匹配器,那么所有参数都必须由匹配器提供。例如:



verify(mock).someMethod(anyInt(), anyString(), eq(“third argument”));

// 上述代码是正确的,因为eq()也是一个参数匹配器

verify(mock).someMethod(anyInt(), anyString(), “third argument”);

// 上述代码是错误的, 因为所有参数必须由匹配器提供,而参数"third argument"并非由参数匹配器提供,因此会抛出异常




像anyObject(), eq()这样的匹配器函数不会返回匹配器。它们会在内部将匹配器记录到一个栈当中,并且返回一个假的值,通常为null。



#### [](
)6\. 使用InOrder验证执行执行顺序



验证执行执行顺序主要使用`InOrder`函数  

如,验证mock一个对象的函数执行顺序:



@Test

public void testInorder() {

    List<String> singleMock = mock(List.class);



    singleMock.add("小明");

    singleMock.add("小红");



    // 为该mock对象创建一个inOrder对象

    InOrder inOrder = inOrder(singleMock);



    // 验证add函数首先执行的是add("小明"),然后才是add("小红"),否则测试失败

    inOrder.verify(singleMock).add("小明");

    inOrder.verify(singleMock).add("小红");

} 



验证多个mock对象的函数执行顺序:



@Test

public void testInorderMulti() {

    List<String> firstMock = mock(List.class);

    List<String> secondMock = mock(List.class);



    firstMock.add("小明");

    secondMock.add("小红");



    // 为这两个Mock对象创建inOrder对象

    InOrder inOrder = inOrder(firstMock, secondMock);



    // 验证它们的执行顺序

    inOrder.verify(firstMock).add("小明");

    inOrder.verify(secondMock).add("小红");

} 



验证执行顺序是非常灵活的,你不需要一个一个的验证所有交互,只需要验证你感兴趣的对象即可。 你可以选择单个mock对象和多个mock对象混合着来,也可以仅通过那些需要验证顺序的mock对象来创建InOrder对象。



#### [](
)7\. 使用Spy监控真实对象



监控真实对象使用`spy()`函数生成,或者也可以像@Mock那样使用`@Spy`注解来生成一个监控对象, 当你你为真实对象创建一个监控(spy)对象后,在你使用这个spy对象时真实的对象也会也调用,除非它的函数被stub了。尽量少使用spy对象,使用时也需要小心形式。



@Test

public void testSpy() {

    List<String> list = new ArrayList<>();

    List<String> spy = spy(list);



    // 你可以选择为某些函数打桩

    when(spy.size()).thenReturn(100);



    // 调用真实对象的函数

    spy.add("one");

    spy.add("two");



    // 输出第一个元素"one"

    System.out.println(spy.get(0));



    // 因为size()函数被打桩了,因此这里返回的是100

    System.out.println(spy.size());



    // 验证交互

    verify(spy).add("one");

    verify(spy).add("two");

} 



使用`@Spy`生成监控对象:



@Spy

Person mSpyPerson;



@Test

public void testSpyPerson() {

	//将会输出Person 类中getName()的真实实现,而不是null

    System.out.println(mSpyPerson.getName());

} 



理解监控真实对象非常重要!有时,在监控对象上使用`when(Object)`来进行打桩是不可能或者不切实际的。因此,当使用监控对象时请考虑`doReturn|Answer|Throw()`函数族来进行打桩。例如:



List list = new LinkedList();

List spy = spy(list);

// 不可能实现 : 因为当调用spy.get(0)时会调用真实对象的get(0)函数,

// 此时会发生IndexOutOfBoundsException异常,因为真实List对象是空的

when(spy.get(0)).thenReturn(“foo”);

// 你需要使用doReturn()来打桩

doReturn(“foo”).when(spy).get(0);




#### [](
)8\. 使用ArgumentCaptor进行参数捕获



参数捕获主要为了下一步的断言做准备,示例代码:



@Test

public void argumentCaptorTest() {

    List<Object> mock = mock(List.class);

    mock.add("John");

	 //构建要捕获的参数类型,这里是String

    ArgumentCaptor argument = ArgumentCaptor.forClass(String.class);

    //在verify方法的参数中调用argument.capture()方法来捕获输入的参数

    verify(mock).add(argument.capture());

    //验证“John”参数捕获

    assertEquals("John", argument.getValue());

} 

 @Test

    public void argumentCaptorTest2() {

        List<Object> mock = mock(List.class);

        mock.add("Brian");

        mock.add("Jim");



        ArgumentCaptor argument = ArgumentCaptor.forClass(String.class);

        verify(mock, times(2)).add(argument.capture());

        //如果又多次参数调用,argument.getValue()捕获到的是最后一次调用的参数

        assertEquals("Jim", argument.getValue());

        //如果要获取所有的参数值可以调用argument.getAllValues()

        assertArrayEquals(new Object[]{"Brian","Jim"}, argument.getAllValues().toArray());

    } 

```



#### [](
)9\. 使用@InjectMocks自动注入依赖对象



有时我们要测试的对象内部需要依赖另一个对象,例如:



```

public class User {

    private Address address;



    public void setAddress(Address address) {

        this.address = address;

    }



    public String getAddress() {

        return address.getDetail();

    }

} 

public class Address {

public String getDetail() {

    return "detail Address";

}

}




User类内部需要依赖Address类,当我们测试的时候需要mock出这两个对象,然后将Address对象传入到User当中,这样如果依赖的对象多了的话就相当麻烦,Mockito 提供了可以不用去手动注入对象的方法,首先使用`@InjectMocks`注解需要被注入的对象,如User,然后需要被依赖注入的对象使用`@Mock`或`@Spy`注解,之后Mockito 会自动完成注入过程,例如:



@InjectMocks

User mTestUser;

@Mock

Address mAddress;

@Test

public void argumentInjectMock() {

    when(mAddress.getDetail()).thenReturn("浙江杭州");

    System.out.println(mTestUser.getAddress());

} 



这样就不用关心为User 设置Address ,只要为User需要依赖的类添加注解就可以了,然后直接将重点放到测试方法的编写上。



或者使用@Spy监控真实对象注入也可以:



@InjectMocks

User mTestUser;

@Spy

Address mAddress;

@Test

public void argumentInjectMock() {

    //  when(mAddress.getDetail()).thenReturn("浙江杭州");

    System.out.println(mTestUser.getAddress());

} 



其他:



连续调用的另一种更简短的版本:



// 第一次调用时返回"one",第二次返回"two",第三次返回"three"

when(mock.someMethod(“some arg”)).thenReturn(“one”, “two”, “three”);




参考:[Mockito 中文文档](
)



### [](
)三、PowerMockito框架使用



Mockito框架基本满足需求但是有一些局限性,如对static、final、private等方法不能mock,PowerMockito就可以解决这些问题,PowerMockito是一个扩展了其它如EasyMock等mock框架的、功能更加强大的框架。PowerMock使用一个自定义类加载器和字节码操作来模拟静态方法,构造函数,final类和方法,私有方法,去除静态初始化器等等。



添加依赖:



testImplementation ‘org.powermock:powermock-module-junit4:2.0.2’

testImplementation 'org.powermock:powermock-module-junit4-rule:2.0.2'

testImplementation 'org.powermock:powermock-api-mockito2:2.0.2'

testImplementation 'org.powermock:powermock-classloading-xstream:2.0.2' 



#### [](
)1\. 普通Mock的方式



目标类:



public class CommonExample {

public boolean callArgumentInstance(File file) {

    return file.exists();

}

}




测试类:



public class CommonExamplePowerMockTest {

@Test

public void testCallArgumentInstance() {

    File file = PowerMockito.mock(File.class);

    CommonExample commonExample = new CommonExample();

    PowerMockito.when(file.exists()).thenReturn(true);

    Assert.assertTrue(commonExample.callArgumentInstance(file));

}

}




普通Mock方式是外部传递Mock参数,基本上和单独使用Mockito是一样的,使用纯Mockito的api也可以完成这个测试。



#### [](
)2\. Mock方法内部new出来的对象



public class CommonExample {

public boolean callArgumentInstance(String path) {

    File file = new File(path);

    return file.exists();

}

}


@RunWith(PowerMockRunner.class)

@PrepareForTest(CommonExample.class)

public class CommonExamplePowerMockTest {

    @Test

    public void callCallArgumentInstance2() throws Exception {

        File file = PowerMockito.mock(File.class);

        CommonExample commonExample = new CommonExample();

        PowerMockito.whenNew(File.class).withArguments("aaa").thenReturn(file);

        PowerMockito.when(file.exists()).thenReturn(true);

        Assert.assertTrue(commonExample.callArgumentInstance("aaa"));

    }

} 

```



跟前面有一点区别的就是,这里要测试的方法内部创建了File对象,这时需要通过`PowerMockito.whenNew(File.class).withArguments("aaa").thenReturn(file)`方法模拟创建File的操作,当File类以aaa的参数创建的时候返回已经mock出来的file对象。同时这时需要在测试类上添加注解`@RunWith(PowerMockRunner.class)`和`@PrepareForTest(CommonExample.class)`,注意是在类上面添加,不是在方法上,一开始在方法上添加时提示找不到测试方法,`@PrepareForTest()`括号里面指定的是要测试的目标类。



#### [](
)3\. Mock普通对象的final方法



```

public class CommonExample {

    public boolean callFinalMethod(DependencyClass dependency) {

        return dependency.isValidate();

    }

}



public class DependencyClass {

    public final boolean isValidate() {

        // do something

        return false;
 
  开发测试 最新文章
pytest系列——allure之生成测试报告(Wind
某大厂软件测试岗一面笔试题+二面问答题面试
iperf 学习笔记
关于Python中使用selenium八大定位方法
【软件测试】为什么提升不了?8年测试总结再
软件测试复习
PHP笔记-Smarty模板引擎的使用
C++Test使用入门
【Java】单元测试
Net core 3.x 获取客户端地址
上一篇文章      下一篇文章      查看所有文章
加:2021-08-28 09:38:55  更:2021-08-28 09:39:50 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/17 22:25:13-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码