以这个项目为例,我觉得对于测试用例的设计,不能离开架构层面和业务层面。
架构层面
不同的架构,决定着测试用例的写法不一,比如MVC或者MVP,每一层负责的测试职责是不一样的。 以todo-mvp这个项目为例,笔者在《解读》中已经提到,一个功能的测试需要MVP三层的协作,彼此各司其职,却又互相联系,这里再做一番总结:
业务层面
做单元测试,测试业务逻辑是重中之重。View层承担着这一重任,设计这一层的测试用例时,不要想太多,站在我们正常使用功能的角度出发,把交互行为翻译成Espresso的代码。由于View是入口,当一种交互行为发生,Presenter开始调度View和Model层各自执行逻辑,因此从这个角度来讲,View层的测试涵盖了MVP三层的逻辑。
聊完有价值的,我们再来看看什么是没价值的测试用例。比如以下几种:
接下来笔者将完整的展示这个MVP项目中的所有单元测试用例,分为三个维度,androidTest下的、androidTestMock下的和test下的所有测试用例,如果觉得阅读起来枯燥,可以直接阅读每个测试类开篇的概述部分。
概述:该测试用例做导航测试,即对DrawerLayout打开、关闭、点击Item后打开的Activity等功能进行测试。
意义:告诉我们如何对DrawerLayout设计有价值的测试用例。
clickOnStatisticsNavigationItem_ShowsStatisticsScreen
clickOnListNavigationItem_ShowsListScreen
clickOnAndroidHomeIcon_OpensNavigation
概述:该测试用例针对任务列表和任务详情页的界面功能测试,涵盖所有页面上的交互,包括增删改查任务、改变任务状态,过滤任务列表等,除此之外还验证了横竖屏的交互对界面数据状态的影响。
意义:告诉我们如何设计有价值的功能界面测试用例。
clickAddTaskButton_opensAddTaskUi
addTaskToTasksList
editTask
markTaskAsComplete
markTaskAsActive
showAllTasks
showActiveTasks
showCompletedTasks
clearCompletedTasks
createOneTask_deleteTask
createTwoTasks_deleteOneTask
markTaskAsCompleteOnDetailScreen_taskIsCompleteInList
markTaskAsActiveOnDetailScreen_taskIsActiveInList
markTaskAsAcompleteAndActiveOnDetailScreen_taskIsActiveInList
markTaskAsActiveAndCompleteOnDetailScreen_taskIsCompleteInList
orientationChange_FilterActivePersists
orientationChange_FilterCompletedPersists
概述:对数据库中处理Task的增删改查、改变Task状态等进行测试。
意义:持久层的CRUD往往需要配合起来测试和断言,此例很好的诠释了这一点
saveTask_retrievesTask
completeTask_retrievedTaskIsComplete
activateTask_retrievedTaskIsActive
clearCompletedTask_taskNotRetrievable
deleteAllTasks_emptyListOfRetrievedTask
getTasks_retrieveSavedTasks
在《解读》一文中,笔者提到该文件夹主要的作用是对网络请求进行Fake,即不发出网络请求,而是返回事先定义好的数据。
errorShownOnEmptyTask
Tasks_ShowsNonEmptyMessage
概述:Fake出不同状态的任务并在详情页进行标题、描述和状态的断言。
意义:指导我们如何对网络请求数据进行Fake。
activeTaskDetails_DisplayedInUi
completedTaskDetails_DisplayedInUi
orientationChange_menuAndTaskPersist
TasksScreenTest
中一致,不再赘述。概述:进入Presenter层的测试后,我们不再去断言输入输出了,取而代之的是,断言是否正确的覆盖了View层和Model层的逻辑。
AddEditTaskPresenter
共有三个方法,分别是createTask
、updateTask
和populateTask
,对应增加、修改、展示任务的功能,其中增加任务涉及到成功和失败的情况,因此有4个测试用例。
意义:这些Presenter层的测试可以教会我们如何Mock,如何verify,如何测试异步回调,以及如何完整的覆盖Presenter层的所有逻辑路径。
saveNewTaskToRepository_showsSuccessMessageUi
saveTask_emptyTaskShowsErrorUi
saveExistingTaskToRepository_showsSuccessMessageUi
populateTask_callsRepoAndUpdatesView
概述:该类的presenter接口比较简单,只有一个入口方法
start
,执行的是加载统计信息的逻辑,执行过程中涉及几个路径:加载空任务列表,加载非空任务列表和数据不可用,分别对应以下1,2,3点。
loadEmptyTasksFromRepository_CallViewToDisplay
loadNonEmptyTasksFromRepository_CallViewToDisplay
loadStatisticsWhenTasksAreUnavailable_CallErrorToDisplay
概述:该Presenter共有5个方法,分别是:
start
:展示任务详情,涉及三种路径:展示Active任务、展示Compeled任务和展示非法ID的任务,对应1,2,3的测试用例deleteTask
:删除任务,对应第4个测试用例completeTask
:完成任务,对于第5个activateTask
:激活任务,对应第6个editTask
:编辑任务,对应第7个,编辑非法ID的任务对应的测试用例为第8个
getActiveTaskFromRepositoryAndLoadIntoView
getCompletedTaskFromRepositoryAndLoadIntoView
getUnknownTaskFromRepositoryAndLoadIntoView
deleteTask
completeTask
activateTask
activeTaskIsShownWhenEditing
invalidTaskIsNotShownWhenEditing
概述,此TasksPresenter的测试与上一点类似,从接口方法出发,此类共有10个接口方法,为此设计了8个测试用例,分别是展示All/Active/Completed的任务列表、点击打开任务详情页、改变任务状态等。
loadAllTasksFromRepositoryAndLoadIntoView
loadActiveTasksFromRepositoryAndLoadIntoView
loadCompletedTasksFromRepositoryAndLoadIntoView
clickOnFab_ShowsAddTaskUi
clickOnTask_ShowsDetailUi
completeTask_ShowsTaskMarkedComplete
activateTask_ShowsTaskMarkedActive
unavailableTasks_ShowsError
概述:该类的测试用例非常齐全,对于如何设计测试用例让数据过期,如何让数据取自本地或者网络等测试技巧都有极高的学习价值。
getTasks_repositoryCachesAfterFirstApiCall
getTasks_requestsAllTasksFromLocalDataSource
saveTask_savesTaskToServiceAPI
completeTask_completesTaskToServiceAPIUpdatesCache
completeTaskId_completesTaskToServiceAPIUpdatesCache
activateTask_activatesTaskToServiceAPIUpdatesCache
activateTaskId_activatesTaskToServiceAPIUpdatesCache
getTask_requestsSingleTaskFromLocalDataSource
deleteCompletedTasks_deleteCompletedTasksToServiceAPIUpdatesCache
deleteTask_deleteTaskToServiceAPIRemovedFromCache
getTasksWithDirtyCache_tasksAreRetrievedFromRemote
getTasksWithLocalDataSourceUnavailable_tasksAreRetrievedFromRemote
getTasksWithBothDataSourcesUnavailable_firesOnDataUnavailable
getTaskWithBothDataSourcesUnavailable_firesOnDataUnavailable
getTasks_refreshesLocalDataSource