前言
PowerMock
是一个单元测试框架,能够模仿静态方法,公有办法和final办法等来简化单元测试的编写。本篇文章将联合简略例子对PowerMock
的罕用办法进行阐明。
筹备工作
一. 注解增加与应用场景
在应用PowerMock
时须要针对不同场景增加对应注解,次要是@RunWith
和@PrepareForTest
注解。注解增加和场景对应如下所示。
场景 | 注解 |
---|---|
模仿final办法 | @PrepareForTest ,@RunWith |
模仿静态方法 | @PrepareForTest ,@RunWith |
模仿公有办法 | @PrepareForTest |
应用whenNew | @PrepareForTest ,@RunWith |
二. 应用PowerMock须要增加的依赖
须要引入的依赖如下所示。
<dependency> <groupId>org.mockito</groupId> <artifactId>mockito-core</artifactId> <version>2.23.0</version> <scope>test</scope></dependency><dependency> <groupId>org.powermock</groupId> <artifactId>powermock-api-mockito2</artifactId> <version>2.0.2</version> <scope>test</scope></dependency><dependency> <groupId>org.powermock</groupId> <artifactId>powermock-module-junit4</artifactId> <version>2.0.2</version> <scope>test</scope></dependency>
引入mockito-core
是为了提供Mockito
性能,次要应用到org.mockito.ArgumentMatchers
参数占位符,局部状况须要应用到org.mockito.BDDMockito
。引入powermock-api-mockito2
和powermock-module-junit4
是为了提供PowerMock
性能,其中powermock-module-junit4
中还引入了hamcrest-core
,次要是应用其提供的org.hamcrest.MatcherAssert.assertThat
和org.hamcrest.Matchers.is
进行断言判断。
在引入依赖时,须要留神核查Mockito
和PowerMock
的版本对应关系,否则会报java.lang.ClassNotFoundException: org.mockito.exceptions.Reporter
谬误。版本对应关系能够去PowerMock
官网进行查问:PowerMock官网,通常状况下,如果引入的mockito-core
版本为2.x,则PowerMock
的api须要应用powermock-api-mockito2
。
注释
一. mock public办法
public class Mock { public boolean isTrue_1() { return true; }}public class PowerMockTest { @Test public void mockPublic() { Mock mock = PowerMockito.mock(Mock.class); PowerMockito.when(mock.isTrue_1()).thenReturn(false); assertThat(mock.isTrue_1(), is(false)); }}
mock public办法时须要应用PowerMockito.mock(办法所在类.class)
获取mock进去的对象,这里称之为mock实例,mock实例的办法均为假办法,不对mock实例进行任何操作的状况下,调用mock实例的办法会返回(如果有返回值的话)返回值类型的默认值(零值,比方String
返回null,Integer
返回0)。如果想要调用mock实例的办法时使其执行实在办法,那么打桩时须要应用thenCallRealMethod()
,如下所示。
public class Mock { public boolean isTrue_1() { return true; }}public class PowerMockTest { @Test public void mockPublicThenCallRealMethod() { Mock mock = PowerMockito.mock(Mock.class); PowerMockito.when(mock.isTrue_1()).thenCallRealMethod(); assertThat(mock.isTrue_1(), is(true)); }}
同时能够应用whenNew()
来实现在程序中new一个对象时失去一个mock实例,如下所示。
public class Mock { public boolean isTrue_1() { return true; }}@RunWith(PowerMockRunner.class)@PrepareForTest(Mock.class)public class PowerMockTest { @Test public void mockWhenNew() throws Exception { Mock mock_1 = PowerMockito.mock(Mock.class); PowerMockito.when(mock_1.isTrue_1()).thenReturn(false); PowerMockito.whenNew(Mock.class).withAnyArguments().thenReturn(mock_1); Mock mock_2 = new Mock(); assertThat(mock_2.isTrue_1(), is(false)); }}
下面的例子中实现了在调用Mock
对象的构造函数时返回预置好的mock实例(下面例子中为mock_1),因而mock_2实例理论指向了mock_1实例,调用mock_2.isTrue_1()办法时会返回false。
二. mock final public办法
和mock public办法操作统一。如下所示。
public class Mock { public final boolean isTure_2() { return true; }}@RunWith(PowerMockRunner.class)@PrepareForTest(Mock.class)public class PowerMockTest { @Test public void mockFinalPublic() { Mock mock = PowerMockito.mock(Mock.class); PowerMockito.when(mock.isTure_2()).thenReturn(false); assertThat(mock.isTure_2(), is(false)); }}
三. mock private办法
public class Mock { public boolean isTrue_3() { return returnTrue_1(); } private boolean returnTrue_1() { return true; }}@RunWith(PowerMockRunner.class)@PrepareForTest(Mock.class)public class PowerMockTest { @Test public void mockPrivate() throws Exception { Mock mock = PowerMockito.mock(Mock.class); PowerMockito.when(mock, "returnTrue_1").thenReturn(false); PowerMockito.when(mock.isTrue_3()).thenCallRealMethod(); assertThat(mock.isTrue_3(), is(false)); }}
mock private办法打桩时,须要应用PowerMockito.when(mock实例,"公有办法名").thenReturn(冀望返回值)
的模式设置mock实例的公有办法的返回值,如果公有办法有参数,还须要在公有办法名前面增加参数占位符,比方PowerMockito.when(mock实例,"公有办法名",anyInt()).thenReturn(冀望返回值)
。下面例子中进行断言时,调用公有办法采取了调用公共办法来间接调用公有办法的模式,单元测试代码对业务代码造成了入侵,因而如果仅仅只是为了验证一个公有办法,能够应用Whitebox
来不便的调用公有办法,如下所示。
public class Mock { private boolean returnTrue_1() { return true; }}@RunWith(PowerMockRunner.class)@PrepareForTest(Mock.class)public class PowerMockTest { @Test public void mockPrivate() throws Exception { Mock mock = PowerMockito.mock(Mock.class); PowerMockito.when(mock, "returnTrue_1").thenReturn(false); assertThat(Whitebox.invokeMethod(mock, "returnTrue_1"), is(false)); }}
四. mock static public办法
通常是针对工具类进行mock。如下所示。
public class Mock { public static boolean isTrue_4() { return true; }}@RunWith(PowerMockRunner.class)@PrepareForTest(Mock.class)public class PowerMockTest { @Test public void mockStaticPublic() { PowerMockito.mockStatic(Mock.class); PowerMockito.when(Mock.isTrue_4()).thenReturn(false); assertThat(Mock.isTrue_4(), is(false)); }}
五. mock static private办法
public class Mock { public static boolean isTrue_5() { return returnTrue_2(); } private static boolean returnTrue_2() { return true; }}@RunWith(PowerMockRunner.class)@PrepareForTest(Mock.class)public class PowerMockTest { @Test public void mockStaticPrivate() throws Exception { PowerMockito.mockStatic(Mock.class); PowerMockito.when(Mock.class, "returnTrue_2").thenReturn(false); PowerMockito.when(Mock.isTrue_5()).thenCallRealMethod(); assertThat(Mock.isTrue_5(), is(false)); }}
同样也能够应用Whitebox
来不便的调用动态公有办法,如下所示。
public class Mock { private static boolean returnTrue_2() { return true; }}@RunWith(PowerMockRunner.class)@PrepareForTest(Mock.class)public class PowerMockTest { @Test public void mockStaticPrivate() throws Exception { PowerMockito.mockStatic(Mock.class); PowerMockito.when(Mock.class, "returnTrue_2").thenReturn(false); assertThat(Whitebox.invokeMethod(Mock.class, "returnTrue_2"), is(false)); }}
六. Whitebox
应用Whitebox
能够不便的设置对象(动态)公有属性值,如下所示。
public class Mock { private boolean flag = true; public boolean isTrue_6() { return flag; }}public class PowerMockTest { @Test public void whiteboxPrivateField() { Mock mock = new Mock(); Whitebox.setInternalState(mock, "flag", false); assertThat(mock.isTrue_6(), is(false)); }}
仅应用Whitebox
时不须要增加@RunWith
和@PrepareForTest
注解,同时对于下面例子如果flag是动态变量,那么设置动态变量值时须要应用Whitebox.setInternalState(Mock.class, "flag", false)
。应用Whitebox
也能够不便的调用对象(动态)公有办法,如下所示。
public class Mock { private boolean isTrue_7() { return true; }}public class PowerMockTest { @Test public void whiteboxPrivateMethod() throws Exception { Mock mock = new Mock(); assertThat(Whitebox.invokeMethod(mock, "isTrue_7"), is(true)); }}
对于下面例子,如果isTrue_7()是动态公有办法,那么调用动态公有办法时的语句为:assertThat(Whitebox.invokeMethod(Mock.class, "isTrue_7").is(true))
。
七. Answer-mock
针对同一办法屡次被调用且不同入参须要mock不同出参的状况,能够应用Answer
,如下所示。
public class Mock { public String isTrue_8(int num) { return ""; }}@RunWith(PowerMockRunner.class)@PrepareForTest(Mock.class)public class PowerMockTest { @Test public void answer() { Mock mock = PowerMockito.mock(Mock.class); Answer<String> answer = new Answer<String>() { @Override public String answer(InvocationOnMock invocation) { int num = (Integer) invocation.getArguments()[0]; if (num == 0) { return "zero"; } else if (num == 1) { return "one"; } return null; } }; PowerMockito.when(mock.isTrue_8(anyInt())).thenAnswer(answer); assertThat(mock.isTrue_8(0), is("zero")); assertThat(mock.isTrue_8(1), is("one")); }}
其中Answer
的泛型类型须要与answer()
办法的返回值类型统一,且通过InvocationOnMock
的getArguments()
能够获取mock实例调用的办法所有入参,callRealMethod()
能够调用实在办法,getMethod()
能够获取mock实例调用的办法,getMock()
能够获取mock实例。同时,还能够应用org.mockito.BDDMockito.given
来实现雷同的成果,如下所示。
public class Mock { public String isTrue_8(int num) { return ""; }}@RunWith(PowerMockRunner.class)@PrepareForTest(Mock.class)public class PowerMockTest { @Test public void answer() { Mock mock = PowerMockito.mock(Mock.class); Answer<String> answer = new Answer<String>() { @Override public String answer(InvocationOnMock invocation) { int num = (Integer) invocation.getArguments()[0]; if (num == 0) { return "zero"; } else if (num == 1) { return "one"; } return null; } }; given(mock.isTrue_8(anyInt())).willAnswer(answer); assertThat(mock.isTrue_8(0), is("zero")); assertThat(mock.isTrue_8(1), is("one")); }}
八. spy public办法
public class Spy { public boolean isTrue_1() { return true; } public boolean isTrue_2() { return true; }}public class SpyTest() { @Test public void spyPublic() { Spy spy = PowerMockito.spy(new Spy()); PowerMockito.doReturn(false).when(spy).isTrue_1(); assertThat(spy.isTrue_1(), is(false)); assertThat(spy.isTrue_2(), is(true)); }}
spy public办法时须要应用PowerMockito.spy(办法所在类的实例)
获取spy进去的对象,这里称之为spy实例,不对spy实例进行任何操作的状况下,spy实例与实在实例是齐全一样的。同时因为spy实例与实在实例齐全一样,因而在对spy实例进行打桩时应用doReturn()
和thenReturn()
是存在差异的:应用doReturn(返回值)
时不会执行实在形式,间接返回返回值;应用thenReturn(返回值)
时会先执行一遍实在办法,而后返回返回值。通常状况下spy须要配合doReturn()
应用,用于克制实在办法的执行,避免执行实在办法时报错。
同时,打桩时应用doReturn()
和thenReturn()
的语法存在差异,下面例子中打桩时如果应用的语句为PowerMockito.doReturn(false).when(spy.isTrue_1())
,会导致编译时失常,运行时报错的景象。下表对打桩时doReturn()
和thenReturn()
的语法进行了比照。
应用场景 | doReturn() | thenReturn() |
---|---|---|
打桩public办法 | PowerMockito.doReturn(false).when(spy).isTrue_1() | PowerMockito.when(spy.isTrue_1()).thenReturn(false) |
打桩private办法 | PowerMockito.doReturn(false).when(spy, "returnTrue_1") | PowerMockito.when(spy, "returnTrue_1").thenReturn(false) |
打桩static办法 | PowerMockito.doReturn(false).when(Spy.class, "isTrue_5") | PowerMockito.when(Spy.isTrue_5()).thenReturn(false) |
打桩static private办法 | PowerMockito.doReturn(false).when(Spy.class, "returnTrue") | PowerMockito.when(Spy.class, "returnTrue").thenReturn(false) |
最初,spy也和mock一样,能够配合whenNew()
进行应用。
九. spy private办法
public class Spy { public boolean isTrue_4() { return returnTrue_1(); } private boolean returnTrue_1() { return true; }}@RunWith(PowerMockRunner.class)@PrepareForTest(Spy.class)public class SpyTest() { @Test public void spyPrivate() throws Exception { Spy spy = PowerMockito.spy(new Spy()); PowerMockito.doReturn(false).when(spy, "returnTrue_1"); assertThat(spy.isTrue_4(), is(false)); }}
十. spy static public办法
public class Spy { public static boolean isTrue_5() { return true; }}@RunWith(PowerMockRunner.class)@PrepareForTest(Spy.class)public class SpyTest() { @Test public void spyStaticPublic() throws Exception { PowerMockito.spy(Spy.class); PowerMockito.doReturn(false).when(Spy.class, "isTrue_5"); assertThat(Spy.isTrue_5(), is(false)); }}
十一. spy static private办法
public class Spy { public static boolean isTrue_6() { return returnTrue(); } private static boolean returnTrue_2() { return true; }}@RunWith(PowerMockRunner.class)@PrepareForTest(Spy.class)public class SpyTest() { @Test public void spyStaticPrivate() throws Exception { PowerMockito.spy(Spy.class); PowerMockito.doReturn(false).when(Spy.class, "returnTrue_2"); assertThat(Spy.isTrue_6(), is(false)); }}
十二. Answer-spy
public class Spy { public String isTrue_7(int num) { return ""; }}@RunWith(PowerMockRunner.class)@PrepareForTest(Spy.class)public class SpyTest() { @Test public void answer() { Spy spy = PowerMockito.spy(new Spy()); Answer<String> answer = new Answer<String>() { @Override public String answer(InvocationOnMock invocation) { int num = (Integer) invocation.getArguments()[0]; if (num == 0) { return "zero"; } else if (num == 1) { return "one"; } return ""; } }; PowerMockito.doAnswer(answer).when(spy).isTrue_7(anyInt()); assertThat(spy.isTrue_7(0), is("zero")); assertThat(spy.isTrue_7(1), is("one")); } @Test public void given_when_then_bdd() { Spy spy = PowerMockito.spy(new Spy()); Answer<String> answer = new Answer<String>() { @Override public String answer(InvocationOnMock invocation) { int num = (Integer) invocation.getArguments()[0]; if (num == 0) { return "zero"; } else if (num == 1) { return "one"; } return ""; } }; given(spy.isTrue_7(anyInt())).willAnswer(answer); assertThat(spy.isTrue_7(0), is("zero")); assertThat(spy.isTrue_7(1), is("one")); }}
总结
正当应用PowerMock
能够解决单元测试编写中的一些常见难题,在解依赖时能够帮忙咱们躲避例如数据库,kafka等与内部存在交互的组件的影响。