51Testing软件测试论坛

 找回密码
 (注-册)加入51Testing

QQ登录

只需一步,快速开始

微信登录,快人一步

手机号码,快捷登录

查看: 2471|回复: 2
打印 上一主题 下一主题

[转贴] 测试用例详解

[复制链接]
  • TA的每日心情
    无聊
    4 天前
  • 签到天数: 1050 天

    连续签到: 1 天

    [LV.10]测试总司令

    跳转到指定楼层
    1#
    发表于 2016-3-14 14:13:43 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

    在上篇文章中介绍了如何使用PHPUnit进行单元测试,现在我就来谈谈如何编写测试用例以及怎样保证测试的全面性。

    通常的测试用例继承自PHPUnit_Framework_TestCase类,其中的每个测试都以test开头,而且声明为公共类型public。每个测试用例都有一个构建方法setUp()和拆除方法tearDown(),分别在每个测试执行之前和之后执行,这两个方法都声明为被保护类型protected。测试语句的类型包括断言、标记跳过、标记未完成。自动生成的测试类使用标记未完成来表示该测试未完成,在测试条件不满足的情况下要使用标记跳过,如测试Oracle数据库驱动时没有Oracle数据库环境、Linux下无法测试SQL Server数据库驱动等。测试结果包括成功、失败和错误。出现错误的结果说明你的代码中有语法或运行时错误,这些错误要首先被解决。

    标记未完成

    在开始写测试用例时,我们使用标记跳过来表示测试是未完成的,这样做和什么都不写的区别是后者会认为测试是成功的,而你可能会在之后忘了写测试。

    PHP代码
    • class UnitTest extends PHPUnit_Framework_TestCase
    • {
    •     /**
    •      * 测试Hello()方法
    •      */
    •     public function testHello()
    •     {
    •         $this->markTestIncomplete('这是一个未完成的测试');
    •     }
    • }
    • ?>

    标记跳过

    使用标记跳过来跳过不满足测试条件的情况,避免出现错误而影响测试结果。

    PHP代码
    • class UnitTest extends PHPUnit_Framework_TestCase
    • {
    •     protected function setUp()
    •     {
    •         // 该测试用例需要xxx扩展,如果不满足就跳过
    •         if (!extension_loaded('xxx')) {
    •             $this->markTestSkipped('这是一个跳过的测试');
    •         }
    •     }
    • }
    • ?>

    断言

    布尔类型
    assertTrue   断言为真
    assertFalse  断言为假

    NULL类型
    assertNull     断言为NULL
    assertNotNull  断言非NULL

    数字类型
    assertEquals             断言等于
    assertNotEquals          断言不等于
    assertGreaterThan        断言大于
    assertGreaterThanOrEqual 断言大于等于
    assertLessThan           断言小于
    assertLessThanOrEqual    断言小于等于

    字符类型
    assertEquals          断言等于
    assertNotEquals       断言不等于
    assertContains        断言包含
    assertNotContains     断言不包含
    assertContainsOnly    断言只包含
    assertNotContainsOnly 断言不只包含

    数组类型
    assertEquals          断言等于
    assertNotEquals       断言不等于
    assertArrayHasKey     断言有键
    assertArrayNotHasKey  断言没有键
    assertContains        断言包含
    assertNotContains     断言不包含
    assertContainsOnly    断言只包含
    assertNotContainsOnly 断言不只包含

    对象类型
    assertAttributeContains           断言属性包含
    assertAttributeContainsOnly       断言属性只包含
    assertAttributeEquals             断言属性等于
    assertAttributeGreaterThan        断言属性大于
    assertAttributeGreaterThanOrEqual 断言属性大于等于
    assertAttributeLessThan           断言属性小于
    assertAttributeLessThanOrEqual    断言属性小于等于
    assertAttributeNotContains        断言不包含
    assertAttributeNotContainsOnly    断言属性不只包含
    assertAttributeNotEquals          断言属性不等于
    assertAttributeNotSame            断言属性不相同
    assertAttributeSame               断言属性相同
    assertSame                        断言类型和值都相同
    assertNotSame                     断言类型或值不相同
    assertObjectHasAttribute          断言对象有某属性
    assertObjectNotHasAttribute       断言对象没有某属性

    class类型
    class类型包含对象类型的所有断言,还有
    assertClassHasAttribute          断言类有某属性
    assertClassHasStaticAttribute    断言类有某静态属性
    assertClassNotHasAttribute       断言类没有某属性
    assertClassNotHasStaticAttribute 断言类没有某静态属性

    文件相关
    assertFileEquals     断言文件内容等于
    assertFileExists     断言文件存在
    assertFileNotEquals  断言文件内容不等于
    assertFileNotExists  断言文件不存在

    XML相关
    assertXmlFileEqualsXmlFile        断言XML文件内容相等
    assertXmlFileNotEqualsXmlFile     断言XML文件内容不相等
    assertXmlStringEqualsXmlFile      断言XML字符串等于XML文件内容
    assertXmlStringEqualsXmlString    断言XML字符串相等
    assertXmlStringNotEqualsXmlFile   断言XML字符串不等于XML文件内容
    assertXmlStringNotEqualsXmlString 断言XML字符串不相等

    有返回值的方法或函数根据其类型选择相应的断言,下面是一个简单例子。

    PHP代码
    • class UnitTest extends PHPUnit_Framework_TestCase
    • {
    •     /**
    •      * 测试返回值为布尔类型
    •      */
    •     public function testReturnBool()
    •     {
    •         // 实际情况把TRUE和FALSE换为被测试方法或函数
    •         $this->assertTrue(TRUE);
    •         $this->assertFalse(FALSE);
    •     }
    •     /**
    •      * 测试返回值为字符串类型
    •      */
    •     public function testReturnString()
    •     {
    •         $expected = 'string';
    •         // 实际情况把下面的'string'换为被测试方法或函数
    •         $result = 'string';
    •         $this->assertEquals($expected, $result);
    •     }
    •     /**
    •      * 测试返回值是数字类型
    •      */
    •     public function testReturnInt()
    •     {
    •         $expected = 10;
    •         // 实际情况把20换为被测试方法或函数
    •         $result = 20;
    •         $this->assertGreaterThan($expected, $result);
    •     }
    •     /**
    •      * 测试返回值是数组类型
    •      */
    •     public function testReturnArray()
    •     {
    •         // 实际情况把$result赋值为被测试方法或函数
    •         $result = array('test' => 'hello');
    •         // 实际情况把'test'换为要测试的键名称
    •         $this->assertArrayHasKey('test', $result);
    •     }
    •     /**
    •      * 测试返回值是对象类型
    •      */
    •     public function testReturnObject()
    •     {
    •         // 实际情况把$this换为期望的对象
    •         $expected = $this;
    •         // 实际情况把$this换为被测试方法或函数
    •         $result = $this;
    •         $this->assertSame($expected, $result);
    •     }
    • }
    • ?>

    无返回值的方法,可以通过其他方法读取属性,也可以使用对象类型中的断言来判断属性的改变。

    PHP代码
    • /**
    • * Unit类,有一个无返回值方法
    • */
    • class Unit
    • {
    •     protected $name;
    •     /**
    •      * 设置name属性
    •      */
    •     public function setName($value)
    •     {
    •         $this->name = $value;
    •     }
    • }
    • ?>

    PHP代码
    • require_once 'Unit.php';
    • class UnitTest extends PHPUnit_Framework_TestCase
    • {
    •     /**
    •      * 测试无返回值的方法
    •      */
    •     public function testsetName()
    •     {
    •         $expected = 'Hello';
    •         $o = new Unit();
    •         $o->setName('Hello');
    •         $this->assertAttributeEquals($expected, 'name', $o);
    •     }
    • }
    • ?>

    编写测试

    编写测试的原则是,尽可能测试每种不同的参数调用和不同的返回结果类型,既要测试成功的情况,也要测试失败的情况;无返回值的情况,要测试属性改变、输出内容、异常类型等;测试后记得要恢复现场。

    在这里做了超出自己能力的事并不光荣。在你写某个函数之前,你只想让它做加法,但它却能做乘法,而且单元测试正确通过。我们来看看它是怎么做到的。

    PHP代码
    • /**
    • * 计算器类
    • */
    • class Calculator
    • {
    •     /**
    •      * 做加法运算
    •      *
    •      * @param  int  $a
    •      * @param  int  $b
    •      * @return int
    •      */
    •     public function add($a, $b)
    •     {
    •         return $a * $b;
    •     }
    • }
    • ?>

    PHP代码
    • require_once 'Calculator.php';
    • class CalcuatorTest extends PHPUnit_Framework_TestCase
    • {
    •     public function testadd()
    •     {
    •         // 创建实例
    •         $c = new Calculator();
    •         $expected = 4;
    •         // 做加法
    •         $result = $c->add(2, 2);
    •         $this->assertEquals($expected, $result);
    •     }
    • }
    • ?>

    合理的测试能帮助我们尽早发现错误。add()方法有两个参数,测试的时候用了两个值相同的参数。如果多做几次测试又太麻烦,对于这个测试我们按照科学的方法只需要一次。从概率学上讲,当你使用的参数差异越大时,结果相同的概率越低。

    对于只有几个返回值的情况,要测试全部,如布尔类型。

    PHP代码
    • /**
    • * File.php
    • */
    • /**
    • * 在文件中写入数据并保存
    • *
    • * @param  string  $path
    • * @param  string  $data
    • * @return bool
    • */
    • function Save($path, $data)
    • {
    •     if (is_dir($path)) {
    •       return FALSE;
    •     }
    •     return file_put_contents($path, $data);
    • }
    • ?>

    PHP代码
    • /**
    • * FileTest.php
    • */
    • require_once 'File.php';
    • class FileTest extends PHPUnit_Framework_TestCase
    • {
    •     /**
    •      * 测试保存文件
    •      */
    •     public function testSave()
    •     {
    •         $file = 'IamFile.txt';
    •         $dir = 'IamDir';
    •         mkdir($dir);
    •         // 测试返回值为真的情况
    •         $this->assertTrue(Save($file, 'TestTrue'));
    •         // 测试返回值为假的情况
    •         $this->assertFalse(Save($dir, 'TestFalse'));
    •         // 恢复现场
    •         if (is_file($file)) {
    •           unlink($file);
    •         }
    •         rmdir($dir);
    •     }
    • }
    • ?>

    对于有多种类型返回值或不同参数的情况,分别测试每种类型和参数。下面是ThinkPHP源代码中的一个函数,有点复杂。这个例子不能单独运行,如需要请用SVN导出最新的ThinkPHP源代码(含单元测试)。

    PHP代码
    • /**
    • * URL生成函数
    • *
    • * @param  string  $action  方法名
    • * @param  string  $module  模块名
    • * @param  string  $route   路由名
    • * @param  array   $params  参数
    • */
    • function url($action=ACTION_NAME,$module=MODULE_NAME,$route='',$app=APP_NAME,$params=array())
    • {
    •     if(C('DISPATCH_ON') && C('URL_MODEL')>0) {
    •         switch(C('PATH_MODEL')) {
    •             case 1:// 普通PATHINFO模式
    •                 $str    =   '/';
    •                 foreach ($params as $var=>$val)
    •                     $str .= $var.'/'.$val.'/';
    •                 $str = substr($str,0,-1);
    •                 if(!emptyempty($route)) {
    •                     $url    =   str_replace(APP_NAME,$app,).'/'.C('VAR_ROUTER').'/'.$route.'/'.$str;
    •                 }else{
    •                     $url    =   str_replace(APP_NAME,$app,).'/'.C('VAR_MODULE').'/'.$module.'/'.C('VAR_ACTION').'/'.$action.$str;
    •                 }
    •                 break;
    •             case 2:// 智能PATHINFO模式
    •                 $depr   =   C('PATH_DEPR');
    •                 $str    =   $depr;
    •                 foreach ($params as $var=>$val)
    •                     $str .= $var.$depr.$val.$depr;
    •                 $str = substr($str,0,-1);
    •                 if(!emptyempty($route)) {
    •                     $url    =   str_replace(APP_NAME,$app,).'/'.$route.$str;
    •                 }else{
    •                     $url    =   str_replace(APP_NAME,$app,).'/'.$module.$depr.$action.$str;
    •                 }
    •                 break;
    •         }
    •         if(C('HTML_URL_SUFFIX')) {
    •             $url .= C('HTML_URL_SUFFIX');
    •         }
    •     }else{
    •         $params =   http_build_query($params);
    •         if(!emptyempty($route)) {
    •             $url    =   str_replace(APP_NAME,$app,).'?'.C('VAR_ROUTER').'='.$route.'&'.$params;
    •         }else{
    •             $url    =   str_replace(APP_NAME,$app,).'?'.C('VAR_MODULE').'='.$module.'&'.C('VAR_ACTION').'='.$action.'&'.$params;
    •         }
    •     }
    •     return $url;
    • }
    • ?>

    PHP代码
    • require_once 'functions.php';
    • class functionsTest extends PHPUnit_Framework_TestCase
    • {
    •     /**
    •      * 确认url()返回预期的字符串
    •      */
    •     public function testurl()
    •     {
    •         define('', 'index.php');
    •         C('VAR_MODULE', 'module');
    •         C('VAR_ACTION', 'action');
    •         C('VAR_ROUTER', 'route');
    •         // 测试通常模式URL
    •         $uri = url('Index', 'Home', '', APP_NAME, array('q' => 'test', 'msg' => 'OK'));
    •         $this->assertEquals('index.php?module=Home&action=Index&q=test&msg=OK', $uri);
    •         // 测试通常模式路由
    •         $uri = url('Index', 'Home', 'default', APP_NAME, array('q' => 'test', 'msg' => 'OK'));
    •         $this->assertEquals('index.php?route=default&q=test&msg=OK', $uri);
    •         C('DISPATCH_ON', true);
    •         C('URL_MODEL', 1);
    •         C('PATH_MODEL', 1);
    •         // 测试普通PATHINFO模式URL
    •         $uri = url('Index', 'Home', '', APP_NAME, array('q' => 'test', 'msg' => 'OK'));
    •         $this->assertEquals('index.php/module/Home/action/Index/q/test/msg/OK', $uri);
    •         // 测试普通PATHINFO模式路由
    •         $uri = url('Index', 'Home', 'default', APP_NAME, array('q' => 'test', 'msg' => 'OK'));
    •         $this->assertEquals('index.php/route/default/q/test/msg/OK', $uri);
    •         C('PATH_MODEL', 2);
    •         C('PATH_DEPR', '/');
    •         // 测试智能PATHINFO模式URL
    •         $uri = url('Index', 'Home', '', APP_NAME, array('q' => 'test', 'msg' => 'OK'));
    •         $this->assertEquals('index.php/Home/Index/q/test/msg/OK', $uri);
    •         // 测试智能PATHINFO模式路由
    •         $uri = url('Index', 'Home', 'default', APP_NAME, array('q' => 'test', 'msg' => 'OK'));
    •         $this->assertEquals('index.php/default/q/test/msg/OK', $uri);
    •     }
    • }
    • ?>

    异常测试

    有时程序执行了非法操作而抛出异常,我们需要模拟某个异常,然后捕捉它是否触发了该异常。

    PHP代码
    • class UnitTest extends PHPUnit_Framework_TestCase
    • {
    •     /**
    •      * 测试异常
    •      */
    •     public function testException()
    •     {
    •         // 期望Exception异常
    •         $this->setExpectedException('Exception');
    •         // 抛出Exception异常
    •         throw new Exception('TestException');
    •     }
    • }
    • ?>

    输出测试

    有时某个方法并不返回而输出某些内容,我们需要继承PHPUnit_Extensions_OutputTestCase类来捕捉输出内容。PHPUnit默认不载入扩展类,需要自己加载。

    PHP代码
    • // 载入输出测试用例扩展
    • require_once 'PHPUnit/Extensions/OutputTestCase.php';
    • class UnitTest extends PHPUnit_Extensions_OutputTestCase
    • {
    •     /**
    •      * 测试输出
    •      */
    •     public function testOutput()
    •     {
    •         // 期望输出的内容是字符串 'Hello'
    •         $this->expectOutputString('Hello');
    •         // 输出 'Hello'
    •         echo 'Hello';
    •     }
    • }
    • ?>

    数据库测试

    PHPUnit的数据库测试并不完善,只提供了assertTablesEqual和assertDataSetsEqual两个断言与createFlatXMLDataSet和createXMLDataSet创建XML数据集的方法。无法进行全面的数据操作测试,建议使用DBUnit。

    附录

    PHPUnit断言参考
    assertArrayHasKey($key, array $array, $message = '')
    assertArrayNotHasKey($key, array $array, $message = '')
    assertAttributeContains($needle, $haystackAttributeName, $haystackClassOrObject, $message = '')
    assertAttributeContainsOnly($type, $haystackAttributeName, $haystackClassOrObject, $isNativeType = NULL, $message = '')
    assertAttributeEquals($expected, $actualAttributeName, $actualClassOrObject, $message = '', $delta = 0, $maxDepth = 10, $canonicalizeEol = FALSE)
    assertAttributeGreaterThan($expected, $actualAttributeName, $actualClassOrObject, $message = '')
    assertAttributeGreaterThanOrEqual($expected, $actualAttributeName, $actualClassOrObject, $message = '')
    assertAttributeLessThan($expected, $actualAttributeName, $actualClassOrObject, $message = '')
    assertAttributeLessThanOrEqual($expected, $actualAttributeName, $actualClassOrObject, $message = '')
    assertAttributeNotContains($needle, $haystackAttributeName, $haystackClassOrObject, $message = '')
    assertAttributeNotContainsOnly($type, $haystackAttributeName, $haystackClassOrObject, $isNativeType = NULL, $message = '')
    assertAttributeNotEquals($expected, $actualAttributeName, $actualClassOrObject, $message = '', $delta = 0, $maxDepth = 10, $canonicalizeEol = FALSE)
    assertAttributeNotSame($expected, $actualAttributeName, $actualClassOrObject, $message = '')
    assertAttributeSame($expected, $actualAttributeName, $actualClassOrObject, $message = '')
    assertClassHasAttribute($attributeName, $className, $message = '')
    assertClassHasStaticAttribute($attributeName, $className, $message = '')
    assertClassNotHasAttribute($attributeName, $className, $message = '')
    assertClassNotHasStaticAttribute($attributeName, $className, $message = '')
    assertContains($needle, $haystack, $message = '')
    assertContainsOnly($type, $haystack, $isNativeType = NULL, $message = '')
    assertEqualXMLStructure(DOMNode $expectedNode, DOMNode $actualNode, $checkAttributes = FALSE, $message = '')
    assertEquals($expected, $actual, $message = '', $delta = 0, $maxDepth = 10, $canonicalizeEol = FALSE)
    assertFalse($condition, $message = '')
    assertFileEquals($expected, $actual, $message = '', $canonicalizeEol = FALSE)
    PHPUnit官方文档
    http://www.phpunit.de/manual/3.3/en/index.html


    分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
    收藏收藏
    回复

    使用道具 举报

    本版积分规则

    关闭

    站长推荐上一条 /1 下一条

    小黑屋|手机版|Archiver|51Testing软件测试网 ( 沪ICP备05003035号 关于我们

    GMT+8, 2024-11-25 09:37 , Processed in 0.067387 second(s), 22 queries .

    Powered by Discuz! X3.2

    © 2001-2024 Comsenz Inc.

    快速回复 返回顶部 返回列表