单元测试是对软件或者程序的基本(最小)组成单元的测试。单元的测试是可重复执行、执行速度快、独立无依赖、结果不改变。通过写单元测试,我们可以快速验证代码正确性,及早发现程序问题。

测试示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import org.junit.*;
import static org.junit.Assert.fail;

public class ClassNameTest {
@BeforeClass //公开表态无返回值
public static void beforeClass() throws Exception{
//每次测试类执行前执行一次,主要用来初使化公共资源等
}
@AfterClass //公开表态无返回值
public static void afterClass() throws Exception{
//每次测试类执行完成后执行一次,主要用来释放资源或清理工作
}
@Before
public void setup() throws Exception {
//每个测试案例执行前都会执行一次
}
@After
public void teardown() throws Exception {
//每个测试案例执行完成后都会执行一次
}
@Test
public void testMethodName_give_…_when_…_then_…() {
fail("失败");
}
}

常用注解

  • @Ignore

    注解标记的测试方法在测试中会被忽略

  • @Test

    1
    2
    @Test(expected=xxxException.class)   断言该方法会抛出异常
    @Test(timeout=1000) 执行时间超过设置的值该案例会失败
  • @RunWith

  • 1
    2
    3
    4
    @RunWith(Suite.class)                 测试集运行器配合使用测试集功能
    @RunWith(JUnit4.class) 默认运行器
    @RunWith(Parameterized.class) 参数化运行器
    @RunWith(Suite.class)
  • @Mock和@Spy

断言

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
assertEquals(a, b)    测试a是否等于b(a和b是原始类型数值(primitive value)或者必须为实现比较而具有equal方法)

assertFalse(a) 测试a是否为false(假),a是一个Boolean数值。

assertTrue(a) 测试a是否为true(真),a是一个Boolean数值

assertNotNull(a) 测试a是否非空,a是一个对象或者null。

assertNull(a) 测试a是否为null,a是一个对象或者null。

assertNotSame(a, b) 测试a和b是否没有都引用同一个对象。

assertSame(a, b) 测试a和b是否都引用同一个对象。

fail(string) Fail让测试失败,并给出指定信息。

assertThat(expected, Matcher) 通过Matcher断言

Hamcrest :greaterThan,greaterThanOrEqualTo,lessThan,anything,anyOf,containsString

Mock

mock就是在测试过程中,对于某些不容易构造或者不容易获取的对象,用一个虚拟的对象来创建以便测试的测试方法。这个虚拟的对象就是mock对象,mock对象就是真实对象在调试期间的代替品。

mockito

  1. mock对象使用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    //通过api创建

    public class UserServiceTest {
    private UserService userService;
    private UserDao mockUserDao;
    @Before
    public void setUp() {
    mockUserDao = mock(UserDao.class);
    userService = new UserServiceImpl();
    userService.setUserDao(mockUserDao);
    }
    //通过注解
    public class UserServiceTest {
    @InjectMocks
    private UserServiceImpl userService;
    @Mock
    private UserDao mockUserDao;
    @Before
    public void setUp() {
    MockitoAnnotations.initMocks(this);
    }
    }
  2. Mock常用方法

    • verify

      1
      2
      3
      4
      verify(mock, never()).add("twice");        //验证add方法没有被调用
      verify(mock, times(2)).add("twice"); //验证add方法被调用了2次
      verify(mock, atLeast(n)).someMethod(); //方法至少被调用n次
      verify(mock, atMost(n)).someMethod(); //方法最多被调用n次
    • when

      1
      2
      3
      4
      when(mock.someMethod()).thenReturn(value1).thenReturn(value2);  //设置方法返回值,第一次返回value1,第二次返回value2
      when(mock.get(0)).thenReturn("first");
      when(mock.get(1)).thenThrow(new RuntimeException());
      when(mock.get(anyInt())).thenReturn("element");
    • spy

      1
      2
      3
      List spy = spy(new LinkedList());
      when(spy.get(0)).thenReturn(“foo");
      doReturn("foo").when(spy).get(0);
  3. Mock VS Spy

    Mock需要制定mock类型类(接口或者实现类),生成Mock类,其中所有的方法都不是真实的方法,而且返回值都是Null

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    public class Service {
    public String service() {
    System.out.print("invoked!");
    return "real service!";
    }
    }
    Service mock = Mockito.mock(Service.class);
    System.out.println(mock.service()); //null
    when(mock.service()).thenReturn("mock");
    System.out.println(mock.service()); //mock

    Spy 机制可以监视一个真实的对象,这时对它进行方法调用将执行真实的方法,同时也可以打桩指定的方法。总结来说, Spy机制提供了部分mock的能力。

    1
    2
    Service spy = Mockito.spy(new Service());
    System.out.println(mock.service()); //inovked!real service!

powermock

使用powermock可以通过修改字节码的方式模拟final/static/private/系统类的功能。

  1. mock new关键字

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    Class LocalServiceImpl implements LocalService{
    @Override
    public Node getLocalNode(int num, String name) {
    return new Node(num, name);
    }
    /**
    * mock new关键字
    */
    @Test
    @PrepareForTest(LocalServiceImpl.class) //PrepareForTest修改local类的字节码以覆盖new的功能
    public void testNew() throws Exception {
    Node target = new Node(1, "name");
    //当传入任意int且name属性为"name"时,new对象返回为target
    //当参数条件使用了any系列方法时,剩余的参数都得使用相应的模糊匹配规则,如eq("name")代表参数等于"name"
    //剩余还有isNull(), isNotNull(), isA()等方法
    PowerMockito.whenNew(Node.class).withArguments(anyInt(), eq("name")).thenReturn(target);
    Node result = localService.getLocalNode(2, "name");
    assertEquals(target, result); //返回值为target
    assertEquals(1, result.getNum());
    assertEquals("name", result.getName());
    //未指定name为"test"的返回值,默认返回null
    Node result2 = localService.getLocalNode(1, "test");
    assertNull(result2);
    }
    }
  2. Mock final方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    //RemoteServiceImpl
    @Override
    public final Node getFinalNode() {
    return new Node(1, "final node");
    }

    /**
    * mock final方法
    */
    @Test
    @PrepareForTest(RemoteServiceImpl.class) //final方法在RemoteServiceImpl类中
    public void testFinal() {
    Node target = new Node(2, "mock");
    PowerMockito.when(remoteService.getFinalNode()).thenReturn(target); //指定返回值

    Node result = remoteService.getFinalNode(); //直接调用final方法,返回mock后的值
    assertEquals(target, result); //验证返回值
    assertEquals(2, result.getNum());
    assertEquals("mock", result.getName());
    }
  3. mock static方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    //Node
    public static Node getStaticNode() {
    return new Node(1, "static node");
    }

    /**
    * mock static方法
    */
    @Test
    @PrepareForTest(Node.class) //static方法定义在Node类中
    public void testStatic() {
    Node target = new Node(2, "mock");
    PowerMockito.mockStatic(Node.class); //mock static方法前需要加这一句
    PowerMockito.when(Node.getStaticNode()).thenReturn(target); //指定返回值

    Node result = Node.getStaticNode(); //直接调用static方法,返回mock后的值
    assertEquals(target, result); //验证返回值
    assertEquals(2, result.getNum());
    assertEquals("mock", result.getName());
    }
  4. Mock private方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    //RemoteServiceImpl
    @Override
    public Node getPrivateNode() {
    return privateMethod();
    }

    //RemoteServiceImpl
    private Node privateMethod() {
    return new Node(1, "private node");
    }

    /**
    * mock 私有方法
    */
    @Test
    @PrepareForTest(RemoteServiceImpl.class) //private方法定义在RemoteServiceImpl类中
    public void testPrivate() throws Exception {
    Node target = new Node(2, "mock");
    //按照真实代码调用privateMethod方法
    PowerMockito.when(remoteService.getPrivateNode()).thenCallRealMethod();
    //私有方法无法访问,类似反射传递方法名和参数,此处无参数故未传
    PowerMockito.when(remoteService, "privateMethod").thenReturn(target);

    Node result = remoteService.getPrivateNode();
    assertEquals(target, result); //验证返回值
    assertEquals(2, result.getNum());
    assertEquals("mock", result.getName());
    }
  5. Mock系统方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    //RemoteServiceImpl
    @Override
    public Node getSystemPropertyNode() {
    return new Node(System.getProperty("abc"));
    }

    /**
    * mock 系统类方法
    */
    @Test
    @PrepareForTest(RemoteServiceImpl.class) //类似new关键字,系统类方法的调用在类RemoteServiceImpl中,所以这里填的是RemoteServiceImpl
    public void testSystem() {
    PowerMockito.mockStatic(System.class); //调用的是系统类的静态方法,所以要加这一句
    PowerMockito.when(System.getProperty("abc")).thenReturn("mock"); //设置System.getProperty("abc")返回"mock"
    PowerMockito.when(remoteService.getSystemPropertyNode()).thenCallRealMethod(); //设置mock对象调用实际方法

    Node result = remoteService.getSystemPropertyNode(); //按代码会返回一个name属性为"mock"的对象
    assertEquals(0, result.getNum()); //int默认值为0
    assertEquals("mock", result.getName()); //remoteService对象中调用System.getProperty("abc")返回的是上面设置的"mock"
    }