单元测试总结

1.软件测试

软件测试是一种用来鉴定软件的正确性、完整性、安全性和质量的过程。主要包括单元测试、集成测试和功能测试等。

1.1 单元测试

主要由开发人员进行,确保程序单元符合预期。

1.2 集成测试

主要由开发人员进行,测试整体程序等功能和性能。

1.3 功能测试

主要由测试人员进行,测试程序是否符合预期。

2.单元测试内容

  1. 模块接口测试
  2. 模块局部数据结构测试
  3. 模块边界条件测试
  4. 模块中所有独立执行通路测试
  5. 模块的各条错误处理通路测试
  6. 模块的非法输入测试
  7. 代码重用测试
  8. 系统兼容测试

3.单元测试最佳实践

3.1 三到五步(Setup,输入,调用,输出,TearDown)

3.2 一致性(任何时候同样的输入需要同样的结果)

反例:

Date date = new Date();
Random.next();

3.3 原子性

所有的测试只有两种结果(成功或失败,不能部分测试通过)

3.4 单一职责

一个测试只验证一个行为

3.5 独立无耦合

  • 单元测试之间无相互调用(单元测试执行顺序无关)
  • 单元测试之间不能共享

3.6 隔离外部调用(不使用不依赖具体、真实的数据,例如:数据库、网络、外部文件等)

3.7 自描述(开发级文档)

3.8 单元测试逻辑

3.9 断言(包含具体的错误信息)

4.Mockito

单元测试开发中,我们经常会遇到测试的类有很多依赖的类、对象、资源,从而形成巨大的依赖树,mock可以模拟外部依赖,适应单元测试。

5.Mockito示例

模拟对象

// 模拟LinkedList 的一个对象  
LinkedList mockedList = mock(LinkedList.class);
// 此时调用get方法,会返回null,因为还没有对方法调用的返回值做模拟
System.out.println(mockedList.get(0)); 

模拟方法调用的返回值

// 模拟获取第一个元素时,返回字符串first。  给特定的方法调用返回固定值在官方说法中称为stub。
when(mockedList.get(0)).thenReturn("first");
// 此时打印输出first
System.out.println(mockedList.get(0)); 

模拟方法调用抛出异常

// 模拟获取第二个元素时,抛出RuntimeException  
when(mockedList.get(1)).thenThrow(new RuntimeException());
// 此时将会抛出RuntimeException  
System.out.println(mockedList.get(1));

如果一个函数没有返回值类型,那么可以使用此方法模拟异常抛出

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

模拟调用方法时的参数匹配

// anyInt()匹配任何int参数,这意味着参数为任意值,其返回值均是element  
when(mockedList.get(anyInt())).thenReturn("element");
// 此时打印是element
System.out.println(mockedList.get(999));

模拟方法调用次数

// 调用add一次
mockedList.add("once"); 
// 下面两个写法验证效果一样,均验证add方法是否被调用了一次
verify(mockedList).add("once");
verify(mockedList, times(1)).add("once"); 

校验行为

// mock creation
 List mockedList = mock(List.class);
 // using mock object
 mockedList.add("one");
 mockedList.clear();
 //verification
 verify(mockedList).add("one");
 verify(mockedList).clear();

模拟方法调用(Stubbing)

//You can mock concrete classes, not just interfaces
 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);

参数匹配

//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));
 //you can also verify using an argument matcher
 verify(mockedList).get(anyInt());
 //argument matchers can also be written as Java 8 Lambdas
 verify(mockedList).add(someString -> someString.length() > 5);

校验方法调用次数

//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");

模拟无返回方法抛出异常

doThrow(new RuntimeException()).when(mockedList).clear();
//following throws RuntimeException:
mockedList.clear();

校验方法调用顺序

// 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

校验方法是否从未调用

//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);

快速创建Mock对象

public class ArticleManagerTest {
   @Mock private ArticleCalculator calculator;
      @Mock private ArticleDatabase database;
      @Mock private UserProvider userProvider;
      @Before
      public void before(){
          MockitoAnnotations.initMocks(this);
      }
}

自定义返回不同结果

when(mock.someMethod("some arg"))
   .thenThrow(new RuntimeException())  // 第一次会抛出异常
   .thenReturn("foo"); // 第二次会返回这个结果
//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")); // 第n次(n> 2),依旧以最后返回最后一个配置

对返回结果进行拦截

when(mock.someMethod(anyString())).thenAnswer(new Answer() {
    Object answer(InvocationOnMock invocation) {
        Object[] args = invocation.getArguments();
        Object mock = invocation.getMock();
        return "called with arguments: " + args;
    }
});
//the following prints "called with arguments: foo"
System.out.println(mock.someMethod("foo"));

Mock函数操作

可以通过doThrow(), doAnswer(), doNothing(), doReturn() and doCallRealMethod() 来自定义函数操作。
暗中调用真实对象

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");

改变默认返回值

Foo mock = mock(Foo.class, Mockito.RETURNS_SMART_NULLS);
Foo mockTwo = mock(Foo.class, new YourOwnAnswer());

捕获函数的参数值

ArgumentCaptor argument = ArgumentCaptor.forClass(Person.class);
verify(mock).doSomething(argument.capture());
assertEquals("John", argument.getValue().getName());

部分Mock

//you can create partial mock with spy() method:
List list = spy(new LinkedList());
//you can enable partial mock capabilities selectively on mocks:
Foo mock = mock(Foo.class);
//Be sure the real implementation is 'safe'.
//If real implementation throws exceptions or depends on specific state of the object then you're in trouble.
when(mock.someMethod()).thenCallRealMethod();

重置Mock

List mock = mock(List.class);
when(mock.size()).thenReturn(10);
mock.add(1);
reset(mock);
//at this point the mock forgot any interactions & stubbing

序列化

List list = new ArrayList();
List spy = mock(ArrayList.class, withSettings()
                 .spiedInstance(list)
                 .defaultAnswer(CALLS_REAL_METHODS)
                 .serializable()); 
  

检查超时

//passes when someMethod() is called within given time span
verify(mock, timeout(100)).someMethod();
//above is an alias to:
verify(mock, timeout(100).times(1)).someMethod();
//passes when som`eMethod() is called *exactly* 2 times within given time span
verify(mock, timeout(100).times(2)).someMethod();
//passes when someMethod() is called *at least* 2 times within given time span
verify(mock, timeout(100).atLeast(2)).someMethod();
//verifies someMethod() within given time span using given verification mode
//useful only if you have your own custom verification modes.
verify(mock, new Timeout(100, yourOwnVerificationMode)).someMethod();

Mock详情

Mockito.mockingDetails(someObject).isMock();
Mockito.mockingDetails(someObject).isSpy();

你可能感兴趣的:(测试)