最开始项目中是没有单元测试的,基本都是本身经过各类方式来实现测试的。好比修改代码,测完再改回来;再好比直接模拟用户操做,直接当黑盒测试,而后本身去看相应的逻辑有没有,状态有没有改变。html
这些方式有几个缺点:框架
测试不完整,挖有一些隐藏的坑ide
改代码测试,在该回来的时候可能引入新bug单元测试
手工测试比较耗时测试
下次改需求时,须要再次手工测试this
这个里面屡次手工测试比较难受,太浪费时间了。之前因为一个逻辑牵扯比较多,构造对象比较复杂,仅仅用JUnit写测试的工做量仍是太大,因此单元测试一直没有进行下去。后来引入的mockito框架来用于新代码的测试,powermock用于之前的代码测试。下面将介绍一下mockito和powermock框架,就明白为何要用这两个框架了。spa
mockito是用的比较广的mock框架。mock技术的目的和做用是模拟一些在应用中不容易构造或者比较复杂的对象,从而把测试与测试边界之外的对象隔离开。为了说明使用方法,先引入一下基本对象code
public class User { private int userId; private ComplexObject complexObject; public int getUserId() { return userId; } //... construction getter }
public class Service { public boolean checkUser(User user) { if(user.getUserId() < 100){ return true; } return false; } }
默认的static importhtm
import static org.mockito.Mockito.*; import static org.junit.Assert.*;
若是要测试Service#checkUser
方法,咱们就要构造User对象。假设ComplexObject构造很复杂,若是不用mock,测试将步履维艰。下面来看看mockito是如何构造一个假的User并进行测试的吧。对象
@Test public void testCheckUser() throws Exception { Service service = new Service(); User user = mock(User.class); when(user.getUserId()).thenReturn(2); boolean checkResult = service.checkUser(user); assertTrue(checkResult); }
上面能够看到只用mock方法就能够了,而后设置一下getUserId方法的返回就好了。when的语法理解很容易,就不解释了。上面的when语句也能够换成
doReturn(2).when(user).getUserId();
在这个例子中,这两种when的写法都是可行的。一共有如下几种方式来模拟一个方法。
doReturn
doCallRealMethod
doNothing
doThrow
doAnswer
固然也有thenXXX这种形式。
spy和mock很像,都是模拟一个对象。可是mock是把全部方法都接管了,spy是默认调用对象的方法。若是先mock出一个对象,而后对每个方法调用doCallRealMethod
,这就至关于spy出一个对象。
因此spy和mock只是初始模拟对象的默认设置不同而已,其余行为都是同样的。
能够直接用注解来实现mock:
@Mock User user; @Before public void initMocks() { MockitoAnnotations.initMocks(this); } @Test public void testCheckUser() throws Exception { Service service = new Service(); doReturn(2).when(user).getUserId(); boolean checkResult = service.checkUser(user); assertTrue(checkResult); }
这个须要调用initMocks(this)
来注入,这里是经过@Before
,也能够经过@RunWith
来调用initMocks
方法。
也能够用Spy注解:
@Spy User user;
还有一个注解比较有用@InjectMocks
,这个能够把对象注入到其余对象中去的。
下面稍微添加一下代码:
public class ComplexObject { @Override public String toString() { return "Complex lhcpig"; } //... }
public class Service { public String handleUser(User user){ return user.getComplexObject() + ""; } //... }
public class TestService { @InjectMocks User user; @Spy ComplexObject complexObject; @Before public void initMocks() { MockitoAnnotations.initMocks(this); } @Test public void testHandleUser() throws Exception { Service service = new Service(); String s = service.handleUser(user); assertThat(s, is("Complex lhcpig")); } //... }
注1:这里和以前的那个test有冲突,由于User的注解不同,因此第一个test会报NotAMockException或者MissingMethodInvocationException异常。
注2:这里用Spy,能够不用额外代码,就CallRealMethod。
这个是用来判断方法是否被调用,调用是否超时,调用了多少次等测试。
@Test public void testCheckUser() throws Exception { Service service = new Service(); when(user.getUserId()).thenReturn(2); boolean checkResult = service.checkUser(user); assertTrue(checkResult); verify(user).getUserId(); verify(user, timeout(100)).getUserId(); user.getUserId(); verify(user, times(2)).getUserId(); }
若是方法有参数,也能够验证参数。
这里只是简介,若是想详细了解Mockito,建议仍是看官网文档。
Mockito不支持final方法,私有方法,静态方法,而PowerMock支持。因此这里也要介绍一下。可是仍是不建议项目中使用,若是须要使用PowerMock才能测试,说明代码的可测试性很差,须要改进代码。通常都是历史遗留代码或者第三方库相关测试的时候才须要使用。
下面是使用方式
@RunWith(PowerMockRunner.class) @PrepareForTest( { YourClassWithEgStaticMethod.class }) public class YourTestCase { ... }
给个例子,你们就理解了
@RunWith(PowerMockRunner.class) @PrepareForTest( { Service.class }) public class TestService { @Before public void initMocks() { mockStatic(Service.class); } @Test public void testTestStaticFinal() throws Exception { PowerMockito.when(Service.testStaticFinal()).thenReturn("mock1"); assertEquals("mock1", Service.testStaticFinal()); } @Test public void testPrivate() throws Exception { Service t = mock(Service.class); PowerMockito.when(t, "testPrivate").thenReturn("xxx"); doCallRealMethod().when(t).testPrivateForPublic(); assertEquals("xxx", t.testPrivateForPublic()); } @Test public void testTestPrivateWithArg() throws Exception { Service t = spy(new Service()); String arg = "dd"; PowerMockito.when(t, "testPrivateWithArg", arg).thenReturn("lhc"); assertEquals("lhc", t.getTestPrivateWithArg(arg)); } }
public class Service { public static final String testStaticFinal() { System.out.println("testStaticFinal"); return "static final"; } private String testPrivate() { System.out.println("testPrivate"); return "private"; } public String testPrivateForPublic() { System.out.println("testPrivateForPublic"); return testPrivate(); } private String testPrivateWithArg(String arg) { System.out.println("testPrivateWithArg"); return arg + "x"; } }
私有方法用PowerMock测试后,若是要修更名字就会很麻烦,重构起来也可能会影响测试用例。所PowerMock的正确使用方式是尽可能不使用。
由于要反射调用私有方法,因此写法没有mockito那么优雅。我这里使用的是基于Mockito的PowerMock,因此能够混合使用,好比上面用到的spy,when等。固然PowerMock还有基于其余mock框架(EasyMock)的扩展,这里就再也不进一步介绍了。
想让测试更加高效,测试框架仍是其次,写出可测试性的代码才是最重要的。