zoukankan      html  css  js  c++  java
  • 单元测试的那点事儿

          作为一个开发人员,必需对自己的代码负责,而一个行之有效的方法就是编写单元测试,虽然说现在的变化很快,可能今天订好的接口,明天就会因为新需求的介入而面临重构的问题,当然这个问题只要软件在发展都是不可避免的事情,所以导致了单元测试的成本升高,而造成很多滞后问题,这就会造成:虽然一直在提倡测试先行,测试驱动的开发模式不能成为长久的行之有效的方案。但是单元测试,果然很重要。

          从Java开发转向PHP(虽然java还没有机会进入公司实践),正是因为PHP的快速开发的特点,很少有机会提及说要记得写单元测试这种话。所以这块就感觉的更加的匮乏,当然也有可能跟我目前的公司开发模式有关系。

         好了话不多说了,进入正题。

         PHP中的单元测试,有一个类库PHPUnit现在最新版本应该有3.5了(我的测试环境:PHP的版本要求5.2.5,pear的版本要求1.9.1,phpunit:3.4.15),PHPUnit的安装是依赖pear安装的,安装的方式我就不说了,我相信在网上可以找到很多行之有效的方法。

         安装好之后(为了方便我配置了环境),命令行下就可以通过输入 phpunit来查看一些提供的命令和版本信息了。

    1

          其中的几个命令选项如:coverage-开头的是因为我在本机上安装了XDEBUG库出现的,我不的不说xdebug确实是一个很好很棒的工具,能分析代码的性能,也能测试代码覆盖率。xdebug的安装我也不赘述了,google出同样有很多,我之前也有写了一些。

          单元测试说简单也简单,主要涉及到两个类,分别是单元测试类,和单元测试的套件类:(以下实例调试环境为Zend studio 8.0),在使用单元测试之前,你需要将你的单元测试类库包含到你的工程中,右击工程->build IncludePath

    2 3

    1. 单元测试类PHPUnit_Framework_TestCase

    创建一个class,继承PHPUnit_Framework_TestCase,

     1: class MyTest extends PHPUnit_Framework_TestCase {
     2:     
     3:     protected function setUp() {
     4:         parent::setUp();
     5:     }
     6:  
     7:     protected function tearDown() {
     8:         parent::tearDown();
     9:     }
     10: }

    setUp和tearDown分别是每个单元测试方法执行的前置和后置操作。

    单元测试的测试方法,都是以test开头进行测试:如:

     1: public function testGetName() {
     2:     $this->assertTrue('myname' == 'myname');
     3: }
     4:  
     5: public function testGetEmail() {
     6:     $this->assertEquals('myname', 'myname1');
     7: }

          分别写两个测试方法:执行会发现:

    4

           当正确的时候,和在该方法的前面显示一个绿色的钩钩,而如果是失败的方法,则会显示一个红叉叉,而上面的那条状态颜色就会是红色的。

          而如果我这个单元测试类中所有方法都测试通过的时候,则会显示绿色的通行颜色:

    5

    单元测试就是这么简单的,对于他里面有啥测试方法,可以查看PHPUnit/frameWork下的testCase类,可以看到很多验证的方法提供,我上面用到的只是其中的两个而已。

       2.  单元测试套件PHPUnit_Framework_TestSuite

          套件,名其曰就是多个测试的集合,可以同时测试多个测试类。

          测试套件有两种写法,两种写法都可以在命令行下运行:

         1)以继承的方式:

          将上面的单元测试做一个套件:

     1: require_once 'PHPUnit\Framework\TestSuite.php';
     2: require_once 'MyTest.php';
     3: class MyTestSuite extends PHPUnit_Framework_TestSuite {
     4:  
     5:     public function __construct() {
     6:         $this->setName('MyTestSuite');
     7:         $this->addTestSuite('MyTest');
     8:     }
     9:  
     10:     public static function suite() {
     11:         return new self();
     12:     }
     13: }

          执行:

    6

    这种方式也是zend studio创建的时候默认提供的,testSuite中提供的方法就是你的套件的方法了,这个方式通常比较简单。但是对于testSuite中需要很多注意的。这个也可以从他的源代码中看出来,

          2)以调用的方式

           还是将上面的测试类做测试:

     1: require_once('MyTest.php');
     2: class MyTestSuite2 {
     3:     static public function main() {
     4:        PHPUnit_TextUI_TestRunner::run(self::suite());
     5:     }
     6:     
     7:     static public function suite() {
     8:         $suite = new PHPUnit_Framework_TestSuite('MyTestSuite');
     9:         $suite->addTestSuite('MyTest');
     10:         return $suite;
     11:     }
     12: }

         这种方式在Zend Studio中运行会有问题(我本地测试是这样),但是用命令行运行是可以的:

    7

           说明:命令行下运行:

                   .  代表测试方法通过

                   I  代表该方法没有实现

                   F 代表方法测试失败

                   E 代表方法测试有错误输出

    通常往一个套件中添加测试方法是通过addTestSuite和addTest两个来完成:实现代码如下:

     1: /**
     2:  * Adds the tests from the given class to the suite.
     3:  *
     4:  * @param mixed $testClass
     5:  * @throws InvalidArgumentException
     6:  */
     7:     public function addTestSuite($testClass)
     8:     {
     9:         if (is_string($testClass) && class_exists($testClass)) {
     10:             $testClass = new ReflectionClass($testClass);
     11:         }
     12:  
     13:         if (!is_object($testClass)) {
     14:             throw PHPUnit_Util_InvalidArgumentHelper::factory(
     15:               1, 'class name or object'
     16:             );
     17:         }
     18:  
     19:         if ($testClass instanceof PHPUnit_Framework_TestSuite) {
     20:             $this->addTest($testClass);
     21:         }
     22:  
     23:         else if ($testClass instanceof ReflectionClass) {
     24:             $suiteMethod = FALSE;
     25:  
     26:             if (!$testClass->isAbstract()) {
     27:                 if ($testClass->hasMethod(PHPUnit_Runner_BaseTestRunner::SUITE_METHODNAME)) {
     28:                     $method = $testClass->getMethod(
     29:                       PHPUnit_Runner_BaseTestRunner::SUITE_METHODNAME
     30:                     );
     31:  
     32:                     if ($method->isStatic()) {
     33:                         $this->addTest(
     34:                           $method->invoke(NULL, $testClass->getName())
     35:                         );
     36:  
     37:                         $suiteMethod = TRUE;
     38:                     }
     39:                 }
     40:             }
     41:  
     42:             if (!$suiteMethod && !$testClass->isAbstract()) {
     43:                 $this->addTest(new PHPUnit_Framework_TestSuite($testClass));
     44:             }
     45:         }
     46:  
     47:         else {
     48:             throw new InvalidArgumentException;
     49:         }
     50:     }

    通过addTestSuite添加的,应该是一个testSuite或是testCase或是一个类中含有名为suite的静态方法方可。

    我们再来看看addTest:

     1: /**
     2:  * Adds a test to the suite.
     3:  *
     4:  * @param PHPUnit_Framework_Test $test
     5:  * @param array $groups
     6:  */
     7:     public function addTest(PHPUnit_Framework_Test $test, $groups = array())
     8:     {
     9:         $class = new ReflectionClass($test);
     10:  
     11:         if (!$class->isAbstract()) {
     12:             $this->tests[]  = $test;
     13:             $this->numTests = -1;
     14:  
     15:             if ($test instanceof PHPUnit_Framework_TestSuite &&
     16:                 empty($groups)) {
     17:                 $groups = $test->getGroups();
     18:             }
     19:  
     20:             if (empty($groups)) {
     21:                 $groups = array('__nogroup__');
     22:             }
     23:  
     24:             foreach ($groups as $group) {
     25:                 if (!isset($this->groups[$group])) {
     26:                     $this->groups[$group] = array($test);
     27:                 } else {
     28:                     $this->groups[$group][] = $test;
     29:                 }
     30:             }
     31:         }
     32:     }

    这个方法就比较简单了,就是从testSuite中拿到注册的test方法。

    而对于testSuite的构造方法,对齐传入的参数的规定也是比较复杂:

     1: /**
     2:  * Constructs a new TestSuite:
     3:  *
     4:  * - PHPUnit_Framework_TestSuite() constructs an empty TestSuite.
     5:  *
     6:  * - PHPUnit_Framework_TestSuite(ReflectionClass) constructs a
     7:  * TestSuite from the given class.
     8:  *
     9:  * - PHPUnit_Framework_TestSuite(ReflectionClass, String)
     10:  * constructs a TestSuite from the given class with the given
     11:  * name.
     12:  *
     13:  * - PHPUnit_Framework_TestSuite(String) either constructs a
     14:  * TestSuite from the given class (if the passed string is the
     15:  * name of an existing class) or constructs an empty TestSuite
     16:  * with the given name.
     17:  *
     18:  * @param mixed $theClass
     19:  * @param string $name
     20:  * @throws InvalidArgumentException
     21:  */
     22:     public function __construct($theClass = '', $name = '')
     23:     {
     24:         $argumentsValid = FALSE;
     25:  
     26:         if (is_object($theClass) &&
     27:             $theClass instanceof ReflectionClass) {
     28:             $argumentsValid = TRUE;
     29:         }
     30:  
     31:         else if (is_string($theClass) && $theClass !== ''
     32:                  && class_exists($theClass, FALSE)) {
     33:             $argumentsValid = TRUE;
     34:  
     35:             if ($name == '') {
     36:                 $name = $theClass;
     37:             }
     38:  
     39:             $theClass = new ReflectionClass($theClass);
     40:         }
     41:  
     42:         else if (is_string($theClass)) {
     43:             $this->setName($theClass);
     44:             return;
     45:         }
     46:  
     47:         if (!$argumentsValid) {
     48:             throw new InvalidArgumentException;
     49:         }
     50:  
     51:         $filename = $theClass->getFilename();
     52:  
     53:         if (strpos($filename, 'eval()') === FALSE) {
     54:             PHPUnit_Util_Filter::addFileToFilter(realpath($filename), 'TESTS');
     55:         }
     56:  
     57:         if ($name != '') {
     58:             $this->setName($name);
     59:         } else {
     60:             $this->setName($theClass->getName());
     61:         }
     62:  
     63:         $constructor = $theClass->getConstructor();
     64:  
     65:         if ($constructor !== NULL &&
     66:             !$constructor->isPublic()) {
     67:             $this->addTest(
     68:               self::warning(
     69:                 sprintf(
     70:                   'Class "%s" has no public constructor.',
     71:  
     72:                   $theClass->getName()
     73:                 )
     74:               )
     75:             );
     76:  
     77:             return;
     78:         }
     79:  
     80:         $names = array();
     81:  
     82:         foreach ($theClass->getMethods() as $method) {
     83:             if (strpos($method->getDeclaringClass()->getName(), 'PHPUnit_') !== 0) {
     84:                 $this->addTestMethod($theClass, $method, $names);
     85:             }
     86:         }
     87:  
     88:         if (empty($this->tests)) {
     89:             $this->addTest(
     90:               self::warning(
     91:                 sprintf(
     92:                   'No tests found in class "%s".',
     93:  
     94:                   $theClass->getName()
     95:                 )
     96:               )
     97:             );
     98:         }
     99:     }

    我的第二种方法也是通过这中构造函数的方式来实现。

    好了··就这样··有啥疑问或是纰漏的地方欢迎交流

  • 相关阅读:
    webpack打包注意事项
    打印内存, 打印16进制
    c++ 字符集转换
    RegSvr32 加载失败,找不到指定的模块
    错误码设计
    mfc 移动绘制的图形
    获取、设置光标
    c++ 函数中定义函数
    python linux 自动补全 tab.py
    3.4.5节 完整神经网络样例程序
  • 原文地址:https://www.cnblogs.com/xiaoyaoxia/p/1986443.html
Copyright © 2011-2022 走看看