TDD for android(based on Mvp)

作者: louiszgm | 来源:发表于2017-01-10 11:30 被阅读577次

    前言

    hacker.png
    • 学习一样东西的时候,从是什么开始,然后会去探究为什么以及这么做之后的好处是什么,当经历了这个过程之后。才能够说是知道这个东西。
    • 笔者在刚开始接触单元测试的时候,只知道这个是测试用的,只知道调用这个类的这个方法是返回这个东西的,但是不知道怎么去测,为什么要去这样子测,会觉得增加代码量和工作负担。
    • 这个时候,就会被卡主。但是,如果是从更高一点的视角去看的话,就会知道怎么去测,我为什么要这样子去测。
    • 开发一个功能,首先从需求的提出,然后针对这个需求设计各种使用的用例。我们开发出一个功能无非就是为了满足用户各种使用场景的需求,根据各种不同的使用场景产生各种结果。
    • 我们身为开发者,所写的一些代码也是为了实现功能。
    • 如果可以在开发的同时,可以顺便把每个用户的使用场景都测试一遍,那该是多么爽的事情啊。这样子写出来的代码就有依据。知道是Cover了什么的一些测试用例。
    • Yeah! We have a thing for the shit! It is call the Test Drive Development(TDD)。It is born for this!

    没有测试之前

    not_happy.png

    测试Pass之后

    happy.png

    准备

    新建类以及接口
    LoginContract

    LoginContract.png

    LoginPresenter

    LoginPresenter.png

    LoginActivity

    LoginActivity.png

    LoginPresenter和LoginActivity各自实现了自己的接口,在上面的接口中,我们是没有定义任何一种方法,只是定义了一个模板。里面具体的方法都由接下来的用例所决定。

    对于登录这个功能来说,会有以下的测试用例:

    • 用户名为空的时候,显示错误信息

    • 密码为空的时候,显示错误信息

    • 用户名和密码输入成功,登录验证成功后进行跳转

    • 用户名和密码有输入,但是验证不通过,显示错误信息

    接下来,根据上面的测试用例,我们开始写我们的单元测试代码
    下面只会选取第一个测试用例进行讲解,完整的例子会分享在github上,戳这里

    Go For the TDD

    前期准备

    加入Mockito

    testCompile 'org.mockito:mockito-core:2.5.4'

    在MVP中,业务逻辑都是写在Presenter中,所以这里我们对LoginPresenter进行一个测试。右键点击LoginPresenter自动生成对应的测试类。

    CreateTestClass.png

    在LoginPresenterTest类中的setup方法里做一些初始化的操作

    public class LoginPresenterTest {
    
        private LoginContract.View mView;
        private Api api;
        private LoginPresenter mPresenter;
        @Before
        public void setUp() throws Exception {
            mView = mock(LoginContract.View.class);
            api = mock(Api.class);
            mPresenter = new LoginPresenter(mView, api);
        }
    }
    

    主要是初始化presenter,presenter需要注入两个对象来完成初始化。
    我们把需要注入的两个对象都Mock掉,被Mock掉的对象我们就可以自由操控它,让他返回我们希望要的值。因为我们这里测试的是LoginPresenter,所以,与这个无关的对象的具体逻辑我们不用去关注。
    关于Mockito的更多用法可以参考这篇文章Android单元测试(四):Mock以及Mockito的使用

    测试第一个用例(用户名为空的时候,显示错误信息)

    在LoginPresenterTest中新增一个@Test方法,新增后的代码如下

    public class LoginPresenterTest {
    
        private LoginContract.View mView;
        private Api api;
        private LoginPresenter mPresenter;
        @Before
        public void setUp() throws Exception {
            mView = mock(LoginContract.View.class);
            api = mock(Api.class);
            mPresenter = new LoginPresenter(mView, api);
        }
    
        @Test
        public void testWhenTheUserNameIsEmpty() throws Exception {
            
    
        }
    }
    

    接下来,我们要为这个测试用例写上一些具体的测试逻辑,并感受TDD的美妙
    首先,来写一个伪“中文代码”

    presenter调用处理具体逻辑的方法
    验证当用户名为空的时候,mView有没有调用到我们期望的方法

    以上就是这个测试用例对应的测试逻辑,是不是觉得上面这两句话没啥用,说了相当于没说。
    我们只需要去写针对当前测试用例的测试逻辑,并验证。
    但是我觉得这才是单元测试美妙的地方,他可以把测试粒度分得很小,以至于我们能够cover到每个case。

    No code say a ***, show me the fucking source code

    testWhenTheUserNameIsEmpty.png

    我们可以看到,很多方法是没有的(上图红色的),我们只需要关注我们需要调用到哪一些方法,只需要关注我们的逻辑,一些模板代码让IDE帮我们生成。
    按下快捷键(mac是option+enter, windows是alt+enter)

    LoginContract_imp.png

    最终的结果就是,在LoginContract中的对应的接口中自动生成了我们想要的方法。
    最后,在我们实现了这个接口的类中实现这些方法,在给每个方法写上具体的逻辑。

    LoginActivity_实现接口方法.png LoginPresenter_实现接口方法.png

    最终LoginActivity如下

    LoginActivity_final.png

    最终LoginPresenter如下

    LoginPresenter_final.png

    以上就是完成了我们第一个测试用例的业务逻辑,接下来就Run一下我们的测试方法,如果能够Pass,说明我们已经Cover了这个用例。那么就可以继续开始写下一个用例的测试方法,然后再去完善我们的逻辑代码,再Run一下我们新的用例的测试方法~如此循环。最后达到Cover每个测试用例的目的。

    testpass.png

    看到绿色的真高兴,说明我们的第一个测试用例已经通过了。
    接下来只需要重复上面的步骤,把剩下的测试用例Cover到了就ok

    总结

    个人觉得在写单元测试的时候,最难的地方在于测试用例的择写。

    在看了前面的例子之后,有人会疑惑,我为什么要把获取用户名和获取用户密码的那一部分抽出一个方法getInputUsername()getInputPwd(),为什么不直接把用户名和密码当做参数传给presenter的方法。
    这样子做的原因如下:

    • 对Mock对象有一个更加深刻的理解,笔者刚接触单元测试的时候,基本上都是被Mock对象给搞晕了,特别是类似这样子的代码
    when(mView.getInputUsername()).thenReturn("louiszgm");
    

    一开始不知道这样子做的意义是什么,为什么要手动指定返回值,只知道这个方法是指定了Mock对象的特定方法返回了一个特定的值。

    这个例子主要是用来去感受TDD的好处,很多地方都没有去完善,比如:

    • 在正式项目中,MVP的使用当然没有这么简陋,需要考虑view的泄漏之类的问题,也会根据每个公司的业务抽出一些基接口
    • Mockito的一些高级用法
    • 结合Dagger2让单元测试来的更爽
    • 这些已经有人写了很多优质的文章,又在这里打一波广告。关于单元测试框架的使用,推荐创神的单元测试系列的文章。
      Android 单元测试: 首先,从是什么开始

    个人感觉用TDD这个模式去开发的好处是:

    对自己写出来的代码有信心,毕竟cover到了每个用例。
    很多模板代码都不用自己写,让关注点更多的关注到我们的逻辑

    相关文章

      网友评论

        本文标题:TDD for android(based on Mvp)

        本文链接:https://www.haomeiwen.com/subject/icjzvttx.html