最新消息:天气越来越冷,记得加一件厚衣裳

PowerMock – 快速入门

Java w3sun 2441浏览 1评论

引言

Bug(程序错误),是程序设计中的术语,是指在软件运行中因为程序本身有错误而造成的功能不正常、死机、数据丢失、非正常中断等现象。有些程序错误会造成计算机安全隐患,此时叫做漏洞。
Bug Free是借鉴微软的研发流程和Bug管理理念,使用PHP+MySQL独立写出的一个Bug管理 系统。简单实用、免费并且开放源代码(遵循GNU GPL)。 命名BugFree 有两层意思:一是希望软件中的缺陷越来越少直到没有,Free嘛;二是表示它是免费且开放源代码的,大家可以自由使用传播。
单元测试(英语:Unit Testing)又称为模块测试,是针对程序模块(软件设计的最小单位)来进行正确性检验的测试工作。程序单元是应用的最小可测试部件。在过程化编程中,一个单元就是单个程序、函数、过程等;对于面向对象编程,最小单元就是方法,包括基类(超类)、抽象类、或者派生类(子类)中的方法。

什么是Power Moc

PowerMock是一个Java模拟框架,通过提供定制的类加载器以及一些字节码篡改技巧的应用,PowerMock 现了对静态方法、构造方法、私有方法以及 Final 方法的模拟支持,对静态初始化过程的移除等强大的功能。可用于解决通常认为很难甚至无法测试的测试问题。使用PowerMock可以模拟静态方法,删除静态初始化程序,允许模拟而不依赖于注入等。PowerMock 通过在执行测试时在运行时修改字节码来完成这些技巧。PowerMock还包含一些实用程序,可更轻松地访问对象的内部状态。
没有Mock框架以前大都通过衍生对象来进行Mock,随后出现了JMock、Easy Mock、Mockito等Mock框架,Power Mock基于Eeasy Mock和Mockito框架来实现,目前基于Power Mock和JUit来进行单元测试的公司还是比较多。

Power Mock Maven依赖

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<dependencies>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
<version>1.6.5</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito</artifactId>
<version>1.6.5</version>
<scope>test</scope>
</dependency>
</dependencies>
<dependencies> <dependency> <groupId>org.powermock</groupId> <artifactId>powermock-module-junit4</artifactId> <version>1.6.5</version> <scope>test</scope> </dependency> <dependency> <groupId>org.powermock</groupId> <artifactId>powermock-api-mockito</artifactId> <version>1.6.5</version> <scope>test</scope> </dependency> </dependencies>
<dependencies>
    <dependency>
        <groupId>org.powermock</groupId>
        <artifactId>powermock-module-junit4</artifactId>
        <version>1.6.5</version>
        <scope>test</scope>
    </dependency>

    <dependency>
        <groupId>org.powermock</groupId>
        <artifactId>powermock-api-mockito</artifactId>
        <version>1.6.5</version>
        <scope>test</scope>
    </dependency>
</dependencies>

Power Mock 快速入门

开始使用Power Mock编写单元测试以前可以对比下常规JUnit单元测试、Mockito和Power Mock的区别与相似的地方。

常规单元测试

常规单元测试采用JUnit编写,其中可以存在较重的Dao层。

Book
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Book {
private String name;
private Double price;
}
@Data @NoArgsConstructor @AllArgsConstructor public class Book { private String name; private Double price; }
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Book {
  private String name;
  private Double price;
}
BookDao
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
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 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 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
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
@Data
@NoArgsConstructor
@AllArgsConstructor
public class BookService {
private BookDao bookDao;
public int count() {
return bookDao.count();
}
public void save(Book book){
bookDao.insert(book);
}
}
@Data @NoArgsConstructor @AllArgsConstructor public class BookService { private BookDao bookDao; public int count() { return bookDao.count(); } public void save(Book book){ bookDao.insert(book); } }
@Data
@NoArgsConstructor
@AllArgsConstructor
public class BookService {
  private BookDao bookDao;

  public int count() {
     return bookDao.count();
  }

  public void save(Book book){
    bookDao.insert(book);
  }
}
BookServiceWithJUnit
Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
public class BookServiceTestWithJUnit {
private BookService bookService;
@Before
public void setup() {
BookDao bookDao = new BookDao();
bookService = new BookService(bookDao);
}
@Test
public void count() {
try {
int count = bookService.count();
Assert.fail("Procedure will not be here.");
} catch (Exception e) {
Assert.assertTrue(e instanceof UnsupportedOperationException);
}
}
@Test
public void save() {
try {
Book book = new Book("C++ Primer", 20.0);
BookDao bookDao = new BookDao();
BookService bookService = new BookService(bookDao);
bookService.save(book);
Assert.fail("Procedure will not be here.");
} catch (Exception e) {
Assert.assertTrue(e instanceof UnsupportedOperationException);
}
}
}
public class BookServiceTestWithJUnit { private BookService bookService; @Before public void setup() { BookDao bookDao = new BookDao(); bookService = new BookService(bookDao); } @Test public void count() { try { int count = bookService.count(); Assert.fail("Procedure will not be here."); } catch (Exception e) { Assert.assertTrue(e instanceof UnsupportedOperationException); } } @Test public void save() { try { Book book = new Book("C++ Primer", 20.0); BookDao bookDao = new BookDao(); BookService bookService = new BookService(bookDao); bookService.save(book); Assert.fail("Procedure will not be here."); } catch (Exception e) { Assert.assertTrue(e instanceof UnsupportedOperationException); } } }
public class BookServiceTestWithJUnit {
  private BookService bookService;

  @Before
  public void setup() {
    BookDao bookDao = new BookDao();
    bookService = new BookService(bookDao);
  }

  @Test
  public void count() {
    try {
      int count = bookService.count();
      Assert.fail("Procedure will not be here.");
    } catch (Exception e) {
      Assert.assertTrue(e instanceof UnsupportedOperationException);
    }
  }

  @Test
  public void save() {
    try {
      Book book = new Book("C++ Primer", 20.0);
      BookDao bookDao = new BookDao();
      BookService bookService = new BookService(bookDao);
      bookService.save(book);
      Assert.fail("Procedure will not be here.");
    } catch (Exception e) {
      Assert.assertTrue(e instanceof UnsupportedOperationException);
    }
  }
}

从上图可以看到单元测试顺利通过并且断言成功,这是目前常用的最简单的方式,无论测试类轻重都可以通过代码实现,只是偶尔略显繁琐。

Mockito单元测试

Book,BookDao和BookService我们直接使用上面示例中的即可,重点是Mockito的使用。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
public class BookServiceTestWithMockito {
/**
* 初始化Mock对象,代替不使用注解的场景中的Mockito.mock(xxx.class)以达到精简代码的目的.
*/
@Mock
private BookDao bookDao;
@Before
public void setup() {
//参数this指代所写的测试类
MockitoAnnotations.initMocks(this);
}
@Test
public void count() {
//录制bookDao的行为以便后续进行回放,具体含义为:当bookDao执行count()时返回一个数值20.
Mockito.when(bookDao.count()).thenReturn(20);
//创建Service实例
BookService bookService = new BookService(bookDao);
//执行bookService.count()时会在其中回放第一步录制的行为,也就是说会拿到bookDao.count()返回的值20.
int count = bookService.count();
//断言
Assert.assertEquals(20, count);
}
@Test
public void save() {
//新建Book实例
Book book = new Book("C++ Primer", 20.9);
//录制bookDao的行为以便后续进行回放,insert为void类型,没有返回值。由于具体含义为:当bookDao执行insert(book)什么也不做.
Mockito.doNothing().when(bookDao).insert(book);
//创建Service实例
BookService bookService = new BookService(bookDao);
//执行保存操作
bookService.save(book);
//验证Mock BookDao对象insert方法没有被调用
Mockito.verify(bookDao).insert(book);
}
}
public class BookServiceTestWithMockito { /** * 初始化Mock对象,代替不使用注解的场景中的Mockito.mock(xxx.class)以达到精简代码的目的. */ @Mock private BookDao bookDao; @Before public void setup() { //参数this指代所写的测试类 MockitoAnnotations.initMocks(this); } @Test public void count() { //录制bookDao的行为以便后续进行回放,具体含义为:当bookDao执行count()时返回一个数值20. Mockito.when(bookDao.count()).thenReturn(20); //创建Service实例 BookService bookService = new BookService(bookDao); //执行bookService.count()时会在其中回放第一步录制的行为,也就是说会拿到bookDao.count()返回的值20. int count = bookService.count(); //断言 Assert.assertEquals(20, count); } @Test public void save() { //新建Book实例 Book book = new Book("C++ Primer", 20.9); //录制bookDao的行为以便后续进行回放,insert为void类型,没有返回值。由于具体含义为:当bookDao执行insert(book)什么也不做. Mockito.doNothing().when(bookDao).insert(book); //创建Service实例 BookService bookService = new BookService(bookDao); //执行保存操作 bookService.save(book); //验证Mock BookDao对象insert方法没有被调用 Mockito.verify(bookDao).insert(book); } }
public class BookServiceTestWithMockito {
  /**
   * 初始化Mock对象,代替不使用注解的场景中的Mockito.mock(xxx.class)以达到精简代码的目的.
   */
  @Mock
  private BookDao bookDao;

  @Before
  public void setup() {
    //参数this指代所写的测试类
    MockitoAnnotations.initMocks(this);
  }

  @Test
  public void count() {
    //录制bookDao的行为以便后续进行回放,具体含义为:当bookDao执行count()时返回一个数值20.
    Mockito.when(bookDao.count()).thenReturn(20);
    //创建Service实例
    BookService bookService = new BookService(bookDao);
    //执行bookService.count()时会在其中回放第一步录制的行为,也就是说会拿到bookDao.count()返回的值20.
    int count = bookService.count();
    //断言
    Assert.assertEquals(20, count);
  }

  @Test
  public void save() {
    //新建Book实例
    Book book = new Book("C++ Primer", 20.9);
    //录制bookDao的行为以便后续进行回放,insert为void类型,没有返回值。由于具体含义为:当bookDao执行insert(book)什么也不做.
    Mockito.doNothing().when(bookDao).insert(book);
    //创建Service实例
    BookService bookService = new BookService(bookDao);
    //执行保存操作
    bookService.save(book);
    //验证Mock BookDao对象insert方法没有被调用
    Mockito.verify(bookDao).insert(book);
  }
}

使用Mockito不仅省去了我们new各种实例的繁琐操作,而且生成的mock对象可以复用。在无法访问依赖服务的情况下需要测试的对象可以安装提前录制好的行为在调用时进行回放,结合断言可以预测返回结果。

Power Mock单元测试

同样Book,BookDao和BookService直接使用上面示例中的即可,此次采用Power Mock进行测试。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
public class BookServiceTestWithPowerMock {
//普通 Mock 不需要加 @RunWith 和 @PrepareForTest 注解。
@Test
public void count() {
//Mock对象,也可以使用 org.mockito.Mock 注解标记来实现
BookDao bookDao = PowerMockito.mock(BookDao.class);
//录制bookDao的行为以便后续进行回放,具体含义为:当bookDao执行count()时返回一个数值15.
PowerMockito.when(bookDao.count()).thenReturn(15);
//创建Service实例
BookService bookService = new BookService(bookDao);
//执行bookService.count()时会在其中回放第一步录制的行为,也就是说会拿到bookDao.count()返回的值20.
int count = bookService.count();
Assert.assertEquals(15, count);
}
@Test
public void save() {
//创建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);
BookService bookService = new BookService(bookDao);
//执行保存操作
bookService.save(book);
//验证Mock BookDao对象insert方法没有被调用
Mockito.verify(bookDao).insert(book);
}
}
public class BookServiceTestWithPowerMock { //普通 Mock 不需要加 @RunWith 和 @PrepareForTest 注解。 @Test public void count() { //Mock对象,也可以使用 org.mockito.Mock 注解标记来实现 BookDao bookDao = PowerMockito.mock(BookDao.class); //录制bookDao的行为以便后续进行回放,具体含义为:当bookDao执行count()时返回一个数值15. PowerMockito.when(bookDao.count()).thenReturn(15); //创建Service实例 BookService bookService = new BookService(bookDao); //执行bookService.count()时会在其中回放第一步录制的行为,也就是说会拿到bookDao.count()返回的值20. int count = bookService.count(); Assert.assertEquals(15, count); } @Test public void save() { //创建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); BookService bookService = new BookService(bookDao); //执行保存操作 bookService.save(book); //验证Mock BookDao对象insert方法没有被调用 Mockito.verify(bookDao).insert(book); } }
public class BookServiceTestWithPowerMock {
  //普通 Mock 不需要加 @RunWith 和 @PrepareForTest 注解。
  @Test
  public void count() {
    //Mock对象,也可以使用 org.mockito.Mock 注解标记来实现
    BookDao bookDao = PowerMockito.mock(BookDao.class);
    //录制bookDao的行为以便后续进行回放,具体含义为:当bookDao执行count()时返回一个数值15.
    PowerMockito.when(bookDao.count()).thenReturn(15);
    //创建Service实例
    BookService bookService = new BookService(bookDao);
    //执行bookService.count()时会在其中回放第一步录制的行为,也就是说会拿到bookDao.count()返回的值20.
    int count = bookService.count();
    Assert.assertEquals(15, count);
  }

  @Test
  public void save() {
    //创建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);
    BookService bookService = new BookService(bookDao);
    //执行保存操作
    bookService.save(book);
    //验证Mock BookDao对象insert方法没有被调用
    Mockito.verify(bookDao).insert(book);
  }
}

通过对比可能发现Mockito和Power Mock API使用起来差别不大,但是为什么选择Power Mock而不是直接用Mockito呢?PowerMock可以模拟静态方法,删除静态初始化程序,允许模拟而不依赖于注入等,而Mockito则不支持某些操作,比如对final修饰类的Mock等。本次通过对比先熟悉下Power Mock的由来与简单使用,后续将从变量、方法、常用类与接口方面详细介绍。

转载请注明:雪后西塘 » PowerMock – 快速入门

发表我的评论
取消评论

表情

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

网友最新评论 (1)

  1. 看起来很牛逼,感谢博主分享。
    jm5年前(2019-10-26)回复