阅读更多
最近一直在用PowerMock做Unit test的Object mock(对象模拟)。总结了一些经验和发现了一些须要注意事项,俗语说的好“好记性不如烂笔头”,为方便日后参考,于是把它们写下来。
说到PowerMock不得不提EasyMock, 如果日常工作中要实现一些对象的Mock(如,写一些Unit test),大家对EasyMock应该会有所了解, 正是由于EasyMock在复杂情况下的表现不佳,则有了后面的PowerMock。这里简单罗列一下EasyMock一些不足之处:
1) 3.0以前只能Mock interface不能Mock Class。(3.0以前要引放Mock class extension实现Mock class)。
2)不能够Mock 静态方法,final方法,private方法和构造函数。
然后这些不足之处对PowerMock来说是很容易实现的,因为PowerMock实现机制与EasyMock不一样,EasyMock是用的proxy的方式实现在的Mock,而PowerMock则是使用Cglib直接修改的字节码。
现在来说一下一些具体一点用法:
(一) Mock class public method
public class ExampleClass {
public String getMsg(String s){
if ("yes".equals(s)){
return "yes";
} else {
return "no";
}
}
public String getTest(String s){
return this.getMsg(s);
}
}
如上所示类,请见unit teset
@RunWith(PowerMockRunner.class)
@PrepareForTest({ExampleClass.class})
public class ExampleClassTest {
@Test
public void testGetMsg() {
ExampleClass ec = PowerMock.createPartialMock(ExampleClass.class, "getMsg");
EasyMock.expect(ec.getMsg("test")).andReturn("PowerMock");
PowerMock.replay(ec);
assertEquals("PowerMock", ec.getTest("test"));
}
}
本例用PowerMock mock了ExampleClass的部分method getMsg(…),由上看出本类实则有两个方法,另一个是getTest(…)。如果采用如下代码
ExampleClass ec = PowerMock.createMock(ExampleClass.class);
EasyMock.expect(ec.getMsg("test")).andReturn("PowerMock");
后面的ec.getTest(“test”)将会抛异常
java.lang.AssertionError:
Unexpected method call getTest("test"):
因为此时mock对象ec仅被指定有一个方法getMsg。
(二) Mock class static method
public class IdGenerator {
public static long generateNewId() {
return System.currentTimeMillis();
}
}
public class StaticClassSample {
public long registerService(Object service) {
final long id = IdGenerator.generateNewId();
return id;
}
}
@RunWith(PowerMockRunner.class)
@PrepareForTest( {StaticClassSample.class, IdGenerator.class})
public class StaticClassSampleTest {
@Test
public void testRegisterService() throws Exception {
long expectedId = 42;
PowerMock.mockStatic(IdGenerator.class);
EasyMock.expect(IdGenerator.generateNewId()).andReturn(expectedId);
PowerMock.replay(IdGenerator.class);
long actualId = new StaticClassSample().registerService(new Object());
Assert.assertEquals(expectedId, actualId);
PowerMock.verify(IdGenerator.class);
}
}
(三) Mock class private method
public class DataService {
public boolean replaceData(final String dataId, final byte[] binaryData) {
return modifyData(dataId, binaryData);
}
public boolean deleteData(final String dataId) {
return modifyData(dataId, null);
}
private boolean modifyData(final String dataId, final byte[] binaryData) {
return true;
}
}
@RunWith(PowerMockRunner.class)
@PrepareForTest( {DataService.class})
public class DataServiceTest {
@Test
public void testReplaceData() throws Exception {
final String modifyDataMethodName = "modifyData";
final byte[] expectedBinaryData = new byte[] { 42 };
final String expectedDataId = "id";
DataService tested = createPartialMock(DataService.class, modifyDataMethodName);
expectPrivate(tested, modifyDataMethodName, expectedDataId,
expectedBinaryData).andReturn(true);
replay(tested);
Assert.assertTrue(tested.replaceData(expectedDataId, expectedBinaryData));
verify(tested);
}
}
(四) Mock class constructor
public class PersistenceManager {
public boolean createDirectoryStructure(String directoryPath) {
File directory = new File(directoryPath);
if (directory.exists()) {
throw new IllegalArgumentException("/"" + directoryPath + "/" already exists.");
}
return directory.mkdirs();
}
}
@RunWith(PowerMockRunner.class)
@PrepareForTest( PersistenceManager.class )
public class PersistenceManagerTest {
@Test
public void testCreateDirectoryStructure_ok() throws Exception {
final String path = "directoryPath";
File fileMock = PowerMock.createMock(File.class);
PersistenceManager tested = new PersistenceManager();
PowerMock.expectNew(File.class, path).andReturn(fileMock);
EasyMock.expect(fileMock.exists()).andReturn(false);
EasyMock.expect(fileMock.mkdirs()).andReturn(true);
PowerMock.replay(fileMock, File.class);
Assert.assertTrue(tested.createDirectoryStructure(path));
PowerMock.verify(fileMock, File.class);
}
}
上面就是PowerMock的一些比较基本的用法