美文网首页
单元测试总结

单元测试总结

作者: 63e29c663713 | 来源:发表于2016-06-13 20:29 被阅读724次

JUnit

JUnit 常用注解:

  • @Test : 表示要执行此区域内的测试代码
  • @BeforeClass : 在开始第一个测试前执行
  • @AfterClass : 全部测试执行完毕后执行
  • @Before : 每次测试前执行
  • @After : 每次测试后执行
  • @Ignore : 标注了@Ignore注解的测试脚本将不会被执行,也可用于 class 层
  • @RunWith

@Test

@Test 有两个属性,分别是expected()和timeout()。

  • expected():校验测试脚本抛出指定类型的异常
  • timeout():测试脚本的运行时间是否满足要求
// 期待抛出IndexOutOfBoundsException.class
@Test(expected=IndexOutOfBoundsException.class) 
public void sample_assert_exception(){
  int[] sampleArr = new int[]{1, 2, 3};
  int indexOutOfBoundNumber = sampleArr[3];
}

// 脚本运行时间小于1000毫秒
@Test(timeout = 1000) 
public void testTimeoutFailed() throws InterruptedException {
  Thread.sleep(2000);
}

@RunWith

基于JUnit的测试脚本都是依靠Runner去解析和执行,@Test等所有注解的执行顺序、生命周期也都是由Runner决定的,所以Runner是JUnit的核心。
JUnit 中默认的 Runner 是 org.junit.runners.BlockJUnit4ClassRunner,由于我们使用 Spring,所以选用 SpringJUnit4ClassRunner,能够更加方便的引用配置文件

Assert

常用方法:

  • assertEquals
  • assertSame
  • assertNotSame
  • assertNull
  • assertNotNull
  • assertTrue
  • assertFalse
  • assertArrayEquals

## Hamcrest
Hamcrest 即为 matchers 的异位构词,它提供了更加强大的 assert 功能,所有验证都是通过AssertThat(actual, matcher)来做校验。

由于 Hamcrest 引入了 matcher 的概念,我们可以进行更多的组合,同时可读性更高。Hamcrest 如此好用,以至于 JUnit 已经将其吸纳进去。如果你现在用的 JUnit 是4.4之后的版本,那你已经有了 Hamcrest。

org.hamcrest.Matchers 中定义了大量的 static 方法,用于生成各种 Matcher。

  1. 字符串相关 API:
  • startsWith
  • endsWith
  • containsString
  1. Iterable 相关 API:
  • contains
  • hasItem
  • containsInAnyOrder
  • hasSize
  1. Map 相关 API:
  • hasKey
  • hasValue
  • hasEntry
  1. 条件相关 API:
  • allOf
  • anyOf
  • not
  • both

示例:

MatcherAssert.assertThat(number,Matchers.greaterThan(5));
MatcherAssert.assertThat(text, Matchers.startsWith("Hello"));
MatcherAssert.assertThat(array, Matchers.hasItem("World"));

同时为了提高可读性,可将方法进行 import.

assertThat(number, greaterThan(5));
assertThat(text, startsWith("Hello"));
assertThat(array, hasItem("World"));

Mock 测试

Mock 测试就是在测试过程中,对于某些不容易构造(如 HttpServletRequest 必须在Servlet 容器中才能构造出来)或者不容易获取比较复杂的对象(如 JDBC 中的ResultSet 对象),用一个虚拟的对象(Mock 对象)来创建以便测试的测试方法。

Mock 最大的功能是帮你把单元测试的耦合分解开,如果你的代码对另一个类或者接口有依赖,它能够帮你模拟这些依赖,并帮你验证所调用的依赖的行为。

目前,在 Java 阵营中主要的 Mock 测试工具有 Mockito,JMock,EasyMock 等。我们接下来注重介绍 Mockito。

Mockito

Mockito 与 JUnit 不同,并不是单元测试框架,它是用于生成 mock 对象的工具,一个典型的例子就是使用模拟对象来模拟数据库 DAO 层。

现在主流的 Mock Framework 是 Mockito。通常的做法是联合 JUnit + Mockito 来进行测试。

当使用 Spring boot 进行开发时,依赖org.springframework.boot:spring-boot-starter-test即可,test starter 会自动依赖junit、mockito、hamcrest。

mock 对象

 import static org.mockito.Mockito.*;

 //mock creation
 List mockedList = mock(List.class);

 //using mock object
 mockedList.add("one");
 mockedList.clear();

 //verification
 verify(mockedList).add("one");
 verify(mockedList).clear();

stubbing

 LinkedList mockedList = mock(LinkedList.class);

 //stubbing
 when(mockedList.get(0)).thenReturn("first");
 when(mockedList.get(1)).thenThrow(new RuntimeException());

 //following prints "first"
 System.out.println(mockedList.get(0));

 //following throws runtime exception
 System.out.println(mockedList.get(1));

 //following prints "null" because get(999) was not stubbed
 System.out.println(mockedList.get(999));

 verify(mockedList).get(0);

参数匹配器(argument matchers)

如果你正在使用参数的匹配,所有的参数都必须由匹配器来提供。

 //stubbing using built-in anyInt() argument matcher
 when(mockedList.get(anyInt())).thenReturn("element");

 //stubbing using custom matcher (let's say isValid() returns your own matcher implementation):
 when(mockedList.contains(argThat(isValid()))).thenReturn("element");

 //following prints "element"
 System.out.println(mockedList.get(999));

verify

verify 可以使用参数匹配器:

//you can also verify using an argument matcher
 verify(mockedList).get(anyInt());
 
 verify(mock).someMethod(anyInt(), anyString(), eq("third argument"));
//above is correct - eq() is also an argument matcher

verify(mock).someMethod(anyInt(), anyString(), "third argument");
//above is incorrect - exception will be thrown because third argument is given without an argument matcher.

verify 确切的调用次数/at least x / never。times(1) 是默认的,因此,使用的 times(1) 可以显示的省略。

//using mock
 mockedList.add("once");

 mockedList.add("twice");
 mockedList.add("twice");

 mockedList.add("three times");
 mockedList.add("three times");
 mockedList.add("three times");

 //following two verifications work exactly the same - times(1) is used by default
 verify(mockedList).add("once");
 verify(mockedList, times(1)).add("once");

 //exact number of invocations verification
 verify(mockedList, times(2)).add("twice");
 verify(mockedList, times(3)).add("three times");

 //verification using never(). never() is an alias to times(0)
 verify(mockedList, never()).add("never happened");

 //verification using atLeast()/atMost()
 verify(mockedList, atLeastOnce()).add("three times");
 verify(mockedList, atLeast(2)).add("five times");
 verify(mockedList, atMost(5)).add("three times");

Stubbing void 方法

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

//following throws RuntimeException:
mockedList.clear();

doNothing().when(mock).someMethod(); 

按序 verify

// A. Single mock whose methods must be invoked in a particular order
List singleMock = mock(List.class);

//using a single mock
singleMock.add("was added first");
singleMock.add("was added second");

//create an inOrder verifier for a single mock
InOrder inOrder = inOrder(singleMock);

//following will make sure that add is first called with was added first, then with was added second
inOrder.verify(singleMock).add("was added first");
inOrder.verify(singleMock).add("was added second");

// B. Multiple mocks that must be used in a particular order
List firstMock = mock(List.class);
List secondMock = mock(List.class);

//using mocks
firstMock.add("was called first");
secondMock.add("was called second");

//create inOrder object passing any mocks that need to be verified in order
InOrder inOrder = inOrder(firstMock, secondMock);

//following will make sure that firstMock was called before secondMock
inOrder.verify(firstMock).add("was called first");
inOrder.verify(secondMock).add("was called second");

// Oh, and A + B can be mixed together at will

确保指定 mock 上没有发生interactions

//using mocks - only mockOne is interacted
mockOne.add("one");

//ordinary verification
verify(mockOne).add("one");

//verify that method was never called on a mock
verify(mockOne, never()).add("two");

//verify that other mocks were not interacted
verifyZeroInteractions(mockTwo, mockThree);

校验是否有未校验的调用

 //using mocks
 mockedList.add("one");
 mockedList.add("two");

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

 //following verification will fail
 verifyNoMoreInteractions(mockedList);

Stubbing 连续调用

when(mock.someMethod("some arg"))
 .thenThrow(new RuntimeException())
 .thenReturn("foo");
// when(mock.someMethod("some arg")).thenReturn("one", "two", "three"); 

//First call: throws runtime exception:
mock.someMethod("some arg");

//Second call: prints "foo"
System.out.println(mock.someMethod("some arg"));

//Any consecutive call: prints "foo" as well (last stubbing wins).
System.out.println(mock.someMethod("some arg"));

doReturn

  1. thenReturn 不能用來 stub void method
  2. spy 不能搭配 thenReturn
  3. stub same method 多次不能用 thenReturn
  4. doReturn 在編譯期間不做 return value 的型別檢查,thenReturn 會做 type check
  5. thenReturn 會先去執行 stub 的 method,然後改寫掉 return value,如果 method 裡有些變數沒有 mock ,極可能會 null pointer exception

在真实对象上 spy

spy的意思是你可以修改某个真实对象的某些方法的行为特征,而不改变他的基本行为特征。

List list = new LinkedList();
List spy = spy(list);

//optionally, you can stub out some methods:
when(spy.size()).thenReturn(100);

//using the spy calls *real* methods
spy.add("one");
spy.add("two");

//prints "one" - the first element of a list
System.out.println(spy.get(0));

//size() method was stubbed - 100 is printed
System.out.println(spy.size());

//optionally, you can verify
verify(spy).add("one");
verify(spy).add("two");

不过spy在使用的时候有很多地方需要注意,一不小心就会导致问题,所以不到万不得已还是不要用spy。

List list = new LinkedList();
List spy = spy(list);

//Impossible: real method is called so spy.get(0) throws IndexOutOfBoundsException (the list is yet empty)
when(spy.get(0)).thenReturn("foo");

//You have to use doReturn() for stubbing
doReturn("foo").when(spy).get(0);

@Mock vs @InjectMocks

使用注解有利于减少重复代码,增强可读性,易于排查错误。

Mockito支持的注解有:

  • @Mock
  • @Spy
  • @Captor
  • @InjectMocks
  1. 自动生成 Mock 类
    在需要 Mock 的属性上标记@Mock注解,生成Mock类即可。使用@Mock注解来定义mock对象有如下的优点:
  • 方便mock对象的创建
  • 减少mock对象创建的重复代码
  • 提高测试代码可读性
  1. 自动注入Mock类到被测试类
    只要在被测试类上标记 @InjectMocks,Mockito就会自动将标记@Mock、@Spy等注解的属性值注入到被测试类中。
 public class ArticleManagerTest {

       @Mock 
       private ArticleCalculator calculator;
       @Mock 
       private ArticleDatabase database;
       @Mock 
       private UserProvider userProvider;
       
       @InjectMocks
       private ArticleService;
}       

相关文章

  • 单元测试--Android单元测试学习总结(junit+Mock

    原文链接:川峰-Android单元测试学习总结 Android单元测试主要分为以下两种 本地单元测试(Junit ...

  • 2018-06-01 python单元测试

    转载自 unittest单元测试框架总结 unittest单元测试框架不仅可以适用于单元测试,还可以适用WEB自动...

  • Android单元测试总结

    单元测试小总结 ​ 单元测试往往在产品赶着上线的情况下被忽视。然后单元测试往往会节约大量修改bug的时间。还有一点...

  • 一点点的差别

    每逢单元测试,我家小妖不由的有点紧张,我总结为单元测试紧张综合征。当然无一例外成绩也是漂浮不定,考后进行归纳总结,...

  • 2020-04-17

    今天总结了一下systemUI的现有单元测试,以及需要新增写的单元测试。 熟悉Mockito的基本api: ver...

  • 单元测试 (7)-参考网站

    Junit: Android单元测试学习总结 https://blog.csdn.net/lyabc123456...

  • 指南与踩坑:Python 单元测试

    本文试图总结编写单元测试的流程,以及自己在写单元测试时踩到的一些坑。如有遗漏,纯属必然,欢迎补充。 目录概览: 编...

  • 单元测试总结

    JUnit JUnit 常用注解: @Test : 表示要执行此区域内的测试代码 @BeforeClass : 在...

  • 如何高质量的写测试用例?

    一、单元测试用例 单元测试用例有人总结出来了编写用例的3A原则,分别是 1、Arrange: 初始化测试对象或者准...

  • 关于Vue单元测试的一些总结

    最近Vue项目要求用到单元测试,就这段时间用到的一些单元测试知识做一下总结。 TDD和BDD的概念 1.TDD(T...

网友评论

      本文标题:单元测试总结

      本文链接:https://www.haomeiwen.com/subject/dfgydttx.html