写点有价值的测试用例


这篇文章为《解读Android官方MVP项目单元测试》(以下简称《解读》)的附录部分,行此文的目的有二,其一是这个项目的单元测试齐全,覆盖率很高,有极高的学习价值,笔者希望把每个测试用例都描述一遍,通过这种方式来强迫自己认真的看完。其二,这部分内容难免枯燥,笔者尽力想把它写得可读性高一点点,却发现着实有难度,简直是给自己挖坑,所以从《写点有价值的测试用例》的角度出发,对这篇附录文章稍作修饰。但不管这样,这个MVP项目及其单元测试用例对我们的工作可以带来很多思路,所以不妨速读一遍。

一 什么是有价值的测试用例

以这个项目为例,我觉得对于测试用例的设计,不能离开架构层面和业务层面。

  1. 架构层面
    不同的架构,决定着测试用例的写法不一,比如MVC或者MVP,每一层负责的测试职责是不一样的。 以todo-mvp这个项目为例,笔者在《解读》中已经提到,一个功能的测试需要MVP三层的协作,彼此各司其职,却又互相联系,这里再做一番总结:
  • Presenter层:这一层很清晰,我们为它的每个接口方法,以及每个方法里涉及的多个逻辑路径设计相应的测试用例,值得注意的是,这一层我们较少做输入输出的断言,而是验证是否正确覆盖V层和M层的逻辑。
  • Model层:同上,我们为它的每个方法设计测试用例,与P层不同,这一层要断言输入输出数据是否准确。
  • View层:这一层我们放在业务层面来讲。
  1. 业务层面
    做单元测试,测试业务逻辑是重中之重。View层承担着这一重任,设计这一层的测试用例时,不要想太多,站在我们正常使用功能的角度出发,把交互行为翻译成Espresso的代码。由于View是入口,当一种交互行为发生,Presenter开始调度View和Model层各自执行逻辑,因此从这个角度来讲,View层的测试涵盖了MVP三层的逻辑。

聊完有价值的,我们再来看看什么是没价值的测试用例。比如以下几种:

  1. 对成熟的工具类进行测试
  2. 对简单的方法进行测试(比如get、set方法)
  3. MVP各层重复测试,比如P层去断言输入输出的正确性

接下来笔者将完整的展示这个MVP项目中的所有单元测试用例,分为三个维度,androidTest下的、androidTestMock下的和test下的所有测试用例,如果觉得阅读起来枯燥,可以直接阅读每个测试类开篇的概述部分

二 androidTest文件下的测试

V层:导航界面测试——AppNavigationTest

概述:该测试用例做导航测试,即对DrawerLayout打开、关闭、点击Item后打开的Activity等功能进行测试。
意义:告诉我们如何对DrawerLayout设计有价值的测试用例。

  1. clickOnStatisticsNavigationItem_ShowsStatisticsScreen
    打开Left Drawer->点击Statistics按钮->断言StatisticsActivity已经打开
  2. clickOnListNavigationItem_ShowsListScreen
    打开Left Drawer->点击Statistics按钮->打开Left Drawer->点击TO-DO list按钮->断言TasksActivity已经打开
  3. clickOnAndroidHomeIcon_OpensNavigation
    验证通过ActionBar的icon进行关闭和打开Left Drawer

V层:任务模块界面测试——TasksScreenTest

概述:该测试用例针对任务列表和任务详情页的界面功能测试,涵盖所有页面上的交互,包括增删改查任务、改变任务状态,过滤任务列表等,除此之外还验证了横竖屏的交互对界面数据状态的影响。
意义:告诉我们如何设计有价值的功能界面测试用例。

  1. clickAddTaskButton_opensAddTaskUi
    点击添加按钮->断言相应Activity已经打开
  2. addTaskToTasksList
    添加标题1的TO-DO任务后回到列表页->断言标题1存在
  3. editTask
    添加标题1的TO-DO任务后回到列表页->点击此Item进入查看页面->点击编辑按钮->修改成标题2->点击保存->断言标题1不存在和标题2存在
  4. markTaskAsComplete
    添加任务并点击CheckBox设置为已完成->通过过滤器进入All/Active/Completed视图,断言该任务是否存在
  5. markTaskAsActive
    测试标记任务为Active状态,手法同上一点
  6. showAllTasks
    添加2个任务->进入All视图->断言两个任务在界面上存在
  7. showActiveTasks
    添加2个任务->进入Active视图->断言两个任务在界面上存在
  8. showCompletedTasks
    添加2个任务->标记为已完成->进入Completed视图->断言两个任务在界面上存在
  9. clearCompletedTasks
    添加2个任务->标记为已完成->点击Clear completed按钮->断言两个任务不存在
  10. createOneTask_deleteTask
    添加1个任务->点击该任务进入详情页->点击删除->断言该任务不存在
  11. createTwoTasks_deleteOneTask
    创建2个任务->删除第2个->断言第1个存在且第2个不存在
  12. markTaskAsCompleteOnDetailScreen_taskIsCompleteInList
    创建1个任务->点击该任务进入详情页->标记为已完成->回到列表页->断言该任务为选中状态
  13. markTaskAsActiveOnDetailScreen_taskIsActiveInList
    创建1个任务->在列表页标记为已选中->进入详情页标记为未选中->回到列表页->断言该任务未被选中
  14. markTaskAsAcompleteAndActiveOnDetailScreen_taskIsActiveInList
    创建1个任务->在详情页触发两次checkbox的点击- 回到列表页->断言该任务未被选中
  15. markTaskAsActiveAndCompleteOnDetailScreen_taskIsCompleteInList
    创建1个任务->标记为已完成->在详情页触发两次checkbox的点击- 回到列表页->断言该任务被选中
  16. orientationChange_FilterActivePersists
    创建1个任务->标记为已完成->进入Active视图->验证该任务不存在->切换横竖屏->断言该任务状态与之前一致
  17. orientationChange_FilterCompletedPersists
    创建1个任务->标记为已完成->进入Completed视图->验证该任务存在->切换横竖屏->断言该任务状态与之前一致

M层:本地数据库操作测试——TasksLocalDataSourceTest

概述:对数据库中处理Task的增删改查、改变Task状态等进行测试。

意义:持久层的CRUD往往需要配合起来测试和断言,此例很好的诠释了这一点

  1. saveTask_retrievesTask
  • 测试目的:验证保存Task到数据库的逻辑
  • 测试用例:实例化Task对象->保存入库->根据ID获取Task->在回调函数中断言与入库的Task一致
  1. completeTask_retrievedTaskIsComplete
  • 测试目的:验证将任务设置成完成状态的逻辑
  • 测试用例:Task对象保存入库->触发完成任务的逻辑->根据ID获取Task->在回调函数中断言该Task已完成
  1. activateTask_retrievedTaskIsActive
  • 测试目的:验证将任务设置为激活状态的逻辑
  • 测试用例:mock一个回调对象callback->Task对象保存入库->触发完成任务的逻辑->触发激活任务的逻辑->根据ID获取Task->断言callback执行了onTaskLoaded的逻辑
  1. clearCompletedTask_taskNotRetrievable
  • 测试目的:验证清除所有已完成任务的逻辑
  • 测试用例:mock三个回调函数对象,分别是callback1到3->保存任务1,任务2和任务3,其中任务1和任务2为completed状态,任务3为active状态->清理所有已完成的任务->根据3个Task的ID分别获取Task->断言callback1和callback2执行了onDataNotAvailable逻辑- >断言callback3执行onTaskLoaded逻辑
  1. deleteAllTasks_emptyListOfRetrievedTask
  • 测试目的:验证删除数据库中所有任务的逻辑
  • 测试用例:保存任务->mock一个回调函数callback->删除所有任务->获取任务列表->断言callback执行了onDataNotAvailable的逻辑
  1. getTasks_retrieveSavedTasks
  • 测试目的:验证获取数据库中所有任务的逻辑
  • 测试用例:保存2个任务->获取任务列表->在回调函数中断言这2个任务存在

三 androidTestMock文件下的测试

《解读》一文中,笔者提到该文件夹主要的作用是对网络请求进行Fake,即不发出网络请求,而是返回事先定义好的数据。

V层:新增编辑任务界面测试——AddEditTaskScreenTest

  1. errorShownOnEmptyTask
  • 测试目的:验证保存或编辑任务时,如果输入空标题,会弹出Snackbar提示不能为空
  • 测试用例:打开详情页->输入空标题和空描述->点击保存->通过Snackbar的消息内容验证Snackbar已显示

V层:统计界面测试——StatisticsScreenTest

  1. Tasks_ShowsNonEmptyMessage
    打开统计界面->事先Fake两条任务数据,状态分别为Completed和Active->断言两种统计栏目都已显示

V层:任务详情界面测试——TaskDetailScreenTest

概述:Fake出不同状态的任务并在详情页进行标题、描述和状态的断言。
意义:指导我们如何对网络请求数据进行Fake。

  1. activeTaskDetails_DisplayedInUi
    Fake一条状态为Active的任务->打开详情页->断言标题、描述、任务状态
  2. completedTaskDetails_DisplayedInUi
    Fake一条状态为Complete的任务->打开详情页->断言标题、描述、任务状态
  3. orientationChange_menuAndTaskPersist
    横竖屏的测试手法与TasksScreenTest中一致,不再赘述。

四 test文件夹下的测试

P层:新增编辑任务测试——AddEditTaskPresenterTest

概述:进入Presenter层的测试后,我们不再去断言输入输出了,取而代之的是,断言是否正确的覆盖了View层和Model层的逻辑。AddEditTaskPresenter共有三个方法,分别是createTaskupdateTaskpopulateTask,对应增加、修改、展示任务的功能,其中增加任务涉及到成功和失败的情况,因此有4个测试用例。
意义:这些Presenter层的测试可以教会我们如何Mock,如何verify,如何测试异步回调,以及如何完整的覆盖Presenter层的所有逻辑路径。

  1. saveNewTaskToRepository_showsSuccessMessageUi
    创建Presenter,执行创建任务的逻辑->断言Model层执行了保存的逻辑->断言View层执行了显示任务列表的逻辑
  2. saveTask_emptyTaskShowsErrorUi
    创建Presenter,执行创建任务的逻辑,且任务Title为空->断言View层执行了展示error的逻辑
  3. saveExistingTaskToRepository_showsSuccessMessageUi
    此用例验证的是update任务的逻辑,测试手法同1。
  4. populateTask_callsRepoAndUpdatesView
  • 测试目的:验证详情页展示的任务信息是否正确
  • 测试用例:presenter执行populateTask()->断言执行了getTask(),且参数正确->断言回调函数执行了正确的逻辑->断言View层展示的是正确的Task数据

P层:统计功能测试——StatisticsPresenterTest

概述:该类的presenter接口比较简单,只有一个入口方法start,执行的是加载统计信息的逻辑,执行过程中涉及几个路径:加载空任务列表,加载非空任务列表和数据不可用,分别对应以下1,2,3点。

  1. loadEmptyTasksFromRepository_CallViewToDisplay
    断言加载空任务列表
  2. loadNonEmptyTasksFromRepository_CallViewToDisplay
    断言加载非空任务列表
  3. loadStatisticsWhenTasksAreUnavailable_CallErrorToDisplay
    断言数据不可用

P层:任务详情功能测试——TaskDetailPresenterTest

概述:该Presenter共有5个方法,分别是:

  • start:展示任务详情,涉及三种路径:展示Active任务、展示Compeled任务和展示非法ID的任务,对应1,2,3的测试用例
  • deleteTask:删除任务,对应第4个测试用例
  • completeTask:完成任务,对于第5个
  • activateTask:激活任务,对应第6个
  • editTask:编辑任务,对应第7个,编辑非法ID的任务对应的测试用例为第8个
  1. getActiveTaskFromRepositoryAndLoadIntoView
  2. getCompletedTaskFromRepositoryAndLoadIntoView
  3. getUnknownTaskFromRepositoryAndLoadIntoView
  4. deleteTask
  5. completeTask
  6. activateTask
  7. activeTaskIsShownWhenEditing
  8. invalidTaskIsNotShownWhenEditing

P层:任务列表功能测试——TasksPresenterTest

概述,此TasksPresenter的测试与上一点类似,从接口方法出发,此类共有10个接口方法,为此设计了8个测试用例,分别是展示All/Active/Completed的任务列表、点击打开任务详情页、改变任务状态等。

  1. loadAllTasksFromRepositoryAndLoadIntoView
  2. loadActiveTasksFromRepositoryAndLoadIntoView
  3. loadCompletedTasksFromRepositoryAndLoadIntoView
  4. clickOnFab_ShowsAddTaskUi
  5. clickOnTask_ShowsDetailUi
  6. completeTask_ShowsTaskMarkedComplete
  7. activateTask_ShowsTaskMarkedActive
  8. unavailableTasks_ShowsError

M层:数据操作门面类测试——TasksRepositoryTest

概述:该类的测试用例非常齐全,对于如何设计测试用例让数据过期,如何让数据取自本地或者网络等测试技巧都有极高的学习价值。

  1. getTasks_repositoryCachesAfterFirstApiCall
  2. getTasks_requestsAllTasksFromLocalDataSource
  3. saveTask_savesTaskToServiceAPI
  4. completeTask_completesTaskToServiceAPIUpdatesCache
  5. completeTaskId_completesTaskToServiceAPIUpdatesCache
  6. activateTask_activatesTaskToServiceAPIUpdatesCache
  7. activateTaskId_activatesTaskToServiceAPIUpdatesCache
  8. getTask_requestsSingleTaskFromLocalDataSource
  9. deleteCompletedTasks_deleteCompletedTasksToServiceAPIUpdatesCache
  10. deleteAllTasks_deleteTasksToServiceAPIUpdatesCache
  11. deleteTask_deleteTaskToServiceAPIRemovedFromCache
  12. getTasksWithDirtyCache_tasksAreRetrievedFromRemote
  13. getTasksWithLocalDataSourceUnavailable_tasksAreRetrievedFromRemote
  14. getTasksWithBothDataSourcesUnavailable_firesOnDataUnavailable
  15. getTaskWithBothDataSourcesUnavailable_firesOnDataUnavailable
  16. getTasks_refreshesLocalDataSource
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,560评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,104评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,297评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,869评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,275评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,563评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,833评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,543评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,245评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,512评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,011评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,359评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,006评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,062评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,825评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,590评论 2 273
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,501评论 2 268

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 170,566评论 25 707
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,099评论 18 139
  • 写在前面 关于MVP关于MVP的介绍很多,这不是本文的重点,这里列举近期一些比较好的文章。 Android官方MV...
    geniusmart阅读 32,683评论 23 350
  • like语句 1、sql: select * from brrps_mart_org_mapwhere part_...
    queenieyin阅读 649评论 0 0