软件测试之我见

关键词:软件测试,xUnit
作者:BIce 创建时间:2011-12-04 20:58:00

 

对前一段测试工作的总结,记录下自己学到的。

一、测试分类

我觉得软件测试可以按照两种不同的标准分类:

1.         按测试粒度分类

   a)         单元测试:

最低粒度的测试,着重在于函数或单个类级别的测试,用于保证最基本的功能和代码无误,一般执行由代码的产生者执行,理所当然是白盒测试。

它至少要保证代码的语法正确、简单的逻辑覆盖、需求中的重要关键点可以覆盖。一般采用的比较成熟的技术是xUnit语言(JavaJUnit),在后面会大体介绍xUnit技术。

      b)         集成测试(模块、系统)

一个模块整体的测试(或者系统级别多个模块的测试),主要负责对模块或系统的对外接口进行测试,测试其提供功能的有效性,保证其对外的信息透明性,一般由专业测试人员进行

此处进行的测试可以是白盒或黑盒测试,具体情况视情况而定,一般两者都要进行。

2.         按着眼角度分类

   a)         功能测试:主要侧重于对系统提供的功能的测试,包含正向功能测试和异常功能测试等等。

   b)         性能测试:

l  主要侧重于对系统性能的测试,主要考虑在较大的请求压力下系统的极限响应能力(负载),一般在大并发、高访问量情况下进行

也经常用于查找系统性能瓶颈,辅助优化系统。(前几天京东的服务当机问题应该就是性能测试不到位,没有找到问题关键,导致问题频出)

      c)         回归测试:一般在系统进行升级后,为了防止本次升级对之前的版本不影响,对之前已有的功能进行测试,保证之前的功能无误。(一般是执行之前所有执行过的测试,用自动化测试比较合适)

(PS:也可以按执行方式不同分为手工和自动化脚本测试)

二、测试用到的技术简介

下面根据学到的东西,简单对上述介绍的各种测试给出简单的介绍:

1.   xUnit技术

   xUnit技术指一系列类似的,针对不同语言设计的单元测试框架,典型的工具有JUnit (for Java)PHPUnit(for PHP)等等。虽然xUnit技术原本的提出是为了解决单元测试的问题,但是现在xUnit也已经可以用于更高级别的测试(模型、系统级集成测试)中,作为一个测试框架,它提供了各种各样的功能。下面简单介绍xUnit的用法。(PHPUnit为例)

      1)   xUnit一般的模式

a)    简单概念

i.   TestCase:测试用例,用于测试一个类或一组函数的功能,每一个TestCase都应该有一到多个testXXX的函数,这些函数就是执行的测试函数,一般每个函数负责测试某个功能的一个方面(Case),有这些所有的testXXX函数来覆盖掉你想覆盖的Case

ii.  TestSuite:测试套件,用于组织TestCase,一般包含一组TestCases,一般在执行测试的时候,只执行TestSuite即可,它会逐一执行包含的TestCases,并返回测试的结果。

iii.  setUp/tearDownTestCase中一般必须重载的两个函数,测试人员需要负责给出每个testXXX函数执行前的数据准备(setUp)和执行后的清理工作(tearDown)

b)    一般流程,一般的TestCase执行流程为:

i.  执行setUp函数,为testXXX准备数据

ii.执行testXXX函数,调用被测代码,对比返回值和期望值,如正确则通过,否则失败。

iii. 执行tearDown函数,执行testXXX的执行后的数据恢复动作

iv. 如果还有其他的testXXX函数,继续1-3的步骤直到所有testXXX执行完成

c)    使用方式

i. 由上可见,一般书写一个TestCase,我们需要做的是仅仅是给出setUp,tearDown,testXXXs函数,其中setUptearDown函数的功能在上面已经给出,而测试的主体testXXX函数一般如下

ii. testXXX的一般流程

1.   初始化被测对象(或者模块)

2.   以期望的方式调用被测的方法,给出期望结果

3.   将实际被测方法的结果与期望结果进行对比,一般用assertEquals方法进行对比,xUnit提供了一系列assertXXX方法用于比较实际结果与期望结果,它们会自动进行对比,如果失败会给出不匹配的信息。

      2)  xUnit的其他功能

xUnit还提供了很多其他的功能用于辅助测试的编写,如Mock技术、TestCase脚本的自动生成技术、数据库测试技术等等,在下面会找一些简单介绍

2.  Mock技术

      1)   Mock(模拟)技术,一般在xUnit里面会提供叫做Mock的技术,它一般用来模拟其他的类或对象,目的是解除不同对象之间的依赖,完成解耦

   2)   Mock技术的简单应用(PHPUnit Mock为例)

假设ClassA要调用ClassBmethodB方法,现在我们要测试ClassA.methodA方法,我们的TestCase可能如下:


require_once 'ClassB.php';
require_once 'ClassA.php';
class TestClassA extends PHPUnit_Framework_TestCase
{
    public function testClassA()
    {   //生成一个ClassB的关于methodB方法的Mock 对象ojbB
        $objB  = $this->getMock('ClassB',array('methodB'));
       
//设置objB在以空/null为参数调用methodB的时候只执行一次,并且会返回字符串I am mock object 字符串
       $objB –>expects($this->once())
                  ->method('methodB')
                  ->with(null)
                  ->will('I am a mock object')
        
        //生成被测对象$objA,并设置$objA.methodA所使用的classB对象为刚刚Mock得到的对象
        $objA = new ClassA();
        $objA ->setClassB($objB);
        //调用objAmethodA方法,我们假设methodA方法声明如下
        //public methodA(){return $this->objectClassB->methodB()}
        //则此时objAmethodA方法调用objBmethodB方法,返回字符串’I am a mock object’,与期望值foo不相符,测试失败,将foo改成I am a mock object则测试通过
        $this->assertEquals('foo', $stub->doSomething());
    }
}
?>


    由上述简单例子可见,Mock技术可以简单的将ClassAClassB的依赖解开,这在测试单个类或模块是非常有用的,这样就避免了出现错误不知道到底是ClassA的问题还是ClassB的问题的情况,可以集中注意力解决ClassA上的问题,起到分离关注点的作用。

3.  Stub技术

      1)  Mock技术类似,Stub技术的目的也是解耦类之间的关系,但是和Mock技术不同的是,Mock技术只是模拟类的一些方法的一些行为,而Stub技术则是给出一个完整的被模拟对象的实例,但是Stub对象仅是一个被模拟对象的简单实现(可能仅仅返回一个固定值),这样就能更方便的测试,更全面的模拟目标对象,当然这样做的工作量会比较大。

   2)  当然,Stub可以由自己手动书写,而PHPUnit里面也提供了一套比较完整的解决方案,其使用方式和Mock类似,感兴趣的话可以查看PHPUnit的在线手册,http://www.phpunit.de/manual/current/en/test-doubles.html#test-doubles.stubbing-and-mocking-web-services  ,现在这里就不具体介绍了。

4.  数据库测试技术

关于数据库的测试,有很多种方法,其中各有好坏,下面简单的对我知道的数据库测试方法进行介绍

      1) Golden Master技术:指在第一次测试时手工进行测试,得到验证结果后,之后每次测试都可以用自动化脚本,测试实际结果与第一次的结果是否一致来进行正确性验证,也叫做Golden Result.用在数据库的测试中就是,第一次手工运行记录SQL语句(无状态的操作等等),然后之后每次运行都用现在生成的SQL与第一次记录的SQL对比,如果一致则返回正确,不对数据库进行查询。这种方式的好处是不用与数据库进行交互,速度很快,但只可用于一类操作。

      2) 使用xUnitDBUnit技术

               i.  xUnit框架下的一些框架,如JUnitPHPUnit中,有一个叫DBUnit的工具,专门用于对数据库的测试,它采用的方法就是真正的数据库操作然后验证真伪了,用PHPUnit为例,手册在:http://www.phpunit.de/manual/current/en/database.html

              ii.  DBUnit在运行测试用例的时候,将数据库置于不同的特有状态(即数据快照),方便测试,状态的定义方法是将数据保存为文件(一般为XML )PHPUnit /DBUnit 使用PDO连接数据库,所以想使用PHPUnit DBUnit需要安装PDO扩展(但是具体被测代码不需要使用PDO,可以使用任何数据访问形式)

              iii. PHPUnitDBUnit使用方法

使用的TestCase与一般的不一样,基类变成了PHPUnit_Extensions_Database_TestCaseTestCase的书写过程也有一些变化:

1.   清理数据库相关表

2.   初始化数据库状态fixture

3.   执行测试

4.   验证结果是否正确,一般使用assertDataSetEqual方法,DBUnit提供了一些专门的assertXXX方法,具体可查手册

5.   还原数据库状态

              iv. DBUnit TestCase书写要满足的要求:DBUnitTestCase通常需要至少实现四个函数:

1.  getConnection():返回需要操作的数据库的PDO连接

2.  getDataSet():定义了每个test方法执行前数据库的初始状态,即fixture,一般以DataSetDataTable定义。

DataSet是可以包含多个DataTable的数据结构

DataTable是包含一张数据库表的数据的结构

DataSetDataTable都有很多中创建的方法,典型的创建方法是使用XML文件进行创建,XML文件中定义了每个涉及到的表的名称,以及每条记录的具体值,DataSet甚至可以也由一个SQL Query来制定,这些DataSet就给出了一些表的数据状态。

3.   getSetUpOperation():可以不写,那么默认的setUp动作就是将getDataSet中返回的相关表TRUNCATE掉,然后将DataSet中的数据插入表中,也就是CLEAN_INSERT动作,而这个函数可以修改默认的setUp动作,如$this->getOperations()->NONE();就是什么都不做的动作,一些常用动作:

NONE    什么都不做

CLEAN_INSERT  清空相关表,将DataSet输入插入,默认setUp动作

INSERT   只插入动作

TRUNCATE 将相关表TRUNCATE

DELETE   删除相关表

DELETE_ALL 删除全部表

UPDATE  只进行Update动作,存在对应主键则Update否则报错

REFRESH  有相关主键则update,没有的则insert(replace)

4.   getTearDownOperation():可以不写,这个函数可以修改默认的tearDown动作,默认的tearDown动作是NONE

             v. DBUnit TestCase的一般流程方式

1.   执行setUp备份相关数据库,然后清空再插入数据。

2.   执行testXXX方法,对比期望数据与实际数据

3.   执行tearDown,恢复相关数据库

    上面提到了备份和恢复功能,所以我们的具体实现策略可能有一些变化:

初始化TestCase时,保存所有测试相关的数据表到一个DataSet中,用于备份

getDataSet方法进行复用,setUp阶段指定成期望的数据,采用CLEAN_INSERT策略,tearDown阶段使getDataSet返回之前备份的数据DataSet,也采用CLEAN_INSERT。这样就完成了每次执行完testXXX方法恢复数据的功能

   当然上面的实现方式很简陋,有好的方法大家多交流,谢啦哈~

             vi. DBUnit的优缺点

1.  优点:直接访问真实数据库,测试比较可靠,提供很多assert方法,表直接的比较很容易进行。

2.  缺点:需要备份和插入数据,执行较慢,并且不适合大规模的数据操作,效率会很低。执行脚本占内存很高。

5.  性能测试技术

性能测试一般主要针对Service 或者Interface进行,主要测试对象是服务器,下面简单介绍下我学到的性能测试相关内容。

      1)  针对服务的性能测试的指标

                   i. 响应时间

                  ii. 负载能力(qps)--------涉及到长短链接(长链接一次链接发送多个请求,短连接一次链接只发一个请求)

                  iii. 最大并发链接数

                  iv. 被测服务器数据:CPU、内存、磁盘以及网络接口IO:磁盘一般<100M,网络<50M

                   v. 可以尝试在压力情况下的手工测试指标,可能会有新发现。

      2) 常用的工具

                   i. ab  apache自带的压力工具,比较简单,但是数据不是很准确

                  ii. LoadRunner 比较重型的压力测试工具,可以录制脚本的方式进行测试,可用于系统级集成测试中(或者JMeter也可以有类似功能)

                 iii.自己开发-----一般简单的压力可以自己开发工具进行

      3)  压力测试场景设计

 被压对象

                  i.  Web页面:使用LoadRunner,支持事务级压力测试

                 ii. Web Service:需要验证返回数据是否正确

                iii. C Service:除了压力长连接之外,还有压最大连接数

      4)  如果出现不确定的问题需要注意的事项

                 i.  一定要注意返回的内容,要对返回内容进行验证,不正确的返回不能记做QPS中!

                ii. 注意测试服务器的配置

1.   物理配置:CPU,内存,物理地址:是否同一机房

2.   句柄数:Linux单个进程最大的打开文件数

3.   端口数:用户可用的端口数可要查看是否到极限,注意TIME_WAIT状态(TCP协议主动关闭方进行,占用端口一段时间,端口不可被其他端口复用,防止被动方发送数据导致混乱)

4.   大小端:数据存储的底层形式(多字节数据高位在上低位在上)

5.   Mysql相关的话注意Mysql的缓存、最大连接数、最大失败连接数、注意打开slow log以便优化

6.   Apache配置,最大连接数

      5)  寻找瓶颈后性能的提升方式

                  i.   修改使用框架

                  ii.  不经过Hibernate层,直接进行SQL查询

                 iii.  利用缓存

                 iv.   利用语言性能,如从JavaC

                  v.  关键是使用优化方式之前,一定要测试优化真的可行,用数据确定真的找对了瓶颈。

6.  Web测试技术

   关于Web的测试本来在上面的讨论中已经覆盖到了很多方面,在这里就简单提一下自动化的页面测试吧(算是最高级别的集成测试了),关于自动化页面测试用的比较多的是Selenium,各种xUnit框架也对其有了比较好的支持,当然它也有一些不足,比如录制脚本不能直接运行,需要手动修改,脚本后期维护比较高等缺点,但是在其强大的功能面前,执行一些自动页面的测试也是很有用的,尤其在回归测试上。关于Selenium的简单实用我在之前的一篇文章里提过,就不多说了,大家有兴趣的话可以看下。

三、测试步骤及总结

1.  测试步骤

关于测试,我觉得应该有RDQA的一起参与,应该由双方一起推动才能做的更好,才能真正的提高效率。

 大体的测试过程应该如下:

RD

QA

给出详细设计

给出测试计划

进行代码开发

进行测试用例设计

开发完代码并完成单元测试

开始测试执行------功能、性能、回归

修复bug

发现bug,并在RD修改后确认bug修复

 

完成测试,给出测试报告,准备checklist

上线(顺利~~~),完成后根据checklist检查上线是否成功,有无错误

 

2. 关于持续集成的吐槽

    在公司学到的最好的一样技术我觉得就是持续集成,或者说敏捷的概念(传送门:http://baike.baidu.com/view/5253255.htm ),持续集成要求做自动化单元测试,它可以保证我们在代码修改的时候迅速的发现问题,保持一个可发布的版本,并且可以迅速的进行回归测试,提高效率。而且因为要求做单元测试,这也就相当于强制在书写代码的时候注意代码风格,进行模块的解耦,这也可以提高代码质量和程序员自身素质,最重要的是增加了积累,单元测试其实就是一个程序的使用说明书哇^_^。我们又何乐而不为呢。另外关于持续集成比较好的工具,俺用过的只有Hudson,我觉得很好用,大家可以尝试着用下,肯定会有所收获。

3. 关于测试的总结

    之前真的对测试了解的很少,现在发现测试真的是一个很复杂,很困难,也很重要的工作,总之不论你想做RD还是QA,了解下测试都没有坏处,对自己的提升都真的很有帮助,但是一般测试只有在真正的生产环境才会有,在学校的开发或实验性项目中都没有测试工作,而且很多人也对测试不重视,导致国内的测试很不专业,也只能在工作岗位上现行培训,不过怎么说呢,大家都了解下测试吧,向国外看看,把测试做起来,让我们来一起学习、实践如何做好测试吧!

留言功能已取消,如需沟通,请邮件联系博主sunswk@sina.com,谢谢:)