初入Android单元测试

aalliioo 7年前
   <h2><strong>前言</strong></h2>    <p>在今年的Android开发技术中,MVP & RxJava & Retrofit 已经成为各个项目的标配了。了解过MVP的人都知道,其中的一个优点就是便于 <strong>单元测试</strong> 的编写。那么我们今天就步入单元测试的这个深坑吧。</p>    <h2><strong>单元测试是什么</strong></h2>    <p>单元测试(unit testing),是指对软件中的最小可测试单元进行检查和验证。对于单元测试中单元的含义,一般来说,要根据实际情况去判定其具体含义,如C语言中单元指一个函数,Java里单元指一个类,图形化的软件中可以指一个窗口或一个菜单等。总的来说,单元就是人为规定的最小的被测功能模块。单元测试是在软件开发过程中要进行的最低级别的测试活动,软件的独立单元将在与程序的其他部分相隔离的情况下进行测试。</p>    <p>其实很容易理解,对于我们开发者来说就是验证一个功能是否正确的一个过程。</p>    <h2><strong>为什么要写单元测试</strong></h2>    <p>相信很多东西都有自己的测试人员,所以有的人就会问了。为什么我们开发者还需要自己写代码来进行测试呢?</p>    <p>首先我们要知道,单元测试一般是开发人员最应该关注的事情之一,单元测试只是测试一个方法单元, 他不是测试整个流程没有问题。引入单元测试,带来的好处是显而易见的,首先可以直接帮我们寻找出 bug,并且在加入新的功能模块时,可以发现它是否影响并破坏了我们原有的功能。单元测试还可以强迫让我们的代码变得精炼短小,因为太过复杂的代码无法引入单元测试。单元测试还可以节省测试成本,不需要启动整个系统,就可以直接的,针对性的对任意模块进行测试。而且可以简单的模拟各种情况覆盖其各种分支。这是降低整体开发时间,提高软件质量的一种有效方法。</p>    <p>当然单元测试不仅需要学习,而且你要学习的东西还真不少,你要学习 JUnit 的使用,你要学习 Mokito 的使用, Robolectric 的使用, 依赖注入 的概念和使用等等等。</p>    <h2><strong>简单的单元测试</strong></h2>    <p>单元测试其实并没有你想象中的那么复杂。</p>    <p>我们先用Java来举例看一下就知道了,</p>    <pre>  <code class="language-java">// 这是一个普通的类  public class Calculator {      public int add(int one, int another) {          // 为了简单起见,暂不考虑溢出等情况。          return one + another;      }        public int multiply(int one, int another) {          // 为了简单起见,暂不考虑溢出等情况。          return one * another;      }  }</code></pre>    <pre>  <code class="language-java">// 这是对应的测试代码  public class CalculatorTest {      public static void main(String[] args) {          Calculator calculator = new Calculator();          int sum = calculator.add(1, 2);          if(sum == 3) {              System.out.println("add() works!")          } else {              System.out.println("add() does not works!")          }            int product = calculator.multiply(2, 4);          if (product == 8) {              System.out.println("multiply() works!")          } else {              System.out.println("multiply() does not works!")          }      }  }</code></pre>    <p>也许这个示例比较简单,让你觉得这个测试代码反而很多余了。如果你的类中方法一旦变多变复杂了,这样的测试就显得很重要了。当然也有人会说,我自己写那么多判断的代码,然后还要在看着terminal的输出,才知道测试是通过还是失败。同时,也有人会问,我们Android中,很多方法也都没有返回值啊,我们应该怎么测呢?</p>    <p>这些一切的一切,都由框架来替我们解决了,所以我们应该掌握几个单元测试的框架。</p>    <h2><strong>有哪些单元测试框架</strong></h2>    <p>测试是一个比较大的东西,对于Android而言,有UI测试,功能测试,集成测试,扒拉扒拉扒拉的很多,当然也有很多的测试方法。今天我们就先来看看有哪些测试的方法或者说是测试框架:</p>    <p>JUnit :能够直接在PC上执行;</p>    <p>Mokito</p>    <p>Robolectric :在不需要依赖Android环境的前提下,实现在PC上直接运行Android的单元测试;</p>    <p>Robotium :第三方UI测试框架;</p>    <p>Espresso :Google推出的测试框架;</p>    <p>UI Automator :流程的UI测试框架;</p>    <p>在此我列举的也只是部分常用的第三方测试框架,但是不要急着就去学习它们了。我们应该先来了解一些基本的概念,这样才能更好的掌握理解框架。</p>    <h2><strong>Mock</strong></h2>    <p>一个类的方法可以分为两种,一种是有返回值的,另一种是没有返回值的。对于有返回值的方法,我们要测起来比较容易,就跟上面的 Calculator 例子那样,输入相应的参数,得到相应的返回值,然后验证得到的返回值跟我们预期的值一样,就好了。</p>    <p>但是没有返回值的方法,要怎么测试呢?</p>    <p>这里我们就以Android中的一个login流程来进行分析一下:</p>    <pre>  <code class="language-java">// 一个Login页面,上面有两个输入框和一个button。两个输入框分别用于输入用户名和密码。点击button以后,有一个UserManager会去执行performlogin操作,然后将结果返回,更新页面。  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);  }</code></pre>    <p>这个方法是void的,那么怎么验证这个方法是正确的呢?其实仔细想想,这个方法也是有输出的,它的输出就是,调用了mUserManager的performLogin方法,同时传给他两个参数。所以只要验证mUserManager</p>    <p>的performLogin方法得到了调用,同时传给他的参数是正确的,就说明这个方法是能正常工作的。</p>    <p>那怎么样验证这个Activity的login()方法,会调用mUserManager的performLogin方法呢?这里涉及到 <a href="/misc/goto?guid=4959670564143304083" rel="nofollow,noindex">mock</a> 的概念,所以我们就先来讲讲什么是 <em>Mock</em></p>    <p>所谓的 Mock 就是创建一个类的虚假的对象,在测试环境中,用来替换掉真实的对象,主要提供两大功能:</p>    <ul>     <li>验证这个对象的某些方法的调用情况,调用了多少次,参数是什么等等</li>     <li>指定这个对象的某些方法的行为,返回特定的值,或者是执行特定的动作</li>    </ul>    <p>要使用 Mock,一般需要用到 Mock 框架,常见的就是 Mockito 这个框架,这个是Java界使用最广泛的一个mock框架。</p>    <h2><strong>小结</strong></h2>    <p>本篇文章,从为什么要做单元测试到单元测试所涉及的一些概念,框架进行了介绍。同时引入了Mockito这个框架,但是这个框架的使用也不是一两句话就可以介绍清楚的,所以打算在下一篇文章对Mockito的使用进行进一步的讲解。</p>    <p> </p>    <p> </p>    <p>来自:http://www.jianshu.com/p/78a67817ee5a</p>    <p> </p>