Java Mocking入门—使用Mockito

2014/03/10 | 分类: 基础技术 | 0 条评论 | 标签: 单元测试

分享到:8
本文由 ImportNew - liken 翻译自 dzone。欢迎加入翻译小组。转载请见文末要求。

我们都会编写单元测试程序,但我们当前所面临的挑战是被测单元可能依赖于其他组件。而对于单元测试配置其他组件,绝对是过枉矫正。相反,我们可以使用Mocks框架代替其他组件,继续进行单元测试。

为了展现如何使用mock框架,我编写一个数据访问层(DAL),该类对应用提供了一套API 接口来访问和修改数据库中的数据。之后在不需要连接数据库的情况下,对DAL类进行单元测试。数据库可以是本地数据库、远程数据库、文件系统或者任何可以储存和检索数据的地方。使用DAL类帮助我们 将数据映射操作从应用代码中分离出来。

使用maven建立一个Java工程。

1
mvn archetype:generate -DgroupId=info.sanaulla -DartifactId=MockitoDemo -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false

以上操作建立了一个MockitoDemo文件夹,然后创建源文件和测试文件的整个目录结构。

考虑下面这个示例模型类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
package info.sanaulla.models;
 
import java.util.List;
 
/**
* Model class for the book details.
*/
public class Book {
 
  private String isbn;
  private String title;
  private List<String> authors;
  private String publication;
  private Integer yearOfPublication;
  private Integer numberOfPages;
  private String image;
 
  public Book(String isbn,
              String title,
              List<String> authors,
              String publication,
              Integer yearOfPublication,
              Integer numberOfPages,
              String image){
 
    this.isbn = isbn;
    this.title = title;
    this.authors = authors;
    this.publication = publication;
    this.yearOfPublication = yearOfPublication;
    this.numberOfPages = numberOfPages;
    this.image = image;
 
  }
 
  public String getIsbn() {
    return isbn;
  }
 
  public String getTitle() {
    return title;
  }
 
  public List<String> getAuthors() {
    return authors;
  }
 
  public String getPublication() {
    return publication;
  }
 
  public Integer getYearOfPublication() {
    return yearOfPublication;
  }
 
  public Integer getNumberOfPages() {
    return numberOfPages;
  }
 
  public String getImage() {
    return image;
  }
}

操作Book模型类的DAL类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
package info.sanaulla.dal;
 
import info.sanaulla.models.Book;
 
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
 
/**
* API layer for persisting and retrieving the Book objects.
*/
public class BookDAL {
 
  private static BookDAL bookDAL = new BookDAL();
 
  public List<Book> getAllBooks(){
      return Collections.EMPTY_LIST;
  }
 
  public Book getBook(String isbn){
      return null;
  }
 
  public String addBook(Book book){
      return book.getIsbn();
  }
 
  public String updateBook(Book book){
      return book.getIsbn();
  }
 
  public static BookDAL getInstance(){
      return bookDAL;
  }
}

当前DAL层以上没有任何功能,我们将对这段代码TDD进行单元测试。DAL层可能与ORM映射API或者数据库API通讯,而我们不关心的这些API是如何设计的。

测试驱动DAL层

对单元测试和Java Mock有很多框架,对这个例子,我将选择JUnit做单元测试,Mockito作为Java mock。我们会在Maven的pom.xml文件中更新依赖属性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 <a href="http://maven.apache.org/maven-v4_0_0.xsd">http://maven.apache.org/maven-v4_0_0.xsd"</a>>
  <modelVersion>4.0.0</modelVersion>
  <groupId>info.sanaulla</groupId>
  <artifactId>MockitoDemo</artifactId>
  <packaging>jar</packaging>
  <version>1.0-SNAPSHOT</version>
  <name>MockitoDemo</name>
  <url>http://maven.apache.org</url>
  <dependencies>
    <!-- Dependency for JUnit -->
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.10</version>
      <scope>test</scope>
    </dependency>
    <!-- Dependency for Mockito -->
    <dependency>
      <groupId>org.mockito</groupId>
      <artifactId>mockito-all</artifactId>
      <version>1.9.5</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
</project>

现在进行BookDAL类的单元测试。单元测试中,我们将注入mock数据到BookDAL类,因此我们可以不依赖数据源就可以完成API的测试。

最初我们有一个空的测试类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class BookDALTest {
  public void setUp() throws Exception {
 
  }
 
  public void testGetAllBooks() throws Exception {
 
  }
 
  public void testGetBook() throws Exception {
 
  }
 
  public void testAddBook() throws Exception {
 
  }
 
  public void testUpdateBook() throws Exception {
 
  }
}

我们将使用下面的setUp()函数把BookDAL类注入mock对象,并设置mock对象数据:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public class BookDALTest {
 
  private static BookDAL mockedBookDAL;
  private static Book book1;
  private static Book book2;
 
  @BeforeClass
  public static void setUp(){
    //Create mock object of BookDAL
    mockedBookDAL = mock(BookDAL.class);
 
    //Create few instances of Book class.
    book1 = new Book("8131721019","Compilers Principles",
            Arrays.asList("D. Jeffrey Ulman","Ravi Sethi", "Alfred V. Aho", "Monica S. Lam"),
            "Pearson Education Singapore Pte Ltd", 2008,1009,"BOOK_IMAGE");
 
    book2 = new Book("9788183331630","Let Us C 13th Edition",
            Arrays.asList("Yashavant Kanetkar"),"BPB PUBLICATIONS", 2012,675,"BOOK_IMAGE");
 
    //Stubbing the methods of mocked BookDAL with mocked data.
    when(mockedBookDAL.getAllBooks()).thenReturn(Arrays.asList(book1, book2));
    when(mockedBookDAL.getBook("8131721019")).thenReturn(book1);
    when(mockedBookDAL.addBook(book1)).thenReturn(book1.getIsbn());
    when(mockedBookDAL.updateBook(book1)).thenReturn(book1.getIsbn());
  }
 
  public void testGetAllBooks() throws Exception {}
 
  public void testGetBook() throws Exception {}
 
  public void testAddBook() throws Exception {}
 
  public void testUpdateBook() throws Exception {}
}

在上面的setUp()方法中,我做了:

1. 创建一个BookDAL的mock对象

1
BookDAL mockedBookDAL = mock(BookDAL.class);

2. 存根带mock数据的BookDAL对象的API,这样无论何时调用API都可以返回mock数据。

1
2
3
4
5
//When getAllBooks() is invoked then return the given data and so on for the other methods.
when(mockedBookDAL.getAllBooks()).thenReturn(Arrays.asList(book1, book2));
when(mockedBookDAL.getBook("8131721019")).thenReturn(book1);
when(mockedBookDAL.addBook(book1)).thenReturn(book1.getIsbn());
when(mockedBookDAL.updateBook(book1)).thenReturn(book1.getIsbn());

填充剩余的测试方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
package info.sanaulla.dal;
 
import info.sanaulla.models.Book;
import org.junit.BeforeClass;
import org.junit.Test;
 
import static org.junit.Assert.*;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
 
import java.util.Arrays;
import java.util.List;
 
public class BookDALTest {
 
  private static BookDAL mockedBookDAL;
  private static Book book1;
  private static Book book2;
 
  @BeforeClass
  public static void setUp(){
    mockedBookDAL = mock(BookDAL.class);
    book1 = new Book("8131721019","Compilers Principles",
            Arrays.asList("D. Jeffrey Ulman","Ravi Sethi", "Alfred V. Aho", "Monica S. Lam"),
            "Pearson Education Singapore Pte Ltd", 2008,1009,"BOOK_IMAGE");
 
    book2 = new Book("9788183331630","Let Us C 13th Edition",
            Arrays.asList("Yashavant Kanetkar"),"BPB PUBLICATIONS", 2012,675,"BOOK_IMAGE");
 
    when(mockedBookDAL.getAllBooks()).thenReturn(Arrays.asList(book1, book2));
    when(mockedBookDAL.getBook("8131721019")).thenReturn(book1);
    when(mockedBookDAL.addBook(book1)).thenReturn(book1.getIsbn());
 
    when(mockedBookDAL.updateBook(book1)).thenReturn(book1.getIsbn());
 
  }
 
  @Test
  public void testGetAllBooks() throws Exception {
 
    List<Book> allBooks = mockedBookDAL.getAllBooks();
    assertEquals(2, allBooks.size());
    Book myBook = allBooks.get(0);
    assertEquals("8131721019", myBook.getIsbn());
    assertEquals("Compilers Principles", myBook.getTitle());
    assertEquals(4, myBook.getAuthors().size());
    assertEquals((Integer)2008, myBook.getYearOfPublication());
    assertEquals((Integer) 1009, myBook.getNumberOfPages());
    assertEquals("Pearson Education Singapore Pte Ltd", myBook.getPublication());
    assertEquals("BOOK_IMAGE", myBook.getImage());
  }
 
  @Test
  public void testGetBook(){
 
    String isbn = "8131721019";
 
    Book myBook = mockedBookDAL.getBook(isbn);
 
    assertNotNull(myBook);
    assertEquals(isbn, myBook.getIsbn());
    assertEquals("Compilers Principles", myBook.getTitle());
    assertEquals(4, myBook.getAuthors().size());
    assertEquals("Pearson Education Singapore Pte Ltd", myBook.getPublication());
    assertEquals((Integer)2008, myBook.getYearOfPublication());
    assertEquals((Integer)1009, myBook.getNumberOfPages());
 
  }
 
  @Test
  public void testAddBook(){
    String isbn = mockedBookDAL.addBook(book1);
    assertNotNull(isbn);
    assertEquals(book1.getIsbn(), isbn);
  }
 
  @Test
  public void testUpdateBook(){
 
    String isbn = mockedBookDAL.updateBook(book1);
    assertNotNull(isbn);
    assertEquals(book1.getIsbn(), isbn);
  }
}

使用maven命令: mvn test进行测试。输出如下:

1
2
3
4
5
6
7
8
9
10
11
-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running info.sanaulla.AppTest
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.029 sec
Running info.sanaulla.dal.BookDALTest
Tests run: 4, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.209 sec
 
Results :
 
Tests run: 5, Failures: 0, Errors: 0, Skipped: 0

至此,通过使用mock框架,在不用实际配置数据源的情况下,就可以测试DAL类。

原文链接: dzone 翻译: ImportNew.comliken
译文链接: http://www.importnew.com/10083.html
转载请保留原文出处、译者和译文链接。]

Java Mocking入门—使用Mockito的更多相关文章

  1. 自学 Java 怎么入门

    自学 Java 怎么入门? 595赞同反对,不会显示你的姓名     给你推荐一个写得非常用心的Java基础教程:java-basic | 天码营 这个教程将Java的入门基础知识贯穿在一个实例中,逐 ...

  2. 《JAVA 从入门到精通》 - 正式走向JAVA项目开发的路

    以前很多时候会开玩笑,说什么,三天学会PHP,七天精通Nodejs,xx天学会xx ... 一般来说,这样子说的多半都带有一点讽刺的意味,我也基本上从不相信什么快速入门.我以前在学校的时候自觉过很多门 ...

  3. Java NIO入门(二):缓冲区内部细节

    Java NIO 入门(二)缓冲区内部细节 概述 本文将介绍 NIO 中两个重要的缓冲区组件:状态变量和访问方法 (accessor). 状态变量是前一文中提到的"内部统计机制"的 ...

  4. 完成《Java编程入门》初稿

    Java编程入门 现在的运维工程师不但要懂得集合网络.系统管理而且要和开发人员一起调试系统,社会上也需要"复合性"的运维人员,所以需要做运维的也要懂一些开发,知道软件系统接口的调试 ...

  5. 三、Android NDK编程预备之Java jni入门创建C/C++共享库

    转自: http://www.eoeandroid.com/thread-264971-1-1.html 应网友回复,答应在两天前要出一篇创建C/C++共享库的,但由于清明节假期,跟朋友出去游玩,丢手 ...

  6. 二、Android NDK编程预备之Java jni入门Hello World

    转自:  http://www.eoeandroid.com/forum.php?mod=viewthread&tid=264543&fromuid=588695 昨天已经简要介绍了J ...

  7. C功底挑战Java菜鸟入门概念干货(一)

    一.认识Java 1.Java 程序比较特殊,它必须先经过编译,然后再利用解释的方式来运行.  2.Byte-codes 最大的好处是——可越平台运行,可让“一次编写,处处运行”成为可能.  3.使用 ...

  8. C功底挑战Java菜鸟入门概念干货(二)

    (接上篇博文:C功底挑战Java菜鸟入门概念干货(一)) 一.Java面向对象程序设计-类的基本形式 1.“类”是把事物的数据与相关的功能封装在一起,形成的一种特殊结构,用以表达对真实世界的一种抽象概 ...

  9. 《Java从入门到精通》src0-8

    public class HelloWorld { public static void main(String[] args) { System.out.println("Hello wo ...

随机推荐

  1. 前端布局神器 display:flex

    前端布局神器display:flex 一直使用flex布局,屡试不爽,但是总是记不住一些属性,这里写来记录一下.   2009年,W3C提出了一种新的方案--Flex布局,可以简便.完整.响应式地实现 ...

  2. File入门及路径名问题

    package com.io.file; import java.io.File; /** * @author 王恒 * @datetime 2017年4月20日 下午2:53:29 * @descr ...

  3. KindEditor文本编辑框的实现

    效果图: kindeditor 是一个插件 下载地址: https://files-cdn.cnblogs.com/files/lxnlxn/kindeditor.zip 解压后将其放在项目的js文件 ...

  4. HTML5 postMessage 和 localStorage 实现窗口间通信

    LocalStorage(不能跨域) 基本思想:通过localStorage的标准事件storage来实现跨页面通信,即页面A通过写入特定数据触发页面B的storage事件,页面B响应之后再写入数据通 ...

  5. css的外边距合并或者外边距塌陷问题

    第一种情况: 已知两个宽和高均为100px,margin均为20px的div垂直排列,现象如下图所示: 当设置css1的margin-bottom:40px:或者css2的margin-top:40p ...

  6. (转)用JS实现表格中隔行显示不同颜色

    用JS实现表格中隔行显示不同颜色 第一种: <style> tr{bgColor:expression(     this.bgColor=((this.rowIndex)%2==0 )? ...

  7. (转)Vue 爬坑之路(一)—— 使用 vue-cli 搭建项目

    vue-cli 是一个官方发布 vue.js 项目脚手架,使用 vue-cli 可以快速创建 vue 项目,GitHub地址是:https://github.com/vuejs/vue-cli 一. ...

  8. Unity引擎GUI之Text

    Text 文本 要显示的字符串. Font 字体 Font Style 加粗与倾斜 Font Size 字体大小 Line Spacing 行距,文本行之间的间距 Rich Text 勾选后,想要单独 ...

  9. RunLoop相关知识

    RunLoop,翻译过来是运行环路.我们在创建命令行项目和创建ios项目时,发现命令行项目当最后一行代码执行完后项目就自动退出了,而ios项目确可以一直运行,知道用户手动点击退出按钮.这就是因为ios ...

  10. servlet-后台获取form表单传的参数

    前台代码: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> & ...