PHPUnit测试私有属性和方法功能示例


Posted in PHP onJune 12, 2018

本文实例讲述了PHPUnit测试私有属性和方法功能。分享给大家供大家参考,具体如下:

一、测试类中的私有方法:

class Sample
{
  private $a = 0;
  private function run()
  {
    echo $a;
  }
}

上面只是简单的写了一个类包含,一个私有变量和一个私有方法。对于protected和private方法,由于无法像是用public方法一样直接调用,所以在使用phpunit进行单测的时候,多有不便,特别是当一个类中,对外只提供少量接口,内部使用了大量private方法的情况。

对于protected方法,建议使用继承的方式进行测试,在此就不再赘述。而对于private方法的测试,建议使用php的反射机制来进行。话不多说,上代码:

class testSample()
{
    $method = new ReflectionMethod('Sample', 'run');
    $method->setAccessible(true); //将run方法从private变成类似于public的权限
    $method->invoke(new Sample()); //调用run方法
}

如果run方法是静态的,如:

private static function run()
{
  echo 'run is a private static function';
}

那么invoke函数还可以这么写:

$method->invoke(null); //只有静态方法可以不必传类的实例化

如果run还需要传参,比如:

private function run($x, $y)
{
  return $x + $y;
}

那么,测试代码可以改为:

$method->invokeArgs(new Sample(), array(1, 2));
//array中依次写入要传的参数。执行结果返回3

【注意】:利用反射的方法测试私有方法虽好,但setAccessible函数是php5.3.2版本以后才支持的(>=5.3.2)

二、私有属性的get/set

说完了私有方法,再来看看私有属性,依旧拿Sample类作为例子,想要获取或设置Sample类中的私有属性$a的值可以用如下方法:

public function testPrivateProperty()
{
  $reflectedClass = new ReflectionClass('Sample');
  $reflectedProperty = $reflectedClass->getProperty('a');
  $reflectedProperty->setAccessible(true);
  $reflectedProperty->getValue(); //获取$a的值
  $reflectedProperty->setValue(123); //给$a赋值:$a = 123;
}

上述方法对静态属性依然有效。

到此,是不是瞬间感觉测试私有方法或属性变得很容易了。

附:PHPunit 测试私有方法(英文原文)

This article is part of a series on testing untestable code:

  • Testing private methods
  • Testing code that uses singletons
  • Stubbing static methods
  • Stubbing hard-coded dependencies

No, not those privates. If you need help with those, this book might help.

One question I get over and over again when talking about Unit Testing is this:

"How do I test the private attributes and methods of my objects?"

Lets assume we have a class Foo:

<?php
class Foo
{
  private $bar = 'baz';
  public function doSomething()
  {
    return $this->bar = $this->doSomethingPrivate();
  }
  private function doSomethingPrivate()
  {
    return 'blah';
  }
}
?>

Before we explore how protected and private attributes and methods can be tested directly, lets have a look at how they can be tested indirectly.

The following test calls the testDoSomething() method which in turn calls thedoSomethingPrivate() method:

<?php
class FooTest extends PHPUnit_Framework_TestCase
{
  /**
   * @covers Foo::doSomething
   * @covers Foo::doSomethingPrivate
   */
  public function testDoSomething()
  {
    $foo = new Foo;
    $this->assertEquals('blah', $foo->doSomething());
  }
}
?>

The test above assumes that testDoSomething() only works correctly whentestDoSomethingPrivate() works correctly. This means that we have indirectly testedtestDoSomethingPrivate(). The problem with this approach is that when the test fails we do not know directly where the root cause for the failure is. It could be in eithertestDoSomething() or testDoSomethingPrivate(). This makes the test less valuable.

PHPUnit supports reading protected and private attributes through thePHPUnit_Framework_Assert::readAttribute() method. Convenience wrappers such asPHPUnit_Framework_TestCase::assertAttributeEquals() exist to express assertions onprotected and private attributes:

<?php
class FooTest extends PHPUnit_Framework_TestCase
{
  public function testPrivateAttribute()
  {
    $this->assertAttributeEquals(
     'baz', /* expected value */
     'bar', /* attribute name */
     new Foo /* object     */
    );
  }
}
?>

PHP 5.3.2 introduces the ReflectionMethod::setAccessible() method to allow the invocation of protected and private methods through the Reflection API:

<?php
class FooTest extends PHPUnit_Framework_TestCase
{
  /**
   * @covers Foo::doSomethingPrivate
   */
  public function testPrivateMethod()
  {
    $method = new ReflectionMethod(
     'Foo', 'doSomethingPrivate'
    );
    $method->setAccessible(TRUE);
    $this->assertEquals(
     'blah', $method->invoke(new Foo)
    );
  }
}
?>

In the test above we directly test testDoSomethingPrivate(). When it fails we immediately know where to look for the root cause.

I agree with Dave Thomas and Andy Hunt, who write in their book "Pragmatic Unit Testing":

"In general, you don't want to break any encapsulation for the sake of testing (or as Mom used to say, "don't expose your privates!"). Most of the time, you should be able to test a class by exercising its public methods. If there is significant functionality that is hidden behind private or protected access, that might be a warning sign that there's another class in there struggling to get out."

So: Just because the testing of protected and private attributes and methods is possible does not mean that this is a "good thing".

参考文献:

http://php.net/manual/en/class.reflectionmethod.php

希望本文所述对大家PHP程序设计有所帮助。

PHP 相关文章推荐
php基础知识:函数基础知识
Dec 13 PHP
关于BIG5-HKSCS的解决方法
Mar 20 PHP
PHP 木马攻击防御技巧
Jun 13 PHP
深入理解PHP原理之异常机制
Aug 21 PHP
PHP中文处理 中文字符串截取(mb_substr)和获取中文字符串字数
Nov 10 PHP
PHP类继承 extends使用介绍
Jan 14 PHP
ThinkPHP3.1之D方法实例详解
Jun 20 PHP
jquery+php+ajax显示上传进度的多图片上传并生成缩略图代码
Oct 15 PHP
PHP实现生成唯一会员卡号
Aug 24 PHP
PHP中单例模式与工厂模式详解
Feb 17 PHP
PHP编程实现脚本异步执行的方法
Aug 09 PHP
PHP用swoole+websocket和redis实现web一对一聊天
Nov 05 PHP
PHP+redis实现的悲观锁机制示例
Jun 12 #PHP
thinkPHP5框架auth权限控制类与用法示例
Jun 12 #PHP
thinkPHP5框架实现基于ajax的分页功能示例
Jun 12 #PHP
Laravel框架路由和控制器的绑定操作方法
Jun 12 #PHP
Laravel框架路由设置与使用示例
Jun 12 #PHP
Laravel框架生命周期与原理分析
Jun 12 #PHP
Laravel框架分页实现方法分析
Jun 12 #PHP
You might like
PHP的Yii框架中过滤器相关的使用总结
2016/03/29 PHP
PHP简单获取网站百度搜索和搜狗搜索收录量的方法
2016/08/23 PHP
教你如何解密js/vbs/vbscript加密的编码异处理小结
2008/06/25 Javascript
jQuery页面滚动浮动层智能定位实例代码
2011/08/23 Javascript
线路分流自动智能跳转代码,自动选择最快镜像网站(js)
2011/10/31 Javascript
Extjs优化(二)Form表单提交通用实现
2013/04/15 Javascript
在页面中js获取光标/鼠标的坐标及光标的像素坐标
2013/11/11 Javascript
Javascript中数组方法汇总(推荐)
2015/04/01 Javascript
javascript+html5实现仿flash滚动播放图片的方法
2015/04/27 Javascript
JavaScript中创建对象的模式汇总
2016/04/19 Javascript
Javascript中的迭代、归并方法详解
2016/06/14 Javascript
JS实现点击事件统计的简单实例
2016/07/10 Javascript
Javascript基于jQuery UI实现选中区域拖拽效果
2016/11/25 Javascript
详解使用angularjs的ng-options时如何设置默认值(初始值)
2017/07/18 Javascript
BootStrap中Table隐藏后显示问题的实现代码
2017/08/31 Javascript
js时间戳与日期格式之间相互转换
2017/12/11 Javascript
详解vue中axios的封装
2018/07/18 Javascript
Nodejs封装类似express框架的路由实例详解
2020/01/05 NodeJs
Python基于identicon库创建类似Github上用的头像功能
2017/09/25 Python
Python实现将doc转化pdf格式文档的方法
2018/01/19 Python
用PyInstaller把Python代码打包成单个独立的exe可执行文件
2018/05/26 Python
详解python Todo清单实战
2018/11/01 Python
Python之修改图片像素值的方法
2019/07/03 Python
说说在weblogic中开发消息Bean时的persistent与non-persisten的差别
2013/04/07 面试题
仓库门卫岗位职责
2013/12/22 职场文书
大学生个人简历中的自我评价
2013/12/27 职场文书
保安部任务及岗位职责
2014/02/25 职场文书
教师节促销方案
2014/03/22 职场文书
法人授权委托书格式
2014/04/08 职场文书
优秀团员事迹材料
2014/12/25 职场文书
新郎婚礼答谢词
2015/01/04 职场文书
工作调动申请报告
2015/05/18 职场文书
详解Django中 render() 函数的使用方法
2021/04/22 Python
如何自己动手写SQL执行引擎
2021/06/02 MySQL
Python的property属性详细讲解
2022/04/11 Python
分析MySQL优化 index merge 后引起的死锁
2022/04/19 MySQL