使用 Mockito 单元测试

目录


1. 需求知识

2. 使用 存根(Stub) 和 模拟对象(Mock Object) 进行测试

2.1. 为什么需要模拟?

2.2. 存根(Stub) vs. 模拟对象 (Mock)

2.3. 行为测试 vs. 状态测试

2.4. 生成模拟对象

3. 模拟框架( Mock Framework)

4. Mockito

4.1. 使用 Mockito 模拟对象

4.2. 使用 Mockito

4.3. Mockito的限制

4.4. 模拟对象的配置

4.5. 验证模拟对象的行为

4.6. Spy

5. Mockito 在 Android 平台测试

5.1. 在 Android 使用 Mockito

5.2. 安装

6. 链接和参考

1.需求知识

该教程需要理解单元测试和熟悉JUnit框架的使用。

如果您不熟悉JUnit,请阅读JUnit教程。

2. 使用 存根(Stub) 和 模拟对象(Mock Object) 进行测试

2.1. 为什么需要模拟?

一个单元测试需要在隔离的环境下执行。如果可以的话需要消除其他依赖的服务影响。但实际上,软件中是充满依赖关系的.我们会基于service类写操作类,而service类又是基于数据访问类(DAOs)的,依次下去.

为了解决这个问题, 可以使用 存根 (Stub) 或者 模拟 (Mock) 对象的方法进行测试。

2.2. 存根(Stub) vs. 模拟对象 (Mock)

存根(Stub)类是实现了一个接口或者抽象类的类,可以在测试过程中使用该类,例如:

一个模拟对象(mock object)是一个接口或者抽象类的虚拟实现。例如:

存根和模拟对象都可以传递给其他的对象进行测试。你的一些单元测试可以测这些类的正确性等。利用存根对象或者模拟对象可以保证测试过程中不受到其他的影响。

存根对象需要自定义实现方法;

模拟对象只需要更少的代码和简单的配置。

以下的内容将详细介绍模拟对象的使用方法。

2.3. 行为测试 vs. 状态测试

Mock 对象允许你对行为进行测试。有一些测试不需要验证结果,但是需要检查某些方法是否被正确的参数调用过。这种测试为行为测试。

状态测试只是关注与结果是否正确,而行为测试能够判断一个应用调用结构以及层次。

2.4. 生成模拟对象

你们可以使用Mock 框架来生成模拟对象。Mock 框架允许你在运行期间创建对象,并且定义它的一些行为。

一个典型的例子就是使用模拟对象来模拟数据库DAO层。在生产环境上是使用运行的数据库,但是在单元测试环境中完全可以用模拟对象来模拟数据,确保单元测试的正确条件。这样就不需要依赖于外部的数据。

3. 模拟框架( Mock Framework)

比较流行的模拟框架有 EasyMock、jMock 和 Mockito。下面的列表是这些框架的链接。
# jMock
http://jmock.org/
# EasyMock
http://easymock.org/
# Mockito
http://mockito.org/

4. Mockito

4.1. 使用 Mockito 模拟对象

Mockito 是比较流行的模拟框架,可以与JUnit 联合起来测试。它允许你进行创建和配置模拟对象。

Mockito的官方网站: Mockito 主页.

4.2. 使用 Mockito

Mockito 支持使用 mock() 静态方法创建模拟对象。

同样也支持 @Mock注解方式,如果使用注解的方式,需要使用在初始化方法调用 MockitoAnnotation.InitMock( this ) 方法

例如,下面的例子就是使用 Mockito 进行对类 ClassToTest 的单元测试。

提示

可以使用静态导入方法调用方法 mock()

4.3. Mockito的限制

Mockito 以下的类型不能进行构造:

  • 终态类(final classes)
  • 匿名类(anonymous classes)
  • 基本数据类型(primitive types)

4.4. 模拟对象的配置

Mockito 可以使用 verify() 方法来确认某些方法是否被调用过.

when(....).thenReturn(....) 结构可以为某些条件给定一个预期的返回值.

同样可以使用doReturn(object).when(kdskfsk).methodCall 结构

4.5. 验证模拟对象的行为

Mockito 跟踪了所有的方法调用和参数的调用情况。verify()可以验证方法的行为。

查看下面的例子:

4.6. Spy

@Spy 或者方法 spy() 可以包含一个真实的对象. 每次调用,除非特出指定,委托给改真实对象的调用.

5. Mockito 在 Android 平台测试

5.1. 在 Android 使用 Mockito

Mockito 同样也可以在安卓平台上进行测试。

5.2. 安装

在 Android 测试项目中使用 Mockito。添加下面的包到Android 测试项目的 libs 目录
https://mockito.googlecode.com/files/mockito-all-1.9.5.jar
http://dexmaker.googlecode.com/files/dexmaker-1.0.jar
http://dexmaker.googlecode.com/files/dexmaker-mockito-1.0.jar
接下来可以在你的测试项目中使用 Mockito 。

6. 链接和参考

Mockito 项目主页
Mockito 的依赖注入功能
Unit tests with Mockito - Tutorial
使用 Mockito 单元测试 – 教程

GSON序列化时排除字段的几种方式


使用transient


这个方法最简单,给字段加上 transient 修饰符就可以了,如下所示:

单元测试用例:

使用Modifier指定


这个方法需要用GsonBuilder定制一个GSON实例,如下所示:

单元测试用例:

使用@Expose注解


注意,没有被 @Expose 标注的字段会被排除,如下所示:

单元测试用例:

使用ExclusionStrategy定制排除策略


这种方式最灵活,下面的例子把所有以下划线开头的字段全部都排除掉:

单元测试用例:

参考链接


GSON序列化时排除字段的几种方式

解决Gson解析Json时,Json 中Key为Java关键字的问题

最近在一个项目中,服务器使用 json 作为返回信息的格式,为了加快开发速度,我使用 Gson 代替 Java 语言中自带的 JSONObject 来解析 json 格式。

当我正在享受 Gson 给我带来的便利的时候,我遇到问题了。

因为 Gson 在转化 json 字符串到对象的时候,要求类的成员变量的名称必须和 json 中的 key 值相同。但是郁闷的是,在封装一个接口的时候发现,json 字符串中的 key 为 Java 的关键字。

按照 Gson 的约定,就必须编写一个变量名为new的类,但是结果可想而知,编译不通过。

幸好,Google 已经为我们想好了解决办法,只要对该变量添加Field Naming Support就可以了。

到此,就完美地解决了我们遇到的问题。

引用链接 解决Gson解析Json时,Json 中Key为Java关键字的问题

Android Studio(JAVA) 编译警告:使用了未经检查或不安全的操作

在编译Android Studio(JAVA)程序时发生了如下的警告:

使用了未经检查或不安全的操作
要了解详细信息,请使用 "-Xlint:unchecked" 重新编译。

  • 如何设置Android Stuido开启 "-Xlint:unchecked"

修改build.gradle

增加

修改后的如下:

  • 警告信息的处理例子

包含-Xlint:unchecked警告的代码

消除警告后的代码如下:

同样的方法适用于"-Xlint:deprecation"。

升级到OS X Yosemite后 Android Studio启动崩溃 Symbol not found: _CGContextSetAllowsAcceleration

Mac OS 升到Yosemite后, 打开 Android Studio报

解决方法,是重新安装Apple的Java支持:
Apple官网下载或者本站下载

Ubuntu 14使用Nexus2.x为Maven3.x搭建私服构件仓库

一、下载与安装Nexus

想为Maven搭建私服,我们可以选择使用Nexus工具,目前已知提供war包的版本为2.2.0,其下载地址为:http://www.sonatype.org/nexus/go

39e36edc-a0d2-3b49-afd8-d0e4a175eb5a

这里我们下载War包,直接部署到Tomcat 7 下面,Ubuntu 下面是 /var/lib/tomcat7/webapps。
接下来我们可以在浏览器中输入http://127.0.0.1:8080/nexus/查看Nexus是否成功.

如果提示错误,则到 /var/log/tomcat7/localhost.YYYY-MM-DD.log 下面查看日志,有可能报告 /usr/share/tomcat7/sonatype-work 权限问题,则说明Nexus 没有权限创建这个目录,因此我们需要手工创建这个目录,然后赋予这个目录 777权限,然后重启 Tomcat.

目前最新的版本Nexus 2.12.0-01,(Nexus OSS官网下载2.x的Tomcat/WAR版本) 。3.x版本已经不提供WAR包下载了,只能是使用Jetty的版本了。

Ubuntu下面的操作如下:

修改bin/jsw/conf/wrapper.conf,设置wrapper.java.command=jdk所在目录/bin/java

修改后的内容如下:

设置目录的权限

启动服务

这种安装模式下,访问的链接地址为http://localhost:8081/nexus

注意,首次启动的耗时时间很长,貌似需要建立数据库等,需要耐心等待几分钟。

如果成功,则出现下面的界面

39e36edc-a0d2-3b49-afd8-d0e4a175eb5a

通过上图我们可以发现我们已经成功的启动了Nexus,那么接下来要做的事情就更有意思了,在后续章节中笔者会陆续讲到应该如何使用Nexus工具来配置和管理咱们的私服仓库。

二、仓库类型

在上一章节中,笔者讲解了如何下载与安装Nexus工具。那么本章节咱么来看看如何使用Nexus工具配置和管理私服仓库。当然在使用Nexus之前你是需要登录的,缺省账号密码为:

account:admin;

password:admin123;

当成功登录后,你可以单击 Repositories属性看到如下页面:
d5600d88-1881-37aa-a4e3-d4e7c12bbf29

由于admin这个账号是属于Administrator权限的,你也可以通过单击Security属性进行账户的配置:

9a6d675e-b13b-327d-b1cb-3c05132db8a7

Nexus的仓库类型一共有4种,每一种仓库类型均对应有不同的操作方式:

1、group: 仓库组;

2、hosted:宿主;

3、proxy:代理;

4、virtual:虚拟;

来吧,咱们先谈谈仓库组这个概念,一个仓库组类似一个仓库集合,它和仓库之间是一对多的关系,也就是说一个仓库组可以允许包含多个仓库,Nexus仓库用例图如下:

96308ff7-be2f-38a3-bc0b-63c8056b1999

在咱们弄明白仓库组的作用后,咱们接着来看宿主仓库是干什么的。其实所谓宿主仓库可以理解为自定义仓库,用于存放一些在中央仓库无法下载的构件,比如自定义构件你可以手动将自定义构件上传至hosted中

代理仓库起到的作用是代理作用,假设本地仓库向私服进行请求时,首先会去私服仓库的本地路径中寻找构件,如果没有找到则会从中央仓库进行下载。

虚拟仓库省略...

 三、使用Nexus管理私服仓库

在了解Nexus的4种仓库类型后,咱们接下来要做的事情就是使用Nexus工具来管理咱们的私服仓库。先来看看Nexus为我们提供的一些缺省仓库:

ac73186e-3406-3004-8849-fab7ad41f8d9从上图我们可以看出Nexus缺省为我们定义了1个仓库组,7个仓库。当中最主要的几个仓库为:

9461f779-b4b9-3b03-b616-5d1ad4c8f494

3rd party(宿主仓库):用于存放一些在中央仓库无法下载的构件仓库;

Apache Snapshots(代理仓库):代理ApacheMaven仓库快照版本的构件仓库;

Central(代理仓库):代理Maven中央仓库中发布版本构件的仓库;

当然你可以根据项目需要新建仓库组及仓库,但在建立这些私有的仓库之前,咱们还是先来看看如何使用Nexus为咱们提供的缺省构件仓库(其实很多时候你真没必要去新建仓库)。

选择Public Repositories分组,单击configuration选项,你可以为仓库组添加对应的仓库:

b07178d7-64b8-3a7f-8b99-93451450ea92

单击Save按钮保存即可配置完成。这里有一点需要提醒大家的是,仓库的添加顺序直接决定了构件的下载顺序,换句话来说我们应该把需要从中央仓库或者其他远程仓库下载构件的代理仓库添加在最后

当咱们成功将指定的仓库集合添加进仓库组后,接下来我们来为3rd party(宿主仓库)上传自定义构件,所谓自定义构件指的是无法从Maven的中央仓库进行下载的构建。

笔者在此选用的是最简单方便的手动上传构件(当然上传构件至宿主仓库还有一些方式,但笔者还是侧重于最简便的方式)。

单击3rd party(宿主仓库)的Artifact Upload选项,我们首先来上传一个基于Maven项目的自定义构件:773c6508-1474-3853-ba4e-217198d96ee5

最后别忘记了添加构件:

c7016c60-68b4-38b9-afd4-e14fe4730fd4

如果你的构件不是基于Maven的呢?那么你可以选择GAV Parameters属性:61fe1457-f7e3-3452-a30b-886879c7d62b

接下来,需要配置 Maven 来连接到似有仓库,如果需要全局配置,则配置地址为 ~\.m2\setting.xml,如果需要根据工程配置,则需要跟pom.xml在同一个目录下面即可,文件存在则修改,不存在则创建,内容如下

参考 http://gao-xianglong.iteye.com/blog/1735536

Method类invoke方法的使用

例如:

执行obj的method方法(参数为sql),返回String类型结果

args参数可以为空,就是对应方法没有参数

如:

obj可以为空,但必填null,表示同类中的公用方法,

如:

参数args是个Object数组,即使只有一个参数,也要建object数组

如:

注意:obj必须是实例化的对象,如果有对应的方法名,就可以实例化

部分代码如下:


getClass在下面:

newInstance() 的参数版本与无参数版本详解

通过反射创建新的类示例,有两种方式:

以下对两种调用方式给以比较说明:
Class.newInstance() 只能够调用无参的构造函数,即默认的构造函数;
Constructor.newInstance() 可以根据传入的参数,调用任意构造构造函数。Class.newInstance() 抛出所有由被调用构造函数抛出的异常。

Class.newInstance() 要求被调用的构造函数是可见的,也即必须是public类型的;

Constructor.newInstance() 在特定的情况下,可以调用私有的构造函数。

Class A(被调用的示例):

Class B(调用者):

输出结果如下:

说明方法newInstanceByClassNewInstance调用失败,而newInstanceByConstructorNewInstance则调用成功。如果被调用的类的构造函数为默认的构造函数,采用Class.newInstance()则是比较好的选择,一句代码就OK;如果被调用的类带参构造函数、私有构造函数,就需要采用Constractor.newInstance(),两种情况视使用情况而定。
不过Java Totorial中推荐采用Constractor.newInstance()。

参考 http://xiaohuafyle.iteye.com/blog/1607258

Mac Gradle 编译报告Failure initializing default system SSL context

Mac 10.9 版本下使用Gradle 编译 Android 项目,报告错误

使用 -debug 参数,得到的详细输出如下

可以看到,异常信息为

到具体的目录下面去看,这个文件果然是不存在的,有些版本链接到了一个不存在的目录,这种情况一般是经历过系统升级,往往会造成这个现象,另外,就是系统安装的Java 是Apple 提供的 Java 版本,而不是 Orcale 官网下载的独立版本。

解决方法是可以到Orcale 官网下载最新版本的 Java ,安装替换即可解决问题。

Java: Finding/Setting JDK/$JAVA_HOME on Mac OS X

As long as I’ve been using a Mac I always understood that if you needed to set $JAVA_HOME for any program.

On my machine this points to the 1.6 JDK:

This was a bit surprising to me since I’ve actually got Java 7 installed on the machine as well so I’d assumed the symlink would have been changed:

Andres and I were looking at something around this yesterday and wanted to set $JAVA_HOME to the location of the 1.7 JDK on the system if it had been installed.

We eventually came across the following article which explains that you can use the /usr/libexec/java_homecommand line tool to do this.

For example, if we want to find where the 1.7 JDK is we could run the following:

And if we want 1.6 the following does the trick:

We can also list all the JVMs installed on the machine: