单元测试和Mock方法

来源:互联网 发布:淘宝的好评可以删除吗 编辑:程序博客网 时间:2024/05/06 05:21

单元测试和MockService

标签(空格分隔): 协作开发 虚拟接口 Android


单元测试首先是开发的一部分,目的是保证没有耦合的情况下任何输入都能保证正确的输出(包括异常),可以保证程序的健壮性,避免代码腐烂化。其重要性是不言而喻的。查找了一些在Java中的测试思想以及测试工具,整理一下思路。

在单元测试时,测试人员根据详细设计说明书和源程序清单,了解到该模块的I/O条件和模块的逻辑结构,主要采用白盒测试的测试用例,辅之以黑盒测试的测试用例,使之对任何合理和不合理的输入都要能鉴别和响应。这就要求对程序所有的局部和全局的数据结构、外部接口和程序代码的关键部分进行桌面检查和代码审查。


测试内容

边界条件

在开发过程中,代码都是建立在正常的边界之内,对于边界与边界外的条件测试相对较少。边界条件是测试的重点之一,也是极易引发异常的重灾区。
1. 一致性:值是否与预期一致
2. 有序性:值是否像我们期望的那样有序或者无序
3. 区间性:值是否位于合理的最大值和最小值之间
4. 依赖性:代码是否引用了一些不在代码本省控制范围之内的外部资源(SD卡)等
5. 存在性:值(对象)是否存在
6. 基数性:是否恰好有足够的值(测试方法能否正确的计数)
7. 时间性:所有的事件的发生是否是有序的,是否在正确的时刻返回正确或合理的值。

局部数据结构测试

  1. 不正确或不一致的数据类型说明
  2. 使用尚未赋值或尚未初始化的变量
  3. 错误的初始值或错误的默认值
  4. 变量名拼写错或书写错
  5. 使用了外部变量或函数
  6. 不一致的数据类型
  7. 全局数据对模块的影响
  8. 数组越界
  9. 非法引用

覆盖路径测试

由于在测试时不可能做到穷举测试,所以在单元测试时要根据“白盒”测试和“黑盒”测试用例
设计方法设计测试用例,对模块中重要的执行路径进行测试。重要的执行路径指那些处在完
单元功能的算法、控制、数据处理等重要位置的执行路径,也指由于控制较复杂而易错的路径,有选择地对执行路径进行测试是一项重要的任务。应当设计测试用例查找由于错误的计算、不正确的比较或不正常的控制流而导致的错误,对基本执行路径和循环进行测试可发现大量的路径错误。

覆盖路径测试需要注意

  1. 死代码(dead code)执行不到的代码
  2. 算法错误
  3. 混用不同类的操作(泛型)
  4. 初始化不正确
  5. 精度错误—— 比较运算错误、赋值错误,表达式的不正确符号
  6. 循环变量的使用错误—— 错误赋值以及其他错误

路径的测试是伴随着选择而来的,而选择在于条件的比较,大部分错误在于比较

不同数据类型的比较。
不正确的逻辑运算符或优先次序。
因浮点运算精度问题而造成的两值比较不等。
关系表达式中不正确的变量和比较符。
“差 1 错”,即不正常的或不存在的循环中的条件。
当遇到发散的循环时无法跳出循环。
当遇到发散的迭代时不能终止循环。
错误的修改循环变量。

异常处理测试

出错的描述是否难以理解,是否能够对错误定位
显示的错误与实际的错误是否相符
对错误条件的处理正确与否
在对错误进行处理之前,错误条件是否已经引起系统的干预

模块接口测试

当完成内部测试之后,我们可以着手于对外部对接的测试。

模块输入参数的数目是否与模块形式参数数目相同。
模块各输入的参数属性与对应的形参属性是否一致。
模块各输入的参数类型与对应的形参类型是否一致。
传到被调用模块的实参的数目是否与被调用模块形参的数目相同。
传到被调用模块的实参的属性是否与被调用模块形参的属性相同。
传到被调用模块的实参的类型是否与被调用模块形参的类型相同。
引用内部函数时,实参的次序和数目是否正确。
是否引用了与当前入口无关的参数。
用于输入的变量有没有改变。
在经过不同模块时,全局变量的定义是否一致。
限制条件是否以形参的形式传递。
使用外部资源时,是否检查可用性并及时释放资源,如内存、文件、硬盘、端口等

当模块进行外部设备输入/输出操作时,要测试以下额外项目

文件的属性是否正确。
Open与Close语句是否正确。
规定的格式是否与I/O语句相符。
缓冲区的大小与记录的大小是否相配合。
在使用文件前,文件是否打开。
文件结束的条件是否安排好了。
I/O错误是否检查并做了处理。
在输出信息中是否有文字错误。


Mock对象

维基百科上这样描述Mock:In object-oriented programming, mock objects are simulated objects that mimic the behavior of real objects in controlled ways. A computer programmer typically creates a mock object to test the behavior of some other object, in much the same way that a car designer uses a crash test dummy to simulate the dynamic behavior. of a human in vehicle impacts.
Mock通常是指,在测试一个对象A时,我们构造一些假的对象来模拟与A之间的交互,而这些Mock对象的行为是我们事先设定且符合预期。通过这些Mock对象来测试A在正常逻辑,异常逻辑或压力情况下工作是否正常。
引入Mock最大的优势在于:Mock的行为固定,它确保当你访问该Mock的某个方法时总是能够获得一个没有任何逻辑的直接就返回的预期结果。

正常的过程
1. MobileApi开发人员事先和App开发人员定义好MobileApi的接口(名称,参数,返回值)
2. MobileApi按照约定写Mock接口,部署到测试环境,Mock Api中返回硬代码(JSON)数据。
3. App开发人员基于上述测试环境的Mock接口,进行开发
4. MobileApi开发完成后,通知App开发人员,对真实逻辑进行联调

以上四步,如果正常实施,是没有问题的,但是问题经常出现在第二步。MobileApi无法及时提供Mock接口

另外一种情况是:项目的进展需要更新现有接口字段,这是一个很耗时的过程

解除依赖

造成上面窘境的原因是模块与模块之间的依赖。我们可以在自己的App端设计自己的MockService,这样就可以完成步骤一之后,在APP端Mock自己的数据,直到开发完成,而不会被任何人阻塞,App开发完成后,如果有新的需要,可以请MobileApi开发人员汇总在一起修改。

设计App端的MockService需要注意一下几点

1, 对需要Mock的数据的MockApi接口,可以通过url.xml中配置Node节点MockClass属性,来指定要使用那个Mock子类生成的数据:

    <Node         Key="getWeatherInfo"        Expires="300"        NetType="get"        MockClass="com.pirate.brook.mock.MockWeatherInfo"        Url="http://www.weather.com.cn/xxx"/>

2, 使用反射工厂来设计MockService。MockService是基类,它有一个抽象方法getJsonData,用于返回不同的Json数据,上述Node节点配置的MockWeatherInfo.class如下

public class MockWeatherService extends MockService{    @Override    public String getJsonData() {        WeatherInfo weatherInfo = new WeatherInfo(); // 实体编程        weatherInfo.setCity("Beijing");        weatherInfo.setCityId("10000");        Response response = getSuccessResponse();        response.setResult(JSON.toJSONSting(weatherInfo));        return JSON.toJSONString(response); // 返回封装为完整的Response    }}

3,如何实现反射机制(RemoteService)

public void invoke(final BaseActivity activity,             final String apiKey,            final List<RequestParameter> params,             final RequestCallback callBack) {        // 从xml文件中获取URLData        final URLData urlData = UrlConfigManager.findURL(activity, apiKey);        // 判断是否需要Mock        if (urlData.getMockClass() != null) {            try {                // 反射得到类的对象                MockService mockService = (MockService) Class.forName(                        urlData.getMockClass()).newInstance();                String strResponse = mockService.getJsonData();                final Response responseInJson = JSON.parseObject(strResponse,                        Response.class);                if (callBack != null) {                    if (responseInJson.hasError()) {                        callBack.onFail(responseInJson.getErrorMessage());                    } else {                        callBack.onSuccess(responseInJson.getResult());                    }                }            } catch (ClassNotFoundException e) {                e.printStackTrace();            } catch (InstantiationException e) {                e.printStackTrace();            } catch (IllegalAccessException e) {                e.printStackTrace();            }        } else {            HttpRequest request = activity.getRequestManager().createRequest(                    urlData, params, callBack);            DefaultThreadPool.getInstance().execute(request);        }    }

URLData.class

public class URLData {    private String key;    private long expires;    private String netType;    private String url;    private String mockClass;    }

这样就减少了对各个模块的依赖


另外一种解除依赖的方法—-使用Mockito库

大家可以参考这篇入门文章Mockito简明教程

0 0