单元测试不是集成测试
  这里需要强调一个观念,那是单元测试只是测试一个方法单元,它不是测试一整个流程。举个例子来说,一个Login页面,上面有两个输入框和一个button。两个输入框分别用于输入用户名和密码。点击button以后,有一个 UserManager 会去执行 performlogin 操作,然后将结果返回,更新页面。
  那么我们给这个东西做单元测试的时候,不是测这一整个login流程。这种整个流程的测试:给两个输入框设置正确的用户名和密码,点击login button, 后页面得到更新。叫做集成测试,而不是单元测试。当然,集成测试也是有他的必要性的,然而这不是我们每个程序员应该花多少精力所在的地方。在这方面,有一个理论叫做 Test Pyramid ,如下图所示:

  Test Pyramid理论基本大意是,单元测试是基础,是我们应该花绝大多数时间去写的部分,而集成测试等应该是冰山上面能看见的那一小部分。
  为什么是这样呢?因为集成测试设置起来很麻烦,运行起来很慢,发现的bug少,在保证代码质量、改善代码设计方面更起不到任何作用,因此它的重要程度并不是那么高,也无法将它纳入我们正常的工作流程中。
  而单元测试则刚好相反,它运行速度超快,能发现的bug更多,在开发时能引导更好的代码设计,在重构时能保证重构的正确性,因此它能保证我们的代码在一个比较高的质量水平上。同时因为运行速度快,我们很容易把它纳入到我们正常的开发流程中。
  至于为什么集成测试发现的bug少,而单元测试发现的bug多,这里也稍作解释,因为集成测试不能测试到其中每个环节的每个方面,某一个集成测试运行正确了,不代表另一个集成测试也能运行正确。而单元测试会比较完整的测试每个单元的各种不同的状况、临界条件等等。一般来说,如果每一个环节是对的,那么在很大的概率上,整个流程是对的。虽然不能保证整个流程一定是对的。所以,集成测试需要有,但应该是少量,单元测试是我们应该花重点去做的事情。
  那对于这个例子,单元测试是怎么样的呢?这个请看下一小节。
  两种函数(方法),两种不同的测试方式

  一个类的方法可以分为两种,一种是有返回值的,另一种是没有返回值的。对于有返回值的方法,我们要测起来比较容易,跟上面的 Calculator 例子那样,输入相应的参数,得到相应的返回值,然后验证得到的返回值跟我们预期的值一样,好了。
  但是没有返回值的方法,要怎么测试呢?比如说刚刚login的例子,点击那个按钮,会执行Activity的 login() 方法,它的定义如下:
  public void login() {
  String username = ...//get username from username EditText
  String password = ...//get password from password EditText
  //do other operation like validation, etc
  ...
  mUserManager.performlogin(username, password);
  }
  这个方法是void的,那么怎么验证这个方法是正确的呢?其实仔细想想,这个方法也是有输出的,它的输出是,调用了 mUserManager 的 performLogin 方法,同时传给他两个参数。所以只要验证 mUserManager 的 performLogin 方法得到了调用,同时传给他的参数是正确的,说明这个方法是能正常工作的。
  那怎么样验证这个Activity的 login() 方法,会调用 mUserManager 的 performLogin 方法呢?这里涉及到 mock 的概念,在后面的文章关于 Mockito 的使用的时候,会介绍到,这里简单解释下,那是在测试环境下,我们会使用一套mock framework(Mockito),生成一个mock的 UserManager 然后赋值给 mUserManager ,因为这个 mUserManager 是通过mock framework生成的,所以这个mock framework可以验证它的什么方法被调用了,参数是什么,等等。
  小结
  上面讲述了单元测试的定义,以及与集成测试的区别,一般来说,单元测试不会接触到数据库,不会接触到网络,不会接触到一些复杂的外部环境,如果有的话,那可能是你测试的方式有误,测试的粒度不够“单元”,希望这篇文章能将这两者的区别解释清楚。