热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Androidmockfor循环,Android单元测试(五):依赖注入,将mock方便的用起来

在上一篇文章中,咱们讲了要将mock出来的dependency真正使用起来,须要在测试环境下经过某种方式set到用到它的那个对象里面进去,

在上一篇文章中,咱们讲了要将mock出来的dependency真正使用起来,须要在测试环境下经过某种方式set 到用到它的那个对象里面进去,替换掉真实的实现。咱们前面举的例子是:html

public class LoginPresenter {

private UserManager mUserManager = new UserManager();

public void login(String username, String password) {

//。。。some other code

mUserManager.performLogin(username, password);

}

}

在测试LoginPresenter#login()时,为了可以将mock出来的UserManager set到LoginPresenter里面,咱们前面的作法是简单粗暴,给LoginPresenter加一个UserManager的setter。然而这种作法毕竟不是很优雅,通常来讲,咱们正式代码里面是不会去调用这个setter,修改UserManager这个对象的。所以这个setter存在的意义就纯粹是为了方便测试。这个虽然不是没有必要,却不是太好看,所以在有选择的状况下,咱们不这么作。在这里,咱们介绍依赖注入这种模式。java

对于依赖注入(Dependency Injection,如下简称DI)的准肯定义能够在这里找到。它的基本理念这边简单描述下,首先这是一种代码模式,这个模式里面有两个概念:Client和Dependency。假如你的代码里面,一个类用到了另一个类,那么前者叫Client,后者叫Dependency。结合上面的例子,LoginPresenter用到了UserManager,那么LoginPresenter叫Client,UserManager叫Dependency。固然,这是个相对的概念,一个类能够是某个类的Dependency,倒是另一个类的Client。好比说若是UserManager里面用到了Retrofit,那么相对于Retrofit,UserManager又是Dependency。DI的基本思想就是,对于Dependency的建立过程,并不在Client里面进行,而是由外部建立好,而后经过某种方式set到Client里面。这种模式,就叫作依赖注入。android

是的,依赖注入就是这么简单的一个概念,这边须要澄清的一点是,这个概念自己跟dagger2啊,RoboGuice这些框架并无什么关系。如今不少介绍DI的文章每每跟dagger2是在一块儿的,由于dagger2的使用相对来讲不是很直观,因此致使不少人认为DI是多么复杂的东西,甚至认为只能用dagger等框架来实现依赖注入,其实不是这样的。实现依赖注入很简单,dagger这些框架只是让这种实现变得更加简单,简洁,优雅而已。git

DI的常见实现方式

下面介绍DI的实现方式,一般来讲,这里是大力介绍dagger2的地方。可是,虽然dagger2的确是很是好的东西,然而若是我直接介绍dagger2的话,会很容易致使一个误区,认为在测试的时候,也只能用dagger来作依赖注入或建立对应的测试类,所以,我这边刻意不介绍dagger。先让你们知道最基本的DI怎么实现,而后在测试的时候如何更方便高效的使用。github

实现DI这种模式其实很简单,有多种方式,上一篇文章中提到的setter,其实就是实现DI的一种方式,叫作 setter injection 。此外,经过方法的参数传递进去(argument injection),也是实现DI的一种方式:框架

public class LoginPresenter {

//这里,LoginPresenter再也不持有UserManager的一个引用,而是做为方法参数直接传进去

public void login(UserManager userManager, String username, String password) {

//... some other code

userManager.performLogin(username, password);

}

}

然而更经常使用的方式,是将Dependency做为Client的构造方法的参数传递进去:单元测试

public class LoginPresenter {

private final UserManager mUserManager;

//将UserManager做为构造方法参数传进来

public LoginPresenter(UserManager userManager) {

this.mUserManager = userManager;

}

public void login(String username, String password) {

//... some other code

mUserManager.performLogin(username, password);

}

}

这种实现DI的模式叫 Constructor Injection。其实通常来讲,提到DI,指的都是这种方式。这种方式的好处是,依赖关系很是明显。你必须在建立这个类的时候,就提供必要的dependency。这从某种程度上来讲,也是在说明这个类所完成的功能。所以,尽可能使用 Constructor injection。测试

说到这里,你可能会有一个疑问,若是把依赖都声明在Constructor的参数里面,这会不会让这个类的Constructor参数变得很是多?若是真的发生这种状况了,那每每说明这个类的设计是有问题的,须要重构。为何呢?咱们代码里面的类,通常能够分为两种,一种是Data类,好比说UserInfo,OrderInfo等等。另一种是Service类,好比UserManager, AudioPlayer等等。因此这个问题就有两种状况了:ui

若是Constructor里面传入的不少是基本类型的数据或数据类,那么或许你要作的,是建立一个(或者是另外一个)数据类把这些数据封装一下,这个过程的价值但是大大滴!而不只仅是封装一下参数的问题,有了一个类,不少的方法就能够放到这个类里面了。这点请参考Martin Fowler的《重构》第十章“Introduce Parameter Object”。this

若是传入的不少是service类,那么这说明这个类作的事情太多了,不符合单一职责的原则(Single Responsibility Principle,SRP),所以,须要重构。

接下来讲回咱们的初衷:DI在测试里面的应用。

DI在单元测试里面的应用

所谓DI在单元测试里面的应用,其实说白了就是使用DI模式,将mock出来的Dependency set到Client里面去。我相信这篇文章解释到这里,那么答案也就比较明显了,为了强调咱们要尽可能使用 Constructor injection,对于 setter Injection 和 Argument injection 这边就不作代码示例了。

若是你的代码使用的是 Constructor injection:

public class LoginPresenter {

private final UserManager mUserManager;

//将UserManager做为构造方法参数传进来

public LoginPresenter(UserManager userManager) {

this.mUserManager = userManager;

}

public void login(String username, String password) {

//... some other code

mUserManager.performLogin(username, password);

}

}

其中咱们要测的方法是login(), 要验证login()方法调用了mUserManager的performLigon()。对应的测试方法以下:

public class LoginPresenterTest {

@Test

public void testLogin() {

UserManager mockUserManager = Mockito.mock(UserManager.class);

LoginPresenter presenter = new LoginPresenter(mockUserManager); //建立的时候,讲mock传进去

presenter.login("xiaochuang", "xiaochuang password");

Mockito.verify(mockUserManager).performLogin("xiaochuang", "xiaochuang password");

}

}

很简单,对吧。

小结

这篇文章介绍了DI的概念,以及在单元测试里面的应用,这里特地没有介绍dagger2的使用,目的是要强调:

一个灵活的,易于测试的,符合SRP的,结构清晰的项目,关键在于要应用依赖注入这种模式,而不是用什么来作依赖注入。

等你学会使用dagger之后,要记得在测试的时候,若是能够直接mock dependency并传给被测类,那就直接建立,不是必定要使用dagger来作DI

然而若是彻底不使用框架来作DI,那么在正式代码里面就有一个问题了,那就是dependency的建立工做就交给上层client去处理了,这可不是件好事情。想一想看,LoginActivity里面建立LoginPresenter的时候,还得知道LoginPresenter用了UserManager。而后建立一个UserManager对象给LoginPresenter。对于LoginActivity来讲,它以为我才懒得管你用什么样的UserManager呢,我只想告诉你login的时候,你给我老老实实的login就行了,你用什么Manager我无论。因此,直接在LoginActivity里面建立UserManager,可能不是个好的选择。那怎么样算是一个好的选择呢?dagger2给了咱们答案。

因而下一篇文章咱们介绍dagger2。

最后,若是你也对安卓单元测试感兴趣的话,欢迎加入咱们的交流群:

android_unit_testing_group.jpg



推荐阅读
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 目录实现效果:实现环境实现方法一:基本思路主要代码JavaScript代码总结方法二主要代码总结方法三基本思路主要代码JavaScriptHTML总结实 ... [详细]
  • 微软头条实习生分享深度学习自学指南
    本文介绍了一位微软头条实习生自学深度学习的经验分享,包括学习资源推荐、重要基础知识的学习要点等。作者强调了学好Python和数学基础的重要性,并提供了一些建议。 ... [详细]
  • 本文介绍了设计师伊振华受邀参与沈阳市智慧城市运行管理中心项目的整体设计,并以数字赋能和创新驱动高质量发展的理念,建设了集成、智慧、高效的一体化城市综合管理平台,促进了城市的数字化转型。该中心被称为当代城市的智能心脏,为沈阳市的智慧城市建设做出了重要贡献。 ... [详细]
  • Java序列化对象传给PHP的方法及原理解析
    本文介绍了Java序列化对象传给PHP的方法及原理,包括Java对象传递的方式、序列化的方式、PHP中的序列化用法介绍、Java是否能反序列化PHP的数据、Java序列化的原理以及解决Java序列化中的问题。同时还解释了序列化的概念和作用,以及代码执行序列化所需要的权限。最后指出,序列化会将对象实例的所有字段都进行序列化,使得数据能够被表示为实例的序列化数据,但只有能够解释该格式的代码才能够确定数据的内容。 ... [详细]
  • 如何使用Java获取服务器硬件信息和磁盘负载率
    本文介绍了使用Java编程语言获取服务器硬件信息和磁盘负载率的方法。首先在远程服务器上搭建一个支持服务端语言的HTTP服务,并获取服务器的磁盘信息,并将结果输出。然后在本地使用JS编写一个AJAX脚本,远程请求服务端的程序,得到结果并展示给用户。其中还介绍了如何提取硬盘序列号的方法。 ... [详细]
  • 本文介绍了OC学习笔记中的@property和@synthesize,包括属性的定义和合成的使用方法。通过示例代码详细讲解了@property和@synthesize的作用和用法。 ... [详细]
  • 本文主要解析了Open judge C16H问题中涉及到的Magical Balls的快速幂和逆元算法,并给出了问题的解析和解决方法。详细介绍了问题的背景和规则,并给出了相应的算法解析和实现步骤。通过本文的解析,读者可以更好地理解和解决Open judge C16H问题中的Magical Balls部分。 ... [详细]
  • 在说Hibernate映射前,我们先来了解下对象关系映射ORM。ORM的实现思想就是将关系数据库中表的数据映射成对象,以对象的形式展现。这样开发人员就可以把对数据库的操作转化为对 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • 使用在线工具jsonschema2pojo根据json生成java对象
    本文介绍了使用在线工具jsonschema2pojo根据json生成java对象的方法。通过该工具,用户只需将json字符串复制到输入框中,即可自动将其转换成java对象。该工具还能解析列表式的json数据,并将嵌套在内层的对象也解析出来。本文以请求github的api为例,展示了使用该工具的步骤和效果。 ... [详细]
  • ZSI.generate.Wsdl2PythonError: unsupported local simpleType restriction ... [详细]
  • 本文介绍了Linux系统中正则表达式的基础知识,包括正则表达式的简介、字符分类、普通字符和元字符的区别,以及在学习过程中需要注意的事项。同时提醒读者要注意正则表达式与通配符的区别,并给出了使用正则表达式时的一些建议。本文适合初学者了解Linux系统中的正则表达式,并提供了学习的参考资料。 ... [详细]
  • Java学习笔记之面向对象编程(OOP)
    本文介绍了Java学习笔记中的面向对象编程(OOP)内容,包括OOP的三大特性(封装、继承、多态)和五大原则(单一职责原则、开放封闭原则、里式替换原则、依赖倒置原则)。通过学习OOP,可以提高代码复用性、拓展性和安全性。 ... [详细]
  • 本文讨论了在手机移动端如何使用HTML5和JavaScript实现视频上传并压缩视频质量,或者降低手机摄像头拍摄质量的问题。作者指出HTML5和JavaScript无法直接压缩视频,只能通过将视频传送到服务器端由后端进行压缩。对于控制相机拍摄质量,只有使用JAVA编写Android客户端才能实现压缩。此外,作者还解释了在交作业时使用zip格式压缩包导致CSS文件和图片音乐丢失的原因,并提供了解决方法。最后,作者还介绍了一个用于处理图片的类,可以实现图片剪裁处理和生成缩略图的功能。 ... [详细]
author-avatar
小文662502914775
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有