目录
引言
Power Mock提供了强大的Mock能力,其中一个体现就是局部Mock变量,而一般情况下在做单元测试的时候我们无法触碰到局部变量。同时其也支持对私有方法、静态方法、final修饰方法的Mock。
Mock变量
Mock局部变量
为了更好的说明局部变量的Mock,需要对Dao层和Service层稍微做出一些修改:
Book
@Data @NoArgsConstructor @AllArgsConstructor public class Book { private String name; private Double price; }
BookDao
public class BookDao { public int count() { throw new UnsupportedOperationException("BookDao does not support count() operation."); } public void insert(Book book) { throw new UnsupportedOperationException("BookDao does not support insert() operation."); } }
BookService
@Data @NoArgsConstructor public class BookService { public int count() { BookDao bookDao = new BookDao(); return bookDao.count(); } public void save(Book book) { BookDao bookDao = new BookDao(); System.out.println("...BookService save book..."); bookDao.insert(book); } }
BookServiceTestWithPowerMock
//定义使用的Runner类型与需要准备的测试类 @RunWith(PowerMockRunner.class) @PrepareForTest(BookService.class) public class BookServiceTestWithPowerMock { @Test public void count() throws Exception { //Mock对象,也可以使用 org.mockito.Mock 注解标记来实现 BookDao bookDao = PowerMockito.mock(BookDao.class); //录制bookDao的行为以便后续进行回放,具体含义为:当bookDao执行count()时返回一个数值15 PowerMockito.when(bookDao.count()).thenReturn(15); //录制行为,在创建BookDao实例时,不管参数如何都返回上述mock的bookDao PowerMockito.whenNew(BookDao.class).withAnyArguments().thenReturn(bookDao); //创建Service实例 BookService bookService = new BookService(); //执行bookService.count()时会在其中回放第一步录制的行为,也就是说会拿到bookDao.count()返回的值20. int count = bookService.count(); //断言结果,同时verify是否执行了BookDao的count()方法 Assert.assertEquals(15, count); Mockito.verify(bookDao).count(); } @Test public void save() throws Exception { //创建Book实例 Book book = new Book("C++ Primer", 30.5); //Mock对象,也可以使用 org.mockito.Mock 注解标记来实现 BookDao bookDao = PowerMockito.mock(BookDao.class); //录制bookDao的行为以便后续进行回放,insert为void类型,没有返回值。由于具体含义为:当bookDao执行insert(book)什么也不做. PowerMockito.doNothing().when(bookDao).insert(book); //录制行为,在创建BookDao实例时,不管参数如何都返回上述mock的bookDao PowerMockito.whenNew(BookDao.class).withAnyArguments().thenReturn(bookDao); //创建Service实例 BookService bookService = new BookService(); //执行保存操作 bookService.save(book); //验证Mock BookDao对象insert方法没有被调用 Mockito.verify(bookDao).insert(book); } }
可以看到BookDao被成功Mock并且在BookService层的测试中也打印了我们期望的结果。
Mock方法
Mock静态方法
有时候代码中需要调用某个静态工具类指定的方法,如果这个方法非常复杂或者依赖了一些其他组件或者服务, 而我们希望”屏蔽”掉该函数的真实调用情况而代码能够继续运行下去, 那么可以对该静态方法进行Mock。为此我们需要对BookDao做出一些轻微的修改将其中的方法静态化以便我们进行测试。
BookDao
public class BookDao { public static int count() { throw new UnsupportedOperationException("BookDao does not support count() operation."); } public static void insert(Book book) { throw new UnsupportedOperationException("BookDao does not support insert() operation."); } }
BookService
@NoArgsConstructor public class BookService { public int count() { return BookDao.count(); } public void save(Book book) { System.out.println("......BookService save book......"); BookDao.insert(book); } }
BookServiceTestWithPowerMock
/定义使用的Runner类型,同时需要用注释的形式将需要测试的静态方法提供给 PowerMockPrepareForTest可以加在指定的方法上. @RunWith(PowerMockRunner.class) @PrepareForTest({BookService.class, BookDao.class}) public class BookServiceTestWithPowerMock { @Test public void count() { //Mock静态方法所在的类 PowerMockito.mockStatic(BookDao.class); //录制bookDao的行为以便后续进行回放,具体含义为:当bookDao执行count()时返回一个数值10. PowerMockito.when(BookDao.count()).thenReturn(10); //创建Service实例 BookService bookService = new BookService(); //执行bookService.count()时会在其中回放第一步录制的行为,也就是说会拿到bookDao.count()返回的值20. int bookCount = bookService.count(); //断言结果,同时verify是否执行了BookDao的count()方法 Assert.assertEquals(10, bookCount); } @Test public void save() { //创建Book实例 Book book = new Book(); //Mock静态方法所在的类 PowerMockito.mockStatic(BookDao.class); //录制bookDao的行为以便后续进行回放,insert为void类型,没有返回值。由于具体含义为:当bookDao执行insert(book)什么也不做. PowerMockito.doNothing().when(BookDao.class); //创建Service实例 BookService bookService = new BookService(); //执行保存操作 bookService.save(book); //验证Mock BookDao对象insert方法没有被调用 PowerMockito.verifyStatic(); } }
Mock final修饰的方法
Book
@Data @NoArgsConstructor @AllArgsConstructor public class Book { private String name; private Double price; }
BookDao
public class BookDao { public int count() { throw new UnsupportedOperationException("BookDao does not support count() operation."); } public void insert(Book book) { throw new UnsupportedOperationException("BookDao does not support insert() operation."); } public final boolean stored(Book book) { System.out.println("......confirm whether specified book is stored by BookDao......"); return true; } }
BookService
@Data @NoArgsConstructor public class BookService { private BookDao bookDao; public int count() { return bookDao.count(); } public void save(Book book) { System.out.println("......BookService save book......"); bookDao.insert(book); } public final boolean stored(Book book) { System.out.println("......confirm whether specified book is stored by BookService......"); return bookDao.stored(book); } }
BookServiceTestWitPowerMock
@RunWith(PowerMockRunner.class) @PrepareForTest({BookService.class, BookDao.class}) public class BookServiceTestWitPowerMock { @Test public void isStored() { //mock一个Book实例. Book book = PowerMockito.mock(Book.class); //mock一个BookDao实例. BookDao bookDao = PowerMockito.mock(BookDao.class); //录制bookDao的行为以便后续进行回放,具体含义为:当bookDao执行stored()时返回一个数值10. PowerMockito.when(bookDao.stored(book)).thenReturn(false); //mock一个BookService实例. BookService bookService = PowerMockito.mock(BookService.class); //设置service层中的dao. bookService.setBookDao(bookDao); //调用store()判断书籍是否存在. boolean stored = bookService.stored(book); //断言返回结果 Assert.assertFalse(stored); } }
Mock构造方法
构造方法分为有参构造和无参构造两种,分别使用:
PowerMockito.whenNew(X.class).withArguments().thenReturn(X instance)
PowerMockito.whenNew(X.class).withAnyArguments().thenReturn(X instance)或者PowerMockito.whenNew(X.class).withNoArguments().thenReturn(X instance)来实现:
Book
@Data @NoArgsConstructor @AllArgsConstructor public class Book { private String name; private Double price; }
BookDao
public class BookDao { public int count() { throw new UnsupportedOperationException("BookDao does not support count() operation."); } public void insert(Book book) { throw new UnsupportedOperationException("BookDao does not support insert() operation."); } }
BookService
@Data @NoArgsConstructor @AllArgsConstructor public class BookService { private BookDao bookDao; public int count() { return bookDao.count(); } public void save(Book book) { System.out.println("BookService save book."); bookDao.insert(book); } }
BookServiceTestWitPowerMock
//定义使用的Runner类型,同时需要用注释的形式将需要测试的静态方法提供给 PowerMockPrepareForTest可以加在指定的方法上. @RunWith(PowerMockRunner.class) @PrepareForTest({BookService.class, BookDao.class}) public class BookServiceTestWitPowerMock { @Test public void save() throws Exception { String name = "C++ Primer"; Double price = 20.6; //Mock一个BookDao实例. BookDao bookDao = PowerMockito.mock(BookDao.class); //Mock一个Book实例. Book book = PowerMockito.mock(Book.class); //录制行为,在创建Book实例时,根据name和price参数构建一个book实例并返回. PowerMockito.whenNew(Book.class).withArguments(name, price).thenReturn(book); //录制行为,在创建BookDao实例时,不管参数如何都返回上述mock的bookDao. PowerMockito.whenNew(BookDao.class).withAnyArguments().thenReturn(bookDao); //录制bookDao的行为以便后续进行回放,insert为void类型,没有返回值。由于具体含义为:当bookDao执行insert(book)什么也不做. PowerMockito.doNothing().when(bookDao).insert(book); BookService bookService = new BookService(bookDao); //执行保存操作 bookService.save(book); //验证Mock BookDao对象insert方法没有被调用 Mockito.verify(bookDao).insert(book); System.out.println(book.getClass()); System.out.println(bookDao.getClass()); } }
最后打印的BookService层方法信息,Book和BookDao实例信息分别为:
BookService save book. class com.w3sun.mock.method.construct.bean.Book$$EnhancerByMockitoWithCGLIB$$b1ce93f9 class com.w3sun.mock.method.construct.dao.BookDao$$EnhancerByMockitoWithCGLIB$$e7eecb6f
其实PowerMock在mock实例的过程中使用CGLIB进行代理,详细信息可以参考:https://github.com/cglib/cglib/wiki
Mock私有方法
BookService
public class BookService { public boolean exist(String name) { return checkExist(name); } private boolean checkExist(String name) { System.out.println("---BookService checkExist---"); throw new UnsupportedOperationException("UserService checkExist unsupported exception."); } }
BookServiceTestWitPowerMock
//定义使用的Runner类型,同时需要用注释的形式将需要测试的静态方法提供给 PowerMockPrepareForTest可以加在指定的方法上. @RunWith(PowerMockRunner.class) @PrepareForTest(BookService.class) public class BookServiceTestWithPowerMock { @Test public void exist() throws Exception { String arg = "yidian.io"; //如果使用spy且不满足断言的情况下将会调用真正的方法,否则调用mock的方法。 BookService bookService = PowerMockito.spy(new BookService()); //录制行为,在调用BookService的checkExist方法且在传入参数为yidian.io时返回true PowerMockito.doReturn(true) .when(bookService, "checkExist", arg); //调用方法 boolean exist = bookService.exist(arg); //断言结果 Assert.assertTrue(exist); System.out.println("=============="); try { //如果调用了bookService.exist且传入的参数与录入时指定参数不一致时则会调用真实的方法 bookService.exist("yidian"); fail("Process here will be error."); } catch (Exception e) { Assert.assertTrue(e instanceof UnsupportedOperationException); } } }
参考资料
- http://jmock.org/
- http://easymock.org/
- https://site.mockito.org/
- https://junit.org/junit4/
- https://github.com/cglib/cglib
转载请注明:雪后西塘 » PowerMock – 局部变量与方法