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. Python-控制流

    if #!/usr/bin/python number=23 guess=int(input('Enter an interger:')) #input返回的结果是string类型,需要用int()转 ...

  2. VMware中linux安装jdk

    首先安装linux系统 如何将jdk安装包复制到linux中不做概述,可以使用xftp工具,或者Xshell,或者其他方式. 1.下载jdk包:本章使用的为后缀为tar.gz的文件(不需要安装),如j ...

  3. OPPO R11 R11plus系列 解锁BootLoader ROOT Xposed 你的手机你做主

    首先准备好所有要使用到的文件 下载链接:https://share.weiyun.com/5WgQHtx 步骤1. 首先安装驱动 解压后执行 Install.bat 部分电脑需要禁用驱动程序签名才可以 ...

  4. div 背景放图和直接放图区别

    <html> <head> <meta charset="UTF-8"> <title></title> <sty ...

  5. Emmet(Zen Coding)语法规则简介

    ———Emmet(Zen Coding)语法规则简介——— [Zen Coding可谓快速开发HTML和CSS的利器,主要采用仿css类选择器方式编写代码,以下是该利器的基本语法规则和代码示例] 基础 ...

  6. hibernate_04_hbm.xml介绍

    先贴上类文件Students.hbm.xml <?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC & ...

  7. Deutsch lernen (09)

    1. die Rückmeldung, -en 反馈,回馈:(销假)报到 die Rückmeldung zur Arbeit 2. dringend a. 紧急的,急切的 Ich brauche d ...

  8. python tips:最内嵌套作用域规则,闭包与装饰器

    在作用域与名字空间提到,python是静态作用域,变量定义的位置决定了变量作用的范围.变量沿着local,global,builtins的路径搜索,直觉上就是从里到外搜索变量,这称为最内嵌套作用域规则 ...

  9. 【笔记】Linux就该这么学-第六课第四章

    vim使用    a 在光标后一位置插入    i 在光标当前位置插入    o 在光标下面创建个空行    dd 删除(剪切)光标所在行    5dd 删除(剪切)从光标处开始的5行    yy 复 ...

  10. UIAutomator定位简介

    UIAutomator元素定位是 Android 系统原生支持的定位方式,虽然与 xpath 类似,但比它更加好用,且支持元素全部属性定位.定位原理是通过android 自带的android uiau ...